dingtalk-manager 1.0.0 → 1.1.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 +63 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +355 -0
- package/dist/cli.js.map +13 -0
- package/package.json +8 -2
package/README.md
CHANGED
|
@@ -15,6 +15,69 @@ yarn add dingtalk-manager
|
|
|
15
15
|
bun add dingtalk-manager
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
+
## 命令行工具
|
|
19
|
+
|
|
20
|
+
本包支持通过 `bunx` 或 `npx` 直接作为命令行工具使用,无需安装。
|
|
21
|
+
|
|
22
|
+
### 配置凭证
|
|
23
|
+
|
|
24
|
+
首先设置环境变量:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
export DINGTALK_APP_KEY="your-app-key"
|
|
28
|
+
export DINGTALK_APP_SECRET="your-app-secret"
|
|
29
|
+
export DINGTALK_AGENT_ID="your-agent-id"
|
|
30
|
+
export DINGTALK_USER_ID="target-user-id"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
或者使用凭证文件(`--credentials-path` 参数):
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
bunx dingtalk-manager@latest sendMarkdown --credentials-path /path/to/credentials.json --title "标题" --content "内容"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### 发送 Markdown 消息
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
bunx dingtalk-manager@latest sendMarkdown --title "标题" --content "**加粗** 内容\n- 项目 1\n- 项目 2"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 发送文本消息
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
bunx dingtalk-manager@latest sendText --content "纯文本消息"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### 输出格式
|
|
52
|
+
|
|
53
|
+
命令执行成功时输出 JSON 格式的结果:
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"success": true,
|
|
58
|
+
"taskId": "123456789"
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
失败时:
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"success": false,
|
|
67
|
+
"error": "错误信息"
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### 帮助信息
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
bunx dingtalk-manager@latest --help
|
|
75
|
+
bunx dingtalk-manager@latest sendMarkdown --help
|
|
76
|
+
bunx dingtalk-manager@latest sendText --help
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
18
81
|
## 快速开始
|
|
19
82
|
|
|
20
83
|
### 方式一:直接传入凭证
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
6
|
+
import { dirname, join as join2 } from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
|
|
9
|
+
// src/utils.ts
|
|
10
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import { tmpdir } from "node:os";
|
|
13
|
+
|
|
14
|
+
// src/errors.ts
|
|
15
|
+
class DingTalkError extends Error {
|
|
16
|
+
constructor(message) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = "DingTalkError";
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
class TokenError extends DingTalkError {
|
|
23
|
+
constructor(message) {
|
|
24
|
+
super(message);
|
|
25
|
+
this.name = "TokenError";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
class MessageError extends DingTalkError {
|
|
30
|
+
constructor(message) {
|
|
31
|
+
super(message);
|
|
32
|
+
this.name = "MessageError";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
class CredentialError extends DingTalkError {
|
|
37
|
+
constructor(message) {
|
|
38
|
+
super(message);
|
|
39
|
+
this.name = "CredentialError";
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// src/utils.ts
|
|
44
|
+
var ENV_KEYS = {
|
|
45
|
+
APP_KEY: "DINGTALK_APP_KEY",
|
|
46
|
+
APP_SECRET: "DINGTALK_APP_SECRET",
|
|
47
|
+
AGENT_ID: "DINGTALK_AGENT_ID",
|
|
48
|
+
USER_ID: "DINGTALK_USER_ID"
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
class TokenManager {
|
|
52
|
+
cacheDir;
|
|
53
|
+
cacheFile;
|
|
54
|
+
constructor(cacheDir) {
|
|
55
|
+
this.cacheDir = cacheDir ?? tmpdir();
|
|
56
|
+
this.cacheFile = join(this.cacheDir, "dingtalk-token-cache.json");
|
|
57
|
+
}
|
|
58
|
+
load() {
|
|
59
|
+
try {
|
|
60
|
+
if (!existsSync(this.cacheFile)) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
const content = readFileSync(this.cacheFile, "utf-8");
|
|
64
|
+
return JSON.parse(content);
|
|
65
|
+
} catch {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
save(cache) {
|
|
70
|
+
try {
|
|
71
|
+
if (!existsSync(this.cacheDir)) {
|
|
72
|
+
mkdirSync(this.cacheDir, { recursive: true, mode: 448 });
|
|
73
|
+
}
|
|
74
|
+
writeFileSync(this.cacheFile, JSON.stringify(cache, null, 2), { mode: 384 });
|
|
75
|
+
} catch (error) {
|
|
76
|
+
console.error("Failed to cache DingTalk token:", error instanceof Error ? error.message : String(error));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
isValid(cache) {
|
|
80
|
+
return cache !== null && cache.expiresAt > Date.now();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function loadCredentials(options) {
|
|
84
|
+
if (options?.credentials) {
|
|
85
|
+
validateCredentials(options.credentials);
|
|
86
|
+
return options.credentials;
|
|
87
|
+
}
|
|
88
|
+
if (options?.credentialsPath) {
|
|
89
|
+
const creds = loadFromFile(options.credentialsPath);
|
|
90
|
+
if (creds) {
|
|
91
|
+
validateCredentials(creds);
|
|
92
|
+
return creds;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const envCreds = loadFromEnv();
|
|
96
|
+
if (envCreds) {
|
|
97
|
+
validateCredentials(envCreds);
|
|
98
|
+
return envCreds;
|
|
99
|
+
}
|
|
100
|
+
throw new CredentialError("DingTalk credentials not found. Provide credentials via options.credentials, options.credentialsPath, or environment variables (DINGTALK_APP_KEY, DINGTALK_APP_SECRET, DINGTALK_AGENT_ID, DINGTALK_USER_ID)");
|
|
101
|
+
}
|
|
102
|
+
function loadFromEnv() {
|
|
103
|
+
const appKey = process.env[ENV_KEYS.APP_KEY];
|
|
104
|
+
const appSecret = process.env[ENV_KEYS.APP_SECRET];
|
|
105
|
+
const agentId = process.env[ENV_KEYS.AGENT_ID];
|
|
106
|
+
const userId = process.env[ENV_KEYS.USER_ID];
|
|
107
|
+
if (appKey && appSecret && agentId && userId) {
|
|
108
|
+
return { appKey, appSecret, agentId, userId };
|
|
109
|
+
}
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
function loadFromFile(path) {
|
|
113
|
+
try {
|
|
114
|
+
if (!existsSync(path)) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
const content = readFileSync(path, "utf-8");
|
|
118
|
+
const data = JSON.parse(content);
|
|
119
|
+
return {
|
|
120
|
+
appKey: data.appKey,
|
|
121
|
+
appSecret: data.appSecret,
|
|
122
|
+
agentId: data.agentId,
|
|
123
|
+
userId: data.userId ?? data.userid
|
|
124
|
+
};
|
|
125
|
+
} catch {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function validateCredentials(creds) {
|
|
130
|
+
const missing = [];
|
|
131
|
+
if (!creds.appKey)
|
|
132
|
+
missing.push("appKey");
|
|
133
|
+
if (!creds.appSecret)
|
|
134
|
+
missing.push("appSecret");
|
|
135
|
+
if (!creds.agentId)
|
|
136
|
+
missing.push("agentId");
|
|
137
|
+
if (!creds.userId)
|
|
138
|
+
missing.push("userId");
|
|
139
|
+
if (missing.length > 0) {
|
|
140
|
+
throw new CredentialError(`Missing required credentials: ${missing.join(", ")}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
var FORBIDDEN_CODES = {
|
|
144
|
+
"143103": {
|
|
145
|
+
description: "企业应用消息发送QPM(每分钟请求数)超限",
|
|
146
|
+
solution: "请稍后重试,或优化发送频率"
|
|
147
|
+
},
|
|
148
|
+
"143104": {
|
|
149
|
+
description: "企业每分钟发送QPM超限",
|
|
150
|
+
solution: "请稍后重试,或优化发送频率"
|
|
151
|
+
},
|
|
152
|
+
"143105": {
|
|
153
|
+
description: "应用发送日限超限(自建应用: 500条/天/用户, ISV应用: 50条/天/用户)",
|
|
154
|
+
solution: "已达到每日发送上限,请明天再试"
|
|
155
|
+
},
|
|
156
|
+
"143106": {
|
|
157
|
+
description: "应用重复消息发送限超限(同一用户每天只能接收一条相同内容的消息)",
|
|
158
|
+
solution: "修改消息内容后重新发送,或等待明天自动重置"
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
function getForbiddenCodeInfo(code) {
|
|
162
|
+
return FORBIDDEN_CODES[code] ?? null;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// src/client.ts
|
|
166
|
+
var TOKEN_EXPIRY_BUFFER_SECONDS = 300;
|
|
167
|
+
|
|
168
|
+
class DingTalk {
|
|
169
|
+
credentials;
|
|
170
|
+
tokenManager;
|
|
171
|
+
constructor(options) {
|
|
172
|
+
this.credentials = loadCredentials(options);
|
|
173
|
+
this.tokenManager = new TokenManager(options?.tokenCacheDir);
|
|
174
|
+
}
|
|
175
|
+
async getAccessToken() {
|
|
176
|
+
const cached = this.tokenManager.load();
|
|
177
|
+
if (this.tokenManager.isValid(cached)) {
|
|
178
|
+
return cached.accessToken;
|
|
179
|
+
}
|
|
180
|
+
const url = `https://oapi.dingtalk.com/gettoken?appkey=${encodeURIComponent(this.credentials.appKey)}&appsecret=${encodeURIComponent(this.credentials.appSecret)}`;
|
|
181
|
+
const response = await fetch(url, { method: "GET" });
|
|
182
|
+
if (!response.ok) {
|
|
183
|
+
throw new TokenError(`Failed to get access token: HTTP ${response.status}`);
|
|
184
|
+
}
|
|
185
|
+
const data = await response.json();
|
|
186
|
+
if (data.errcode !== 0) {
|
|
187
|
+
throw new TokenError(`Failed to get access token: ${data.errmsg ?? "Unknown error"}`);
|
|
188
|
+
}
|
|
189
|
+
if (!data.access_token) {
|
|
190
|
+
throw new TokenError("Access token missing in response");
|
|
191
|
+
}
|
|
192
|
+
const expiresInSeconds = data.expires_in ?? 7200;
|
|
193
|
+
this.tokenManager.save({
|
|
194
|
+
accessToken: data.access_token,
|
|
195
|
+
expiresAt: Date.now() + (expiresInSeconds - TOKEN_EXPIRY_BUFFER_SECONDS) * 1000
|
|
196
|
+
});
|
|
197
|
+
return data.access_token;
|
|
198
|
+
}
|
|
199
|
+
async sendMarkdown(title, content) {
|
|
200
|
+
try {
|
|
201
|
+
const accessToken = await this.getAccessToken();
|
|
202
|
+
const url = `https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2?access_token=${accessToken}`;
|
|
203
|
+
const body = {
|
|
204
|
+
agent_id: this.credentials.agentId,
|
|
205
|
+
userid_list: this.credentials.userId,
|
|
206
|
+
msg: {
|
|
207
|
+
msgtype: "markdown",
|
|
208
|
+
markdown: {
|
|
209
|
+
title,
|
|
210
|
+
text: content
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
const response = await fetch(url, {
|
|
215
|
+
method: "POST",
|
|
216
|
+
headers: { "Content-Type": "application/json" },
|
|
217
|
+
body: JSON.stringify(body)
|
|
218
|
+
});
|
|
219
|
+
if (!response.ok) {
|
|
220
|
+
return { success: false, error: `HTTP ${response.status}` };
|
|
221
|
+
}
|
|
222
|
+
const data = await response.json();
|
|
223
|
+
if (data.errcode !== 0) {
|
|
224
|
+
return { success: false, error: data.errmsg ?? `Error code: ${data.errcode}` };
|
|
225
|
+
}
|
|
226
|
+
return { success: true, taskId: data.task_id?.toString() };
|
|
227
|
+
} catch (error) {
|
|
228
|
+
return {
|
|
229
|
+
success: false,
|
|
230
|
+
error: error instanceof Error ? error.message : String(error)
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
async sendText(content) {
|
|
235
|
+
try {
|
|
236
|
+
const accessToken = await this.getAccessToken();
|
|
237
|
+
const url = `https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2?access_token=${accessToken}`;
|
|
238
|
+
const body = {
|
|
239
|
+
agent_id: this.credentials.agentId,
|
|
240
|
+
userid_list: this.credentials.userId,
|
|
241
|
+
msg: {
|
|
242
|
+
msgtype: "text",
|
|
243
|
+
text: { content }
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
const response = await fetch(url, {
|
|
247
|
+
method: "POST",
|
|
248
|
+
headers: { "Content-Type": "application/json" },
|
|
249
|
+
body: JSON.stringify(body)
|
|
250
|
+
});
|
|
251
|
+
if (!response.ok) {
|
|
252
|
+
return { success: false, error: `HTTP ${response.status}` };
|
|
253
|
+
}
|
|
254
|
+
const data = await response.json();
|
|
255
|
+
if (data.errcode !== 0) {
|
|
256
|
+
return { success: false, error: data.errmsg ?? `Error code: ${data.errcode}` };
|
|
257
|
+
}
|
|
258
|
+
return { success: true, taskId: data.task_id?.toString() };
|
|
259
|
+
} catch (error) {
|
|
260
|
+
return {
|
|
261
|
+
success: false,
|
|
262
|
+
error: error instanceof Error ? error.message : String(error)
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
async getSendResult(taskId) {
|
|
267
|
+
try {
|
|
268
|
+
const accessToken = await this.getAccessToken();
|
|
269
|
+
const url = `https://oapi.dingtalk.com/topapi/message/corpconversation/getsendresult?access_token=${accessToken}`;
|
|
270
|
+
const body = {
|
|
271
|
+
agent_id: this.credentials.agentId,
|
|
272
|
+
task_id: Number(taskId)
|
|
273
|
+
};
|
|
274
|
+
const response = await fetch(url, {
|
|
275
|
+
method: "POST",
|
|
276
|
+
headers: { "Content-Type": "application/json" },
|
|
277
|
+
body: JSON.stringify(body)
|
|
278
|
+
});
|
|
279
|
+
if (!response.ok) {
|
|
280
|
+
return { success: false, error: `HTTP ${response.status}` };
|
|
281
|
+
}
|
|
282
|
+
const data = await response.json();
|
|
283
|
+
if (data.errcode !== 0) {
|
|
284
|
+
return { success: false, error: data.errmsg ?? `Error code: ${data.errcode}` };
|
|
285
|
+
}
|
|
286
|
+
return {
|
|
287
|
+
success: true,
|
|
288
|
+
invalidUserIdList: data.send_result?.invalid_user_id_list,
|
|
289
|
+
forbiddenUserIdList: data.send_result?.forbidden_user_id_list,
|
|
290
|
+
failedUserIdList: data.send_result?.failed_user_id_list,
|
|
291
|
+
readUserIdList: data.send_result?.read_user_id_list,
|
|
292
|
+
unreadUserIdList: data.send_result?.unread_user_id_list,
|
|
293
|
+
forbiddenList: data.send_result?.forbidden_list
|
|
294
|
+
};
|
|
295
|
+
} catch (error) {
|
|
296
|
+
return {
|
|
297
|
+
success: false,
|
|
298
|
+
error: error instanceof Error ? error.message : String(error)
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
static getForbiddenCodeInfo(code) {
|
|
303
|
+
return getForbiddenCodeInfo(code);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// src/cli.ts
|
|
308
|
+
var __dirname2 = dirname(fileURLToPath(import.meta.url));
|
|
309
|
+
var packageJson = JSON.parse(readFileSync2(join2(__dirname2, "../package.json"), "utf-8"));
|
|
310
|
+
var program = new Command;
|
|
311
|
+
program.name("dingtalk-manager").description("DingTalk work notification CLI - send markdown and text messages").version(packageJson.version).option("--credentials-path <path>", "Path to credentials JSON file");
|
|
312
|
+
program.command("sendMarkdown").description("Send a markdown message to DingTalk").requiredOption("--title <title>", "Message title").requiredOption("--content <content>", "Markdown content").action(async (options, cmd) => {
|
|
313
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
314
|
+
const clientOptions = {};
|
|
315
|
+
if (globalOpts.credentialsPath) {
|
|
316
|
+
clientOptions.credentialsPath = globalOpts.credentialsPath;
|
|
317
|
+
}
|
|
318
|
+
try {
|
|
319
|
+
const client = new DingTalk(clientOptions);
|
|
320
|
+
const result = await client.sendMarkdown(options.title, options.content);
|
|
321
|
+
console.log(JSON.stringify(result, null, 2));
|
|
322
|
+
process.exit(result.success ? 0 : 1);
|
|
323
|
+
} catch (error) {
|
|
324
|
+
const result = {
|
|
325
|
+
success: false,
|
|
326
|
+
error: error instanceof Error ? error.message : String(error)
|
|
327
|
+
};
|
|
328
|
+
console.log(JSON.stringify(result, null, 2));
|
|
329
|
+
process.exit(1);
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
program.command("sendText").description("Send a text message to DingTalk").requiredOption("--content <content>", "Text content").action(async (options, cmd) => {
|
|
333
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
334
|
+
const clientOptions = {};
|
|
335
|
+
if (globalOpts.credentialsPath) {
|
|
336
|
+
clientOptions.credentialsPath = globalOpts.credentialsPath;
|
|
337
|
+
}
|
|
338
|
+
try {
|
|
339
|
+
const client = new DingTalk(clientOptions);
|
|
340
|
+
const result = await client.sendText(options.content);
|
|
341
|
+
console.log(JSON.stringify(result, null, 2));
|
|
342
|
+
process.exit(result.success ? 0 : 1);
|
|
343
|
+
} catch (error) {
|
|
344
|
+
const result = {
|
|
345
|
+
success: false,
|
|
346
|
+
error: error instanceof Error ? error.message : String(error)
|
|
347
|
+
};
|
|
348
|
+
console.log(JSON.stringify(result, null, 2));
|
|
349
|
+
process.exit(1);
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
program.parse();
|
|
353
|
+
|
|
354
|
+
//# debugId=4B8AA822D0E0923864756E2164756E21
|
|
355
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/cli.ts", "../src/utils.ts", "../src/errors.ts", "../src/client.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"#!/usr/bin/env node\nimport { Command } from 'commander';\nimport { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { DingTalk } from './client.js';\nimport type { DingTalkOptions } from './types.js';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8'));\n\nconst program = new Command();\n\nprogram\n .name('dingtalk-manager')\n .description('DingTalk work notification CLI - send markdown and text messages')\n .version(packageJson.version)\n .option('--credentials-path <path>', 'Path to credentials JSON file');\n\nprogram\n .command('sendMarkdown')\n .description('Send a markdown message to DingTalk')\n .requiredOption('--title <title>', 'Message title')\n .requiredOption('--content <content>', 'Markdown content')\n .action(async (options, cmd) => {\n const globalOpts = cmd.optsWithGlobals();\n const clientOptions: DingTalkOptions = {};\n \n if (globalOpts.credentialsPath) {\n clientOptions.credentialsPath = globalOpts.credentialsPath;\n }\n \n try {\n const client = new DingTalk(clientOptions);\n const result = await client.sendMarkdown(options.title, options.content);\n \n console.log(JSON.stringify(result, null, 2));\n process.exit(result.success ? 0 : 1);\n } catch (error) {\n const result = {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n console.log(JSON.stringify(result, null, 2));\n process.exit(1);\n }\n });\n\nprogram\n .command('sendText')\n .description('Send a text message to DingTalk')\n .requiredOption('--content <content>', 'Text content')\n .action(async (options, cmd) => {\n const globalOpts = cmd.optsWithGlobals();\n const clientOptions: DingTalkOptions = {};\n \n if (globalOpts.credentialsPath) {\n clientOptions.credentialsPath = globalOpts.credentialsPath;\n }\n \n try {\n const client = new DingTalk(clientOptions);\n const result = await client.sendText(options.content);\n \n console.log(JSON.stringify(result, null, 2));\n process.exit(result.success ? 0 : 1);\n } catch (error) {\n const result = {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n console.log(JSON.stringify(result, null, 2));\n process.exit(1);\n }\n });\n\nprogram.parse();\n",
|
|
6
|
+
"import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { tmpdir } from 'node:os';\nimport type { DingTalkCredentials, DingTalkOptions, TokenCache } from './types.js';\nimport { CredentialError } from './errors.js';\n\nconst ENV_KEYS = {\n APP_KEY: 'DINGTALK_APP_KEY',\n APP_SECRET: 'DINGTALK_APP_SECRET',\n AGENT_ID: 'DINGTALK_AGENT_ID',\n USER_ID: 'DINGTALK_USER_ID',\n} as const;\n\nexport class TokenManager {\n private cacheDir: string;\n private cacheFile: string;\n\n constructor(cacheDir?: string) {\n this.cacheDir = cacheDir ?? tmpdir();\n this.cacheFile = join(this.cacheDir, 'dingtalk-token-cache.json');\n }\n\n load(): TokenCache | null {\n try {\n if (!existsSync(this.cacheFile)) {\n return null;\n }\n const content = readFileSync(this.cacheFile, 'utf-8');\n return JSON.parse(content) as TokenCache;\n } catch {\n return null;\n }\n }\n\n save(cache: TokenCache): void {\n try {\n if (!existsSync(this.cacheDir)) {\n mkdirSync(this.cacheDir, { recursive: true, mode: 0o700 });\n }\n writeFileSync(this.cacheFile, JSON.stringify(cache, null, 2), { mode: 0o600 });\n } catch (error) {\n console.error('Failed to cache DingTalk token:', error instanceof Error ? error.message : String(error));\n }\n }\n\n isValid(cache: TokenCache | null): cache is TokenCache {\n return cache !== null && cache.expiresAt > Date.now();\n }\n}\n\nexport function loadCredentials(options?: DingTalkOptions): DingTalkCredentials {\n if (options?.credentials) {\n validateCredentials(options.credentials);\n return options.credentials;\n }\n\n if (options?.credentialsPath) {\n const creds = loadFromFile(options.credentialsPath);\n if (creds) {\n validateCredentials(creds);\n return creds;\n }\n }\n\n const envCreds = loadFromEnv();\n if (envCreds) {\n validateCredentials(envCreds);\n return envCreds;\n }\n\n throw new CredentialError(\n 'DingTalk credentials not found. Provide credentials via options.credentials, options.credentialsPath, or environment variables (DINGTALK_APP_KEY, DINGTALK_APP_SECRET, DINGTALK_AGENT_ID, DINGTALK_USER_ID)'\n );\n}\n\nfunction loadFromEnv(): DingTalkCredentials | null {\n const appKey = process.env[ENV_KEYS.APP_KEY];\n const appSecret = process.env[ENV_KEYS.APP_SECRET];\n const agentId = process.env[ENV_KEYS.AGENT_ID];\n const userId = process.env[ENV_KEYS.USER_ID];\n\n if (appKey && appSecret && agentId && userId) {\n return { appKey, appSecret, agentId, userId };\n }\n return null;\n}\n\nfunction loadFromFile(path: string): DingTalkCredentials | null {\n try {\n if (!existsSync(path)) {\n return null;\n }\n const content = readFileSync(path, 'utf-8');\n const data = JSON.parse(content);\n return {\n appKey: data.appKey,\n appSecret: data.appSecret,\n agentId: data.agentId,\n userId: data.userId ?? data.userid,\n };\n } catch {\n return null;\n }\n}\n\nfunction validateCredentials(creds: DingTalkCredentials): void {\n const missing: string[] = [];\n if (!creds.appKey) missing.push('appKey');\n if (!creds.appSecret) missing.push('appSecret');\n if (!creds.agentId) missing.push('agentId');\n if (!creds.userId) missing.push('userId');\n\n if (missing.length > 0) {\n throw new CredentialError(`Missing required credentials: ${missing.join(', ')}`);\n }\n}\n\nexport const FORBIDDEN_CODES: Record<string, { description: string; solution: string }> = {\n '143103': {\n description: '企业应用消息发送QPM(每分钟请求数)超限',\n solution: '请稍后重试,或优化发送频率',\n },\n '143104': {\n description: '企业每分钟发送QPM超限',\n solution: '请稍后重试,或优化发送频率',\n },\n '143105': {\n description: '应用发送日限超限(自建应用: 500条/天/用户, ISV应用: 50条/天/用户)',\n solution: '已达到每日发送上限,请明天再试',\n },\n '143106': {\n description: '应用重复消息发送限超限(同一用户每天只能接收一条相同内容的消息)',\n solution: '修改消息内容后重新发送,或等待明天自动重置',\n },\n};\n\nexport function getForbiddenCodeInfo(code: string): { description: string; solution: string } | null {\n return FORBIDDEN_CODES[code] ?? null;\n}\n",
|
|
7
|
+
"export class DingTalkError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'DingTalkError';\n }\n}\n\nexport class TokenError extends DingTalkError {\n constructor(message: string) {\n super(message);\n this.name = 'TokenError';\n }\n}\n\nexport class MessageError extends DingTalkError {\n constructor(message: string) {\n super(message);\n this.name = 'MessageError';\n }\n}\n\nexport class CredentialError extends DingTalkError {\n constructor(message: string) {\n super(message);\n this.name = 'CredentialError';\n }\n}\n",
|
|
8
|
+
"import type {\n DingTalkOptions,\n DingTalkCredentials,\n SendMessageResult,\n SendResult,\n TokenResponse,\n SendResponse,\n SendResultResponse,\n} from './types.js';\nimport { TokenManager, loadCredentials, getForbiddenCodeInfo } from './utils.js';\nimport { TokenError, MessageError } from './errors.js';\n\nconst TOKEN_EXPIRY_BUFFER_SECONDS = 300;\n\nexport class DingTalk {\n private credentials: DingTalkCredentials;\n private tokenManager: TokenManager;\n\n constructor(options?: DingTalkOptions) {\n this.credentials = loadCredentials(options);\n this.tokenManager = new TokenManager(options?.tokenCacheDir);\n }\n\n async getAccessToken(): Promise<string> {\n const cached = this.tokenManager.load();\n if (this.tokenManager.isValid(cached)) {\n return cached.accessToken;\n }\n\n const url = `https://oapi.dingtalk.com/gettoken?appkey=${encodeURIComponent(this.credentials.appKey)}&appsecret=${encodeURIComponent(this.credentials.appSecret)}`;\n\n const response = await fetch(url, { method: 'GET' });\n\n if (!response.ok) {\n throw new TokenError(`Failed to get access token: HTTP ${response.status}`);\n }\n\n const data = (await response.json()) as TokenResponse;\n\n if (data.errcode !== 0) {\n throw new TokenError(`Failed to get access token: ${data.errmsg ?? 'Unknown error'}`);\n }\n\n if (!data.access_token) {\n throw new TokenError('Access token missing in response');\n }\n\n const expiresInSeconds = data.expires_in ?? 7200;\n this.tokenManager.save({\n accessToken: data.access_token,\n expiresAt: Date.now() + (expiresInSeconds - TOKEN_EXPIRY_BUFFER_SECONDS) * 1000,\n });\n\n return data.access_token;\n }\n\n async sendMarkdown(title: string, content: string): Promise<SendMessageResult> {\n try {\n const accessToken = await this.getAccessToken();\n const url = `https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2?access_token=${accessToken}`;\n\n const body = {\n agent_id: this.credentials.agentId,\n userid_list: this.credentials.userId,\n msg: {\n msgtype: 'markdown',\n markdown: {\n title,\n text: content,\n },\n },\n };\n\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n return { success: false, error: `HTTP ${response.status}` };\n }\n\n const data = (await response.json()) as SendResponse;\n\n if (data.errcode !== 0) {\n return { success: false, error: data.errmsg ?? `Error code: ${data.errcode}` };\n }\n\n return { success: true, taskId: data.task_id?.toString() };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n }\n\n async sendText(content: string): Promise<SendMessageResult> {\n try {\n const accessToken = await this.getAccessToken();\n const url = `https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2?access_token=${accessToken}`;\n\n const body = {\n agent_id: this.credentials.agentId,\n userid_list: this.credentials.userId,\n msg: {\n msgtype: 'text',\n text: { content },\n },\n };\n\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n return { success: false, error: `HTTP ${response.status}` };\n }\n\n const data = (await response.json()) as SendResponse;\n\n if (data.errcode !== 0) {\n return { success: false, error: data.errmsg ?? `Error code: ${data.errcode}` };\n }\n\n return { success: true, taskId: data.task_id?.toString() };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n }\n\n async getSendResult(taskId: string): Promise<SendResult> {\n try {\n const accessToken = await this.getAccessToken();\n const url = `https://oapi.dingtalk.com/topapi/message/corpconversation/getsendresult?access_token=${accessToken}`;\n\n const body = {\n agent_id: this.credentials.agentId,\n task_id: Number(taskId),\n };\n\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n return { success: false, error: `HTTP ${response.status}` };\n }\n\n const data = (await response.json()) as SendResultResponse;\n\n if (data.errcode !== 0) {\n return { success: false, error: data.errmsg ?? `Error code: ${data.errcode}` };\n }\n\n return {\n success: true,\n invalidUserIdList: data.send_result?.invalid_user_id_list,\n forbiddenUserIdList: data.send_result?.forbidden_user_id_list,\n failedUserIdList: data.send_result?.failed_user_id_list,\n readUserIdList: data.send_result?.read_user_id_list,\n unreadUserIdList: data.send_result?.unread_user_id_list,\n forbiddenList: data.send_result?.forbidden_list,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n }\n\n static getForbiddenCodeInfo(code: string): { description: string; solution: string } | null {\n return getForbiddenCodeInfo(code);\n }\n}\n"
|
|
9
|
+
],
|
|
10
|
+
"mappings": "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",
|
|
11
|
+
"debugId": "4B8AA822D0E0923864756E2164756E21",
|
|
12
|
+
"names": []
|
|
13
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dingtalk-manager",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "DingTalk work notification API client for Node.js/Bun - send markdown and text messages",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
"types": "./dist/index.d.ts"
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
|
+
"bin": {
|
|
16
|
+
"dingtalk-manager": "./dist/cli.js"
|
|
17
|
+
},
|
|
15
18
|
"files": [
|
|
16
19
|
"dist",
|
|
17
20
|
"README.md",
|
|
@@ -20,7 +23,7 @@
|
|
|
20
23
|
"scripts": {
|
|
21
24
|
"build": "bun run build:clean && bun run build:js && bun run build:types",
|
|
22
25
|
"build:clean": "rm -rf dist",
|
|
23
|
-
"build:js": "bun build ./src/index.ts --outdir ./dist --target node --format esm --sourcemap --external commander",
|
|
26
|
+
"build:js": "bun build ./src/index.ts ./src/cli.ts --outdir ./dist --target node --format esm --sourcemap --external commander",
|
|
24
27
|
"build:types": "tsc --emitDeclarationOnly --declaration --outDir dist",
|
|
25
28
|
"test": "bun test",
|
|
26
29
|
"test:watch": "bun test --watch",
|
|
@@ -49,6 +52,9 @@
|
|
|
49
52
|
"engines": {
|
|
50
53
|
"node": ">=18.0.0"
|
|
51
54
|
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"commander": "^12.0.0"
|
|
57
|
+
},
|
|
52
58
|
"devDependencies": {
|
|
53
59
|
"@types/bun": "latest",
|
|
54
60
|
"typescript": "^5.3.0"
|