dsh-connect 0.7.2 → 0.9.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.i18n.yaml +2 -2
- package/README.md +196 -43
- package/README.zh.md +171 -42
- package/client/client.js +817 -0
- package/client/client.js.map +7 -0
- package/client/locale.mjs +227 -0
- package/client/panel-state.mjs +54 -0
- package/client/settings-client.mjs +389 -0
- package/docs/images/settings-advanced-en.png +0 -0
- package/docs/images/settings-advanced-zh.png +0 -0
- package/docs/images/settings-defaults-en.png +0 -0
- package/docs/images/settings-defaults-zh.png +0 -0
- package/docs/images/settings-overview-en.png +0 -0
- package/docs/images/settings-overview-zh.png +0 -0
- package/examples/minimal.config.json +16 -0
- package/lib/binding.d.ts.map +1 -1
- package/lib/binding.js +2 -2
- package/lib/binding.js.map +1 -1
- package/lib/channels/dingtalk/adapter.d.ts +59 -0
- package/lib/channels/dingtalk/adapter.d.ts.map +1 -0
- package/lib/channels/dingtalk/adapter.js +123 -0
- package/lib/channels/dingtalk/adapter.js.map +1 -0
- package/lib/channels/dingtalk/i18n.d.ts +14 -0
- package/lib/channels/dingtalk/i18n.d.ts.map +1 -0
- package/lib/channels/dingtalk/i18n.js +16 -0
- package/lib/channels/dingtalk/i18n.js.map +1 -0
- package/lib/channels/dingtalk/index.d.ts +127 -0
- package/lib/channels/dingtalk/index.d.ts.map +1 -0
- package/lib/channels/dingtalk/index.js +144 -0
- package/lib/channels/dingtalk/index.js.map +1 -0
- package/lib/channels/dingtalk/message.d.ts +49 -0
- package/lib/channels/dingtalk/message.d.ts.map +1 -0
- package/lib/channels/dingtalk/message.js +50 -0
- package/lib/channels/dingtalk/message.js.map +1 -0
- package/lib/channels/dingtalk/stomp.d.ts +36 -0
- package/lib/channels/dingtalk/stomp.d.ts.map +1 -0
- package/lib/channels/dingtalk/stomp.js +107 -0
- package/lib/channels/dingtalk/stomp.js.map +1 -0
- package/lib/channels/dingtalk/stream.d.ts +53 -0
- package/lib/channels/dingtalk/stream.d.ts.map +1 -0
- package/lib/channels/dingtalk/stream.js +199 -0
- package/lib/channels/dingtalk/stream.js.map +1 -0
- package/lib/channels/dingtalk/webhook.d.ts +84 -0
- package/lib/channels/dingtalk/webhook.d.ts.map +1 -0
- package/lib/channels/dingtalk/webhook.js +143 -0
- package/lib/channels/dingtalk/webhook.js.map +1 -0
- package/lib/channels/feishu/adapter.d.ts +133 -0
- package/lib/channels/feishu/adapter.d.ts.map +1 -0
- package/lib/channels/feishu/adapter.js +662 -0
- package/lib/channels/feishu/adapter.js.map +1 -0
- package/lib/channels/feishu/i18n.d.ts +30 -0
- package/lib/channels/feishu/i18n.d.ts.map +1 -0
- package/lib/channels/feishu/i18n.js +42 -0
- package/lib/channels/feishu/i18n.js.map +1 -0
- package/lib/channels/feishu/index.d.ts +82 -0
- package/lib/channels/feishu/index.d.ts.map +1 -0
- package/lib/channels/feishu/index.js +103 -0
- package/lib/channels/feishu/index.js.map +1 -0
- package/lib/channels/feishu/onboard.d.ts +16 -0
- package/lib/channels/feishu/onboard.d.ts.map +1 -0
- package/lib/channels/feishu/onboard.js +86 -0
- package/lib/channels/feishu/onboard.js.map +1 -0
- package/lib/channels/telegram/adapter.d.ts +70 -0
- package/lib/channels/telegram/adapter.d.ts.map +1 -0
- package/lib/channels/telegram/adapter.js +460 -0
- package/lib/channels/telegram/adapter.js.map +1 -0
- package/lib/channels/telegram/client.d.ts +115 -0
- package/lib/channels/telegram/client.d.ts.map +1 -0
- package/lib/channels/telegram/client.js +198 -0
- package/lib/channels/telegram/client.js.map +1 -0
- package/lib/channels/telegram/i18n.d.ts +14 -0
- package/lib/channels/telegram/i18n.d.ts.map +1 -0
- package/lib/channels/telegram/i18n.js +18 -0
- package/lib/channels/telegram/i18n.js.map +1 -0
- package/lib/channels/telegram/index.d.ts +46 -0
- package/lib/channels/telegram/index.d.ts.map +1 -0
- package/lib/channels/telegram/index.js +54 -0
- package/lib/channels/telegram/index.js.map +1 -0
- package/lib/channels/web/adapter.d.ts +103 -0
- package/lib/channels/web/adapter.d.ts.map +1 -0
- package/lib/channels/web/adapter.js +161 -0
- package/lib/channels/web/adapter.js.map +1 -0
- package/lib/channels/web/index.d.ts +47 -0
- package/lib/channels/web/index.d.ts.map +1 -0
- package/lib/channels/web/index.js +59 -0
- package/lib/channels/web/index.js.map +1 -0
- package/lib/chat-key.d.ts +29 -0
- package/lib/chat-key.d.ts.map +1 -0
- package/lib/chat-key.js +38 -0
- package/lib/chat-key.js.map +1 -0
- package/lib/index.d.ts +78 -11
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +312 -9
- package/lib/index.js.map +1 -1
- package/lib/runner.d.ts +55 -2
- package/lib/runner.d.ts.map +1 -1
- package/lib/runner.js +158 -23
- package/lib/runner.js.map +1 -1
- package/lib/scheduler.d.ts.map +1 -1
- package/lib/scheduler.js +2 -2
- package/lib/scheduler.js.map +1 -1
- package/lib/service.d.ts +17 -1
- package/lib/service.d.ts.map +1 -1
- package/lib/service.js +47 -3
- package/lib/service.js.map +1 -1
- package/lib/settings/channel-runtime.d.ts +75 -0
- package/lib/settings/channel-runtime.d.ts.map +1 -0
- package/lib/settings/channel-runtime.js +165 -0
- package/lib/settings/channel-runtime.js.map +1 -0
- package/lib/settings/channels.d.ts +62 -0
- package/lib/settings/channels.d.ts.map +1 -0
- package/lib/settings/channels.js +132 -0
- package/lib/settings/channels.js.map +1 -0
- package/lib/settings/credential-store.d.ts +85 -0
- package/lib/settings/credential-store.d.ts.map +1 -0
- package/lib/settings/credential-store.js +142 -0
- package/lib/settings/credential-store.js.map +1 -0
- package/lib/settings/index.d.ts +14 -0
- package/lib/settings/index.d.ts.map +1 -0
- package/lib/settings/index.js +14 -0
- package/lib/settings/index.js.map +1 -0
- package/lib/settings/namespace.d.ts +175 -0
- package/lib/settings/namespace.d.ts.map +1 -0
- package/lib/settings/namespace.js +200 -0
- package/lib/settings/namespace.js.map +1 -0
- package/lib/settings/rpc-client.d.ts +36 -0
- package/lib/settings/rpc-client.d.ts.map +1 -0
- package/lib/settings/rpc-client.js +45 -0
- package/lib/settings/rpc-client.js.map +1 -0
- package/lib/settings/secret-disclosure.d.ts +50 -0
- package/lib/settings/secret-disclosure.d.ts.map +1 -0
- package/lib/settings/secret-disclosure.js +132 -0
- package/lib/settings/secret-disclosure.js.map +1 -0
- package/lib/settings/settings-model.d.ts +124 -0
- package/lib/settings/settings-model.d.ts.map +1 -0
- package/lib/settings/settings-model.js +118 -0
- package/lib/settings/settings-model.js.map +1 -0
- package/lib/settings/settings-rpc.d.ts +130 -0
- package/lib/settings/settings-rpc.d.ts.map +1 -0
- package/lib/settings/settings-rpc.js +233 -0
- package/lib/settings/settings-rpc.js.map +1 -0
- package/lib/settings/settings-service.d.ts +35 -0
- package/lib/settings/settings-service.d.ts.map +1 -0
- package/lib/settings/settings-service.js +171 -0
- package/lib/settings/settings-service.js.map +1 -0
- package/lib/state-dir.d.ts +28 -0
- package/lib/state-dir.d.ts.map +1 -0
- package/lib/state-dir.js +33 -0
- package/lib/state-dir.js.map +1 -0
- package/lib/stream.d.ts.map +1 -1
- package/lib/stream.js +3 -2
- package/lib/stream.js.map +1 -1
- package/package.json +56 -12
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal STOMP 1.x frame codec used by the DingTalk stream-mode gateway.
|
|
3
|
+
* Pure string logic (no sockets) so it can be unit-tested without a network:
|
|
4
|
+
* DingTalk's stream gateway speaks STOMP over WebSocket with JSON bodies —
|
|
5
|
+
* CONNECT / CONNECTED / SUBSCRIBE / MESSAGE / SEND / ERROR frames and bare
|
|
6
|
+
* newline heartbeats.
|
|
7
|
+
* @module dsh-connect/channels/dingtalk/stomp
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Escape a header value per STOMP rules: backslash, newline, carriage
|
|
11
|
+
* return and colon become \\, \n, \r, \c.
|
|
12
|
+
*/
|
|
13
|
+
export function escapeHeader(value) {
|
|
14
|
+
let out = "";
|
|
15
|
+
for (const ch of value) {
|
|
16
|
+
switch (ch) {
|
|
17
|
+
case "\\":
|
|
18
|
+
out += "\\\\";
|
|
19
|
+
break;
|
|
20
|
+
case "\n":
|
|
21
|
+
out += "\\n";
|
|
22
|
+
break;
|
|
23
|
+
case "\r":
|
|
24
|
+
out += "\\r";
|
|
25
|
+
break;
|
|
26
|
+
case ":":
|
|
27
|
+
out += "\\c";
|
|
28
|
+
break;
|
|
29
|
+
default: out += ch;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
34
|
+
/** Reverse of {@link escapeHeader}. */
|
|
35
|
+
export function unescapeHeader(value) {
|
|
36
|
+
let out = "";
|
|
37
|
+
for (let i = 0; i < value.length; i++) {
|
|
38
|
+
const ch = value[i];
|
|
39
|
+
if (ch === "\\" && i + 1 < value.length) {
|
|
40
|
+
const next = value[++i];
|
|
41
|
+
if (next === "n")
|
|
42
|
+
out += "\n";
|
|
43
|
+
else if (next === "r")
|
|
44
|
+
out += "\r";
|
|
45
|
+
else if (next === "c")
|
|
46
|
+
out += ":";
|
|
47
|
+
else
|
|
48
|
+
out += "\\"; // "\\" escape sequence → a literal backslash
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
out += ch;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return out;
|
|
55
|
+
}
|
|
56
|
+
/** Serialize one frame (command, header block, blank line, body, NUL). */
|
|
57
|
+
export function encodeFrame(command, headers, body = "") {
|
|
58
|
+
const lines = [command];
|
|
59
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
60
|
+
lines.push(`${key}:${escapeHeader(value)}`);
|
|
61
|
+
}
|
|
62
|
+
lines.push("", body);
|
|
63
|
+
return `${lines.join("\n")}\0`;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Incrementally decode STOMP frames from a string buffer. Frames are
|
|
67
|
+
* NUL-terminated; heartbeats (bare newlines) and other whitespace-only
|
|
68
|
+
* segments are skipped. Returns the decoded frames plus the leftover tail.
|
|
69
|
+
*/
|
|
70
|
+
export function decodeFrames(buffer) {
|
|
71
|
+
const frames = [];
|
|
72
|
+
let rest = buffer;
|
|
73
|
+
for (;;) {
|
|
74
|
+
// STOMP heartbeats are bare EOLs (not NUL-terminated): strip any leading
|
|
75
|
+
// newlines before the next frame so a heartbeat doesn't glue onto it.
|
|
76
|
+
rest = rest.replace(/^(?:\r?\n)+/, "");
|
|
77
|
+
const nul = rest.indexOf("\0");
|
|
78
|
+
if (nul === -1)
|
|
79
|
+
break;
|
|
80
|
+
const segment = rest.slice(0, nul);
|
|
81
|
+
rest = rest.slice(nul + 1);
|
|
82
|
+
if (segment.replace(/\r?\n/g, "").trim() === "")
|
|
83
|
+
continue; // NUL-terminated heartbeat / stray EOL
|
|
84
|
+
const normalized = segment.replace(/\r\n/g, "\n");
|
|
85
|
+
const nl = normalized.indexOf("\n");
|
|
86
|
+
const command = (nl === -1 ? normalized : normalized.slice(0, nl)).trim();
|
|
87
|
+
const headerPart = nl === -1 ? "" : normalized.slice(nl + 1);
|
|
88
|
+
const headers = {};
|
|
89
|
+
let bodyStart = -1;
|
|
90
|
+
const lines = headerPart.split("\n");
|
|
91
|
+
for (let i = 0; i < lines.length; i++) {
|
|
92
|
+
const line = lines[i];
|
|
93
|
+
if (line === "") {
|
|
94
|
+
bodyStart = i + 1;
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
const colon = line.indexOf(":");
|
|
98
|
+
if (colon !== -1) {
|
|
99
|
+
headers[unescapeHeader(line.slice(0, colon))] = unescapeHeader(line.slice(colon + 1));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const body = bodyStart === -1 ? "" : lines.slice(bodyStart).join("\n");
|
|
103
|
+
frames.push({ command, headers, body });
|
|
104
|
+
}
|
|
105
|
+
return { frames, rest };
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=stomp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stomp.js","sourceRoot":"","sources":["../../../src/channels/dingtalk/stomp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type DingtalkBotMessage } from "./message.js";
|
|
2
|
+
export interface DingtalkStreamOptions {
|
|
3
|
+
clientId: string;
|
|
4
|
+
clientSecret: string;
|
|
5
|
+
/** Gateway URL (default wss://api.dingtalk.com/connect). */
|
|
6
|
+
url?: string;
|
|
7
|
+
logger?: {
|
|
8
|
+
info?: (...args: unknown[]) => void;
|
|
9
|
+
warn?: (...args: unknown[]) => void;
|
|
10
|
+
error?: (...args: unknown[]) => void;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export declare const DEFAULT_STREAM_URL = "wss://api.dingtalk.com/connect";
|
|
14
|
+
/**
|
|
15
|
+
* STOMP-over-WebSocket gateway client. Usage:
|
|
16
|
+
* ```ts
|
|
17
|
+
* const client = new DingtalkStreamClient({ clientId, clientSecret });
|
|
18
|
+
* client.onInbound((raw) => console.log(raw.conversationId, raw.text?.content));
|
|
19
|
+
* await client.connect();
|
|
20
|
+
* await client.sendReply(msgId, buildTextReplyBody(msgId, "hi"));
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare class DingtalkStreamClient {
|
|
24
|
+
private readonly clientId;
|
|
25
|
+
private readonly clientSecret;
|
|
26
|
+
private readonly url;
|
|
27
|
+
private readonly logger?;
|
|
28
|
+
private ws?;
|
|
29
|
+
private rest;
|
|
30
|
+
private heartbeat?;
|
|
31
|
+
private reconnectTimer?;
|
|
32
|
+
private backoffMs;
|
|
33
|
+
private closedByUs;
|
|
34
|
+
private messageHandler?;
|
|
35
|
+
constructor(options: DingtalkStreamOptions);
|
|
36
|
+
/** Register the inbound-message handler (called for every gateway push). */
|
|
37
|
+
onInbound(handler: (raw: DingtalkBotMessage) => void | Promise<void>): void;
|
|
38
|
+
/** Open the socket, perform the STOMP handshake and subscribe. Reconnect-safe. */
|
|
39
|
+
connect(): Promise<void>;
|
|
40
|
+
/** Send a reply to an inbound message (SEND frame to the reply topic). */
|
|
41
|
+
sendReply(msgId: string, body: string): Promise<void>;
|
|
42
|
+
/** Stop the client and cancel any scheduled reconnect. */
|
|
43
|
+
close(): void;
|
|
44
|
+
private onSocketOpen;
|
|
45
|
+
private onSocketMessage;
|
|
46
|
+
private onSocketClose;
|
|
47
|
+
private scheduleReconnect;
|
|
48
|
+
private handleFrame;
|
|
49
|
+
private sendFrame;
|
|
50
|
+
private startHeartbeat;
|
|
51
|
+
private stopHeartbeat;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=stream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../../src/channels/dingtalk/stream.ts"],"names":[],"mappings":"AAcA,OAAO,EAKL,KAAK,kBAAkB,EACxB,MAAM,cAAc,CAAC;AAatB,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,4DAA4D;IAC5D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE;QACP,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;QACpC,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;QACpC,KAAK,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;KACtC,CAAC;CACH;AAED,eAAO,MAAM,kBAAkB,mCAAmC,CAAC;AAUnE;;;;;;;;GAQG;AACH,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAkC;IAC1D,OAAO,CAAC,EAAE,CAAC,CAAgB;IAC3B,OAAO,CAAC,IAAI,CAAM;IAClB,OAAO,CAAC,SAAS,CAAC,CAAiB;IACnC,OAAO,CAAC,cAAc,CAAC,CAAiB;IACxC,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,cAAc,CAAC,CAAoD;gBAE/D,OAAO,EAAE,qBAAqB;IAO1C,4EAA4E;IAC5E,SAAS,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,kBAAkB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IAI3E,kFAAkF;IAC5E,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAqC9B,0EAA0E;IACpE,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ3D,0DAA0D;IAC1D,KAAK,IAAI,IAAI;IAWb,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,eAAe;IAMvB,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,iBAAiB;IAazB,OAAO,CAAC,WAAW;IAkCnB,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,aAAa;CAMtB"}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DingTalk stream-mode gateway client: STOMP over WebSocket, zero third-party
|
|
3
|
+
* dependencies (Node ≥ 22 global `WebSocket`; older Node needs a polyfill —
|
|
4
|
+
* the constructor is looked up lazily on `globalThis` so the module loads
|
|
5
|
+
* fine on Node 20, it only fails at connect time).
|
|
6
|
+
*
|
|
7
|
+
* This file is the live network boundary: it is intentionally thin and every
|
|
8
|
+
* protocol detail it depends on lives in the pure modules (stomp.ts,
|
|
9
|
+
* message.ts) that carry the unit tests. Actual connectivity requires real
|
|
10
|
+
* app credentials and an outbound connection to the DingTalk gateway, so it
|
|
11
|
+
* cannot be exercised in CI here.
|
|
12
|
+
* @module dsh-connect/channels/dingtalk/stream
|
|
13
|
+
*/
|
|
14
|
+
import { decodeFrames, encodeFrame } from "./stomp.js";
|
|
15
|
+
import { buildConnectBody, INBOUND_DESTINATION, INBOUND_SUBSCRIPTION, REPLY_DESTINATION, } from "./message.js";
|
|
16
|
+
export const DEFAULT_STREAM_URL = "wss://api.dingtalk.com/connect";
|
|
17
|
+
/** STOMP heartbeat interval (client → gateway), ms. */
|
|
18
|
+
const HEARTBEAT_MS = 10_000;
|
|
19
|
+
const RECONNECT_BASE_MS = 1_000;
|
|
20
|
+
const RECONNECT_MAX_MS = 30_000;
|
|
21
|
+
function lazyWebSocketCtor() {
|
|
22
|
+
return globalThis.WebSocket;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* STOMP-over-WebSocket gateway client. Usage:
|
|
26
|
+
* ```ts
|
|
27
|
+
* const client = new DingtalkStreamClient({ clientId, clientSecret });
|
|
28
|
+
* client.onInbound((raw) => console.log(raw.conversationId, raw.text?.content));
|
|
29
|
+
* await client.connect();
|
|
30
|
+
* await client.sendReply(msgId, buildTextReplyBody(msgId, "hi"));
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export class DingtalkStreamClient {
|
|
34
|
+
clientId;
|
|
35
|
+
clientSecret;
|
|
36
|
+
url;
|
|
37
|
+
logger;
|
|
38
|
+
ws;
|
|
39
|
+
rest = "";
|
|
40
|
+
heartbeat;
|
|
41
|
+
reconnectTimer;
|
|
42
|
+
backoffMs = RECONNECT_BASE_MS;
|
|
43
|
+
closedByUs = false;
|
|
44
|
+
messageHandler;
|
|
45
|
+
constructor(options) {
|
|
46
|
+
this.clientId = options.clientId;
|
|
47
|
+
this.clientSecret = options.clientSecret;
|
|
48
|
+
this.url = options.url ?? DEFAULT_STREAM_URL;
|
|
49
|
+
this.logger = options.logger;
|
|
50
|
+
}
|
|
51
|
+
/** Register the inbound-message handler (called for every gateway push). */
|
|
52
|
+
onInbound(handler) {
|
|
53
|
+
this.messageHandler = handler;
|
|
54
|
+
}
|
|
55
|
+
/** Open the socket, perform the STOMP handshake and subscribe. Reconnect-safe. */
|
|
56
|
+
async connect() {
|
|
57
|
+
const Ctor = lazyWebSocketCtor();
|
|
58
|
+
if (Ctor === undefined) {
|
|
59
|
+
throw new Error("connect-dingtalk: WebSocket is not available on this runtime (Node ≥ 22 or a polyfill required)");
|
|
60
|
+
}
|
|
61
|
+
if (this.closedByUs)
|
|
62
|
+
return;
|
|
63
|
+
this.logger?.info?.(`connect-dingtalk: connecting to ${this.url}`);
|
|
64
|
+
const ws = new Ctor(this.url);
|
|
65
|
+
this.ws = ws;
|
|
66
|
+
await new Promise((resolve, reject) => {
|
|
67
|
+
const timer = setTimeout(() => {
|
|
68
|
+
cleanup();
|
|
69
|
+
reject(new Error("connect-dingtalk: WebSocket handshake timed out"));
|
|
70
|
+
}, 15_000);
|
|
71
|
+
const cleanup = () => {
|
|
72
|
+
clearTimeout(timer);
|
|
73
|
+
ws.onopen = null;
|
|
74
|
+
ws.onerror = null;
|
|
75
|
+
};
|
|
76
|
+
ws.onopen = () => {
|
|
77
|
+
cleanup();
|
|
78
|
+
this.onSocketOpen();
|
|
79
|
+
resolve();
|
|
80
|
+
};
|
|
81
|
+
ws.onerror = () => {
|
|
82
|
+
// onclose fires next; the close handler owns reconnect scheduling.
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
ws.onmessage = (ev) => this.onSocketMessage(String(ev.data ?? ""));
|
|
86
|
+
ws.onclose = (ev) => this.onSocketClose(ev.code, ev.reason);
|
|
87
|
+
}
|
|
88
|
+
/** Send a reply to an inbound message (SEND frame to the reply topic). */
|
|
89
|
+
async sendReply(msgId, body) {
|
|
90
|
+
const ws = this.ws;
|
|
91
|
+
if (ws === undefined || ws.readyState !== 1 /* OPEN */) {
|
|
92
|
+
throw new Error("connect-dingtalk: stream not connected");
|
|
93
|
+
}
|
|
94
|
+
ws.send(encodeFrame("SEND", { destination: REPLY_DESTINATION }, body));
|
|
95
|
+
}
|
|
96
|
+
/** Stop the client and cancel any scheduled reconnect. */
|
|
97
|
+
close() {
|
|
98
|
+
this.closedByUs = true;
|
|
99
|
+
if (this.reconnectTimer !== undefined) {
|
|
100
|
+
clearTimeout(this.reconnectTimer);
|
|
101
|
+
this.reconnectTimer = undefined;
|
|
102
|
+
}
|
|
103
|
+
this.stopHeartbeat();
|
|
104
|
+
this.ws?.close(1000, "bye");
|
|
105
|
+
this.ws = undefined;
|
|
106
|
+
}
|
|
107
|
+
onSocketOpen() {
|
|
108
|
+
this.logger?.info?.("connect-dingtalk: socket open, sending CONNECT");
|
|
109
|
+
this.sendFrame("CONNECT", {
|
|
110
|
+
"accept-version": "1.2",
|
|
111
|
+
host: "api.dingtalk.com",
|
|
112
|
+
"heart-beat": `${HEARTBEAT_MS},${HEARTBEAT_MS}`,
|
|
113
|
+
}, buildConnectBody(this.clientId, this.clientSecret));
|
|
114
|
+
}
|
|
115
|
+
onSocketMessage(data) {
|
|
116
|
+
const { frames, rest } = decodeFrames(this.rest + data);
|
|
117
|
+
this.rest = rest;
|
|
118
|
+
for (const frame of frames)
|
|
119
|
+
this.handleFrame(frame);
|
|
120
|
+
}
|
|
121
|
+
onSocketClose(code, reason) {
|
|
122
|
+
this.stopHeartbeat();
|
|
123
|
+
this.ws = undefined;
|
|
124
|
+
if (this.closedByUs)
|
|
125
|
+
return;
|
|
126
|
+
this.logger?.warn?.(`connect-dingtalk: socket closed (code=${String(code)} reason=${String(reason)}), reconnecting in ${this.backoffMs}ms`);
|
|
127
|
+
this.scheduleReconnect();
|
|
128
|
+
}
|
|
129
|
+
scheduleReconnect() {
|
|
130
|
+
if (this.closedByUs || this.reconnectTimer !== undefined)
|
|
131
|
+
return;
|
|
132
|
+
const delay = this.backoffMs;
|
|
133
|
+
this.backoffMs = Math.min(this.backoffMs * 2, RECONNECT_MAX_MS);
|
|
134
|
+
this.reconnectTimer = setTimeout(() => {
|
|
135
|
+
this.reconnectTimer = undefined;
|
|
136
|
+
void this.connect().catch((error) => {
|
|
137
|
+
this.logger?.error?.(`connect-dingtalk: reconnect attempt failed: ${String(error)}`);
|
|
138
|
+
this.scheduleReconnect();
|
|
139
|
+
});
|
|
140
|
+
}, delay);
|
|
141
|
+
}
|
|
142
|
+
handleFrame(frame) {
|
|
143
|
+
switch (frame.command) {
|
|
144
|
+
case "CONNECTED": {
|
|
145
|
+
this.backoffMs = RECONNECT_BASE_MS;
|
|
146
|
+
this.logger?.info?.("connect-dingtalk: CONNECTED, subscribing");
|
|
147
|
+
this.sendFrame("SUBSCRIBE", { id: INBOUND_SUBSCRIPTION, destination: INBOUND_DESTINATION });
|
|
148
|
+
this.startHeartbeat();
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
case "MESSAGE": {
|
|
152
|
+
if (this.messageHandler === undefined)
|
|
153
|
+
break;
|
|
154
|
+
try {
|
|
155
|
+
const raw = JSON.parse(frame.body);
|
|
156
|
+
void Promise.resolve(this.messageHandler(raw)).catch((error) => {
|
|
157
|
+
this.logger?.warn?.(`connect-dingtalk: inbound handler failed: ${String(error)}`);
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
this.logger?.warn?.(`connect-dingtalk: malformed gateway payload: ${String(error)}`);
|
|
162
|
+
}
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
case "ERROR": {
|
|
166
|
+
this.logger?.error?.(`connect-dingtalk: gateway ERROR: ${frame.body || frame.headers.message || "no detail"}`);
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
case "RECEIPT": {
|
|
170
|
+
this.logger?.info?.("connect-dingtalk: gateway receipt received");
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
default:
|
|
174
|
+
this.logger?.warn?.(`connect-dingtalk: unhandled STOMP frame ${frame.command}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
sendFrame(command, headers, body = "") {
|
|
178
|
+
const ws = this.ws;
|
|
179
|
+
if (ws === undefined || ws.readyState !== 1)
|
|
180
|
+
return;
|
|
181
|
+
ws.send(encodeFrame(command, headers, body));
|
|
182
|
+
}
|
|
183
|
+
startHeartbeat() {
|
|
184
|
+
this.stopHeartbeat();
|
|
185
|
+
this.heartbeat = setInterval(() => {
|
|
186
|
+
const ws = this.ws;
|
|
187
|
+
if (ws !== undefined && ws.readyState === 1)
|
|
188
|
+
ws.send("\n");
|
|
189
|
+
}, HEARTBEAT_MS);
|
|
190
|
+
this.heartbeat.unref?.();
|
|
191
|
+
}
|
|
192
|
+
stopHeartbeat() {
|
|
193
|
+
if (this.heartbeat !== undefined) {
|
|
194
|
+
clearInterval(this.heartbeat);
|
|
195
|
+
this.heartbeat = undefined;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.js","sourceRoot":"","sources":["../../../src/channels/dingtalk/stream.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/** DingTalk webhook configuration. */
|
|
2
|
+
export interface DingtalkWebhookConfig {
|
|
3
|
+
/** Full webhook URL, e.g. `https://oapi.dingtalk.com/robot/send?access_token=xxx`. */
|
|
4
|
+
webhookUrl: string;
|
|
5
|
+
/** Optional signing secret (`SEC...`) — when set, requests are signed. */
|
|
6
|
+
secret?: string;
|
|
7
|
+
/** HTTP timeout in ms (default 10s). */
|
|
8
|
+
timeoutMs?: number;
|
|
9
|
+
/** Backoff delays between retries for transient network errors (default 1s/2s/4s). */
|
|
10
|
+
retryDelaysMs?: readonly number[];
|
|
11
|
+
/** Extra delay for the 130101 frequency-limit retry (default 10s). */
|
|
12
|
+
rateLimitDelayMs?: number;
|
|
13
|
+
}
|
|
14
|
+
/** Body shape accepted by the DingTalk robot API. */
|
|
15
|
+
export type DingtalkBody = {
|
|
16
|
+
msgtype: "text";
|
|
17
|
+
text: {
|
|
18
|
+
content: string;
|
|
19
|
+
};
|
|
20
|
+
at?: {
|
|
21
|
+
atMobiles?: string[];
|
|
22
|
+
atUserIds?: string[];
|
|
23
|
+
isAtAll?: boolean;
|
|
24
|
+
};
|
|
25
|
+
} | {
|
|
26
|
+
msgtype: "markdown";
|
|
27
|
+
markdown: {
|
|
28
|
+
title: string;
|
|
29
|
+
text: string;
|
|
30
|
+
};
|
|
31
|
+
at?: {
|
|
32
|
+
atMobiles?: string[];
|
|
33
|
+
atUserIds?: string[];
|
|
34
|
+
isAtAll?: boolean;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
/** Response of the DingTalk robot API. */
|
|
38
|
+
export interface DingtalkResponse {
|
|
39
|
+
errcode: number;
|
|
40
|
+
errmsg: string;
|
|
41
|
+
}
|
|
42
|
+
/** How to address people in a DingTalk group. */
|
|
43
|
+
export interface DingtalkAt {
|
|
44
|
+
/** Phone numbers — required by DingTalk for `atMobiles` (robots can't resolve names). */
|
|
45
|
+
mobiles?: readonly string[];
|
|
46
|
+
/** User ids (unionid/`userId` when known). */
|
|
47
|
+
userIds?: readonly string[];
|
|
48
|
+
/** @everyone. */
|
|
49
|
+
all?: boolean;
|
|
50
|
+
}
|
|
51
|
+
/** DingTalk caps markdown body text at 20000 chars. */
|
|
52
|
+
export declare const MARKDOWN_LIMIT = 20000;
|
|
53
|
+
/**
|
|
54
|
+
* Compute the DingTalk webhook signature. The SDK expects
|
|
55
|
+
* `timestamp + "\n" + secret` signed with HMAC-SHA256, base64-encoded, then
|
|
56
|
+
* URL-encoded as the `sign` query param.
|
|
57
|
+
*/
|
|
58
|
+
export declare function signDingtalk(secret: string, timestampMs: number): string;
|
|
59
|
+
/**
|
|
60
|
+
* Verify an incoming signature against a secret (used by webhook receivers, if
|
|
61
|
+
* ever needed). Both sides are compared after URL-decoding, so a sender that
|
|
62
|
+
* encoded `+` as `%2B` (or vice versa) still verifies, and the timestamp must
|
|
63
|
+
* be fresh (default: ±5 minutes) to be replay-resistant.
|
|
64
|
+
*/
|
|
65
|
+
export declare function verifyDingtalkSignature(secret: string, timestampMs: string, sign: string, nowMs?: number, maxAgeMs?: number): boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Minimal DingTalk group-robot webhook client built on the global `fetch`
|
|
68
|
+
* (Node ≥ 18 / ≥ 20 — no third-party HTTP dependency). Transient failures
|
|
69
|
+
* (network errors, the 20/min frequency limit) are retried with backoff.
|
|
70
|
+
*/
|
|
71
|
+
export declare class DingtalkWebhook {
|
|
72
|
+
readonly config: DingtalkWebhookConfig;
|
|
73
|
+
constructor(config: DingtalkWebhookConfig);
|
|
74
|
+
/**
|
|
75
|
+
* Send a markdown message. `title` is shown as the message's card title in
|
|
76
|
+
* DingTalk; `text` is the markdown body (truncated to 20000 chars, the API
|
|
77
|
+
* limit). Optionally @-mention people.
|
|
78
|
+
*/
|
|
79
|
+
sendMarkdown(title: string, text: string, at?: DingtalkAt): Promise<DingtalkResponse>;
|
|
80
|
+
/** Send a plain-text message (content shown verbatim, `\n` newlines allowed). */
|
|
81
|
+
sendText(content: string, at?: DingtalkAt): Promise<DingtalkResponse>;
|
|
82
|
+
private send;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=webhook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../../src/channels/dingtalk/webhook.ts"],"names":[],"mappings":"AAYA,sCAAsC;AACtC,MAAM,WAAW,qBAAqB;IACpC,sFAAsF;IACtF,UAAU,EAAE,MAAM,CAAC;IACnB,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sFAAsF;IACtF,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,sEAAsE;IACtE,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,qDAAqD;AACrD,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1B,EAAE,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CACxE,GAAG;IACF,OAAO,EAAE,UAAU,CAAC;IACpB,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1C,EAAE,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CACxE,CAAC;AAEF,0CAA0C;AAC1C,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,iDAAiD;AACjD,MAAM,WAAW,UAAU;IACzB,yFAAyF;IACzF,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,8CAA8C;IAC9C,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,iBAAiB;IACjB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,uDAAuD;AACvD,eAAO,MAAM,cAAc,QAAS,CAAC;AAIrC;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAIxE;AAUD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,EACZ,KAAK,GAAE,MAAmB,EAC1B,QAAQ,GAAE,MAAmB,GAC5B,OAAO,CAOT;AA2BD;;;;GAIG;AACH,qBAAa,eAAe;IAC1B,QAAQ,CAAC,MAAM,EAAE,qBAAqB,CAAC;gBAE3B,MAAM,EAAE,qBAAqB;IAOzC;;;;OAIG;IACG,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAK3F,iFAAiF;IAC3E,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,gBAAgB,CAAC;YAI7D,IAAI;CAiCnB"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DingTalk group-robot webhook transport. A webhook URL is created from a
|
|
3
|
+
* group's "custom robot" (群自定义机器人): the group owner adds a robot, gets
|
|
4
|
+
* an `access_token`, and optionally enables a signing secret. This transport
|
|
5
|
+
* only *pushes* — DingTalk custom webhooks cannot receive messages, so this
|
|
6
|
+
* package is a one-way notice channel (progress / results / alerts), not a
|
|
7
|
+
* bidirectional conversation adapter like the Feishu one.
|
|
8
|
+
*
|
|
9
|
+
* @module dsh-connect/channels/dingtalk/webhook
|
|
10
|
+
*/
|
|
11
|
+
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
12
|
+
/** DingTalk caps markdown body text at 20000 chars. */
|
|
13
|
+
export const MARKDOWN_LIMIT = 20_000;
|
|
14
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
15
|
+
/**
|
|
16
|
+
* Compute the DingTalk webhook signature. The SDK expects
|
|
17
|
+
* `timestamp + "\n" + secret` signed with HMAC-SHA256, base64-encoded, then
|
|
18
|
+
* URL-encoded as the `sign` query param.
|
|
19
|
+
*/
|
|
20
|
+
export function signDingtalk(secret, timestampMs) {
|
|
21
|
+
const stringToSign = `${timestampMs}\n${secret}`;
|
|
22
|
+
const mac = createHmac("sha256", secret).update(stringToSign, "utf8").digest("base64");
|
|
23
|
+
return encodeURIComponent(mac);
|
|
24
|
+
}
|
|
25
|
+
function safeDecode(value) {
|
|
26
|
+
try {
|
|
27
|
+
return decodeURIComponent(value);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return value; // not URL-encoded — compare as-is
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Verify an incoming signature against a secret (used by webhook receivers, if
|
|
35
|
+
* ever needed). Both sides are compared after URL-decoding, so a sender that
|
|
36
|
+
* encoded `+` as `%2B` (or vice versa) still verifies, and the timestamp must
|
|
37
|
+
* be fresh (default: ±5 minutes) to be replay-resistant.
|
|
38
|
+
*/
|
|
39
|
+
export function verifyDingtalkSignature(secret, timestampMs, sign, nowMs = Date.now(), maxAgeMs = 5 * 60_000) {
|
|
40
|
+
const ts = Number(timestampMs);
|
|
41
|
+
if (!Number.isFinite(ts) || Math.abs(nowMs - ts) > maxAgeMs)
|
|
42
|
+
return false;
|
|
43
|
+
const expected = signDingtalk(secret, ts);
|
|
44
|
+
const a = Buffer.from(safeDecode(sign));
|
|
45
|
+
const b = Buffer.from(safeDecode(expected));
|
|
46
|
+
return a.length === b.length && timingSafeEqual(a, b);
|
|
47
|
+
}
|
|
48
|
+
/** Replace non-url-safe chars in a DingTalk webhook URL's query (idempotent-safe). */
|
|
49
|
+
function withSignature(url, secret, timestampMs) {
|
|
50
|
+
const sign = signDingtalk(secret, timestampMs);
|
|
51
|
+
const sep = url.includes("?") ? "&" : "?";
|
|
52
|
+
return `${url}${sep}timestamp=${timestampMs}&sign=${sign}`;
|
|
53
|
+
}
|
|
54
|
+
/** Errors that are safe to retry: transient network issues and the frequency limit. */
|
|
55
|
+
function isRetryable(error) {
|
|
56
|
+
const errcode = error.errcode;
|
|
57
|
+
if (errcode === 130101)
|
|
58
|
+
return true; // 发送频率限制 (20/min per robot)
|
|
59
|
+
if (error instanceof TypeError)
|
|
60
|
+
return true; // fetch network failure
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
function delayFor(config, error, attempt) {
|
|
64
|
+
const errcode = error.errcode;
|
|
65
|
+
if (errcode === 130101)
|
|
66
|
+
return config.rateLimitDelayMs ?? 10_000; // rate limit: wait for the window to pass
|
|
67
|
+
const backoff = config.retryDelaysMs;
|
|
68
|
+
if (backoff !== undefined && backoff.length > 0)
|
|
69
|
+
return backoff[Math.min(attempt, backoff.length - 1)] ?? 1_000;
|
|
70
|
+
return 1_000 * 2 ** attempt; // 1s, 2s, 4s…
|
|
71
|
+
}
|
|
72
|
+
const MAX_ATTEMPTS = 3;
|
|
73
|
+
/**
|
|
74
|
+
* Minimal DingTalk group-robot webhook client built on the global `fetch`
|
|
75
|
+
* (Node ≥ 18 / ≥ 20 — no third-party HTTP dependency). Transient failures
|
|
76
|
+
* (network errors, the 20/min frequency limit) are retried with backoff.
|
|
77
|
+
*/
|
|
78
|
+
export class DingtalkWebhook {
|
|
79
|
+
config;
|
|
80
|
+
constructor(config) {
|
|
81
|
+
if (!config.webhookUrl || !config.webhookUrl.startsWith("https://")) {
|
|
82
|
+
throw new Error("connect-dingtalk: webhookUrl must be a https:// DingTalk robot URL");
|
|
83
|
+
}
|
|
84
|
+
this.config = config;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Send a markdown message. `title` is shown as the message's card title in
|
|
88
|
+
* DingTalk; `text` is the markdown body (truncated to 20000 chars, the API
|
|
89
|
+
* limit). Optionally @-mention people.
|
|
90
|
+
*/
|
|
91
|
+
async sendMarkdown(title, text, at) {
|
|
92
|
+
const body = text.length > MARKDOWN_LIMIT ? `${text.slice(0, MARKDOWN_LIMIT)}\n\n…(已截断)` : text;
|
|
93
|
+
return this.send({ msgtype: "markdown", markdown: { title, text: body }, at: atBody(at) });
|
|
94
|
+
}
|
|
95
|
+
/** Send a plain-text message (content shown verbatim, `\n` newlines allowed). */
|
|
96
|
+
async sendText(content, at) {
|
|
97
|
+
return this.send({ msgtype: "text", text: { content }, at: atBody(at) });
|
|
98
|
+
}
|
|
99
|
+
async send(body, attempt = 0) {
|
|
100
|
+
const { webhookUrl, secret, timeoutMs = 10_000 } = this.config;
|
|
101
|
+
const url = secret ? withSignature(webhookUrl, secret, Date.now()) : webhookUrl;
|
|
102
|
+
const controller = new AbortController();
|
|
103
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
104
|
+
try {
|
|
105
|
+
const response = await fetch(url, {
|
|
106
|
+
method: "POST",
|
|
107
|
+
headers: { "Content-Type": "application/json" },
|
|
108
|
+
body: JSON.stringify(body),
|
|
109
|
+
signal: controller.signal,
|
|
110
|
+
});
|
|
111
|
+
if (!response.ok) {
|
|
112
|
+
throw new Error(`connect-dingtalk: HTTP ${response.status} ${response.statusText}`);
|
|
113
|
+
}
|
|
114
|
+
const parsed = (await response.json());
|
|
115
|
+
if (parsed.errcode !== 0) {
|
|
116
|
+
const error = new Error(`connect-dingtalk: DingTalk error ${parsed.errcode}: ${parsed.errmsg}`);
|
|
117
|
+
error.errcode = parsed.errcode;
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
return parsed;
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
if (isRetryable(error) && attempt < MAX_ATTEMPTS) {
|
|
124
|
+
await sleep(delayFor(this.config, error, attempt));
|
|
125
|
+
return this.send(body, attempt + 1);
|
|
126
|
+
}
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
finally {
|
|
130
|
+
clearTimeout(timer);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
function atBody(at) {
|
|
135
|
+
if (at === undefined)
|
|
136
|
+
return undefined;
|
|
137
|
+
return {
|
|
138
|
+
...(at.mobiles !== undefined && at.mobiles.length > 0 ? { atMobiles: [...at.mobiles] } : {}),
|
|
139
|
+
...(at.userIds !== undefined && at.userIds.length > 0 ? { atUserIds: [...at.userIds] } : {}),
|
|
140
|
+
...(at.all === true ? { isAtAll: true } : {}),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=webhook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook.js","sourceRoot":"","sources":["../../../src/channels/dingtalk/webhook.ts"],"names":[],"mappings":"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"}
|