@qwen-code/channel-dingtalk 0.21.0-preview.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/media.js ADDED
@@ -0,0 +1,93 @@
1
+ /**
2
+ * DingTalk media download helpers.
3
+ *
4
+ * Two-step flow:
5
+ * 1. POST downloadCode to DingTalk API → get a temporary downloadUrl
6
+ * 2. GET the downloadUrl → arraybuffer
7
+ */
8
+ const DOWNLOAD_API = 'https://api.dingtalk.com/v1.0/robot/messageFiles/download';
9
+ /**
10
+ * Download a media file from DingTalk using a downloadCode.
11
+ *
12
+ * @param downloadCode - The code from incoming message richText/content
13
+ * @param robotCode - The bot's clientId (appKey)
14
+ * @param accessToken - A valid DingTalk access token
15
+ * @returns MediaFile with buffer and mimeType, or null on failure
16
+ */
17
+ export async function downloadMedia(downloadCode, robotCode, accessToken) {
18
+ if (!downloadCode || !robotCode || !accessToken) {
19
+ return null;
20
+ }
21
+ try {
22
+ // Step 1: Get downloadUrl from DingTalk API
23
+ const apiResp = await fetch(DOWNLOAD_API, {
24
+ method: 'POST',
25
+ headers: {
26
+ 'x-acs-dingtalk-access-token': accessToken,
27
+ 'Content-Type': 'application/json',
28
+ },
29
+ body: JSON.stringify({ downloadCode, robotCode }),
30
+ });
31
+ if (!apiResp.ok) {
32
+ const detail = await apiResp.text().catch(() => '');
33
+ process.stderr.write(`[DingTalk] downloadMedia API failed: HTTP ${apiResp.status} ${detail}\n`);
34
+ return null;
35
+ }
36
+ const payload = (await apiResp.json());
37
+ const downloadUrl = payload['downloadUrl'] ??
38
+ payload['data']?.['downloadUrl'];
39
+ if (!downloadUrl) {
40
+ process.stderr.write(`[DingTalk] downloadMedia: no downloadUrl in response\n`);
41
+ return null;
42
+ }
43
+ // Step 2: Download the actual file. Bound the request so a stalled CDN
44
+ // connection can't hang the streaming loop below indefinitely (matches
45
+ // the Feishu adapter).
46
+ const fileResp = await fetch(downloadUrl, {
47
+ signal: AbortSignal.timeout(30_000),
48
+ });
49
+ if (!fileResp.ok) {
50
+ process.stderr.write(`[DingTalk] downloadMedia file fetch failed: HTTP ${fileResp.status}\n`);
51
+ return null;
52
+ }
53
+ const MAX_DOWNLOAD_BYTES = 50 * 1024 * 1024; // 50 MB
54
+ const contentLength = fileResp.headers.get('content-length');
55
+ if (contentLength && parseInt(contentLength, 10) > MAX_DOWNLOAD_BYTES) {
56
+ // Release the connection instead of letting the unread body pin it
57
+ // until GC.
58
+ await fileResp.body?.cancel();
59
+ process.stderr.write(`[DingTalk] downloadMedia rejected: size ${contentLength} exceeds ${MAX_DOWNLOAD_BYTES} byte limit\n`);
60
+ return null;
61
+ }
62
+ const mimeType = fileResp.headers.get('content-type') || 'application/octet-stream';
63
+ // Stream-read with size enforcement (handles chunked transfer without Content-Length)
64
+ const reader = fileResp.body?.getReader();
65
+ if (!reader) {
66
+ return null;
67
+ }
68
+ const chunks = [];
69
+ let totalSize = 0;
70
+ while (true) {
71
+ const { done, value } = await reader.read();
72
+ if (done)
73
+ break;
74
+ totalSize += value.byteLength;
75
+ if (totalSize > MAX_DOWNLOAD_BYTES) {
76
+ // Await the cancel so a stream error during teardown surfaces here
77
+ // instead of becoming an unhandled rejection (matches the body.cancel
78
+ // above).
79
+ await reader.cancel();
80
+ process.stderr.write(`[DingTalk] downloadMedia rejected: actual size exceeds ${MAX_DOWNLOAD_BYTES} byte limit\n`);
81
+ return null;
82
+ }
83
+ chunks.push(Buffer.from(value));
84
+ }
85
+ const buffer = Buffer.concat(chunks);
86
+ return { buffer, mimeType };
87
+ }
88
+ catch (err) {
89
+ process.stderr.write(`[DingTalk] downloadMedia error: ${err instanceof Error ? err.message : err}\n`);
90
+ return null;
91
+ }
92
+ }
93
+ //# sourceMappingURL=media.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media.js","sourceRoot":"","sources":["../src/media.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@qwen-code/channel-dingtalk",
3
+ "version": "0.21.0-preview.0",
4
+ "description": "DingTalk channel adapter for Qwen Code",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "scripts": {
18
+ "build": "tsc --build",
19
+ "test": "vitest run",
20
+ "test:ci": "vitest run"
21
+ },
22
+ "dependencies": {
23
+ "@qwen-code/channel-base": "^0.21.0",
24
+ "dingtalk-stream-sdk-nodejs": "^2.0.4"
25
+ },
26
+ "devDependencies": {
27
+ "typescript": "^5.0.0"
28
+ }
29
+ }