lansenger-sdk-ts 1.0.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/LICENSE +21 -0
- package/README.fr.md +501 -0
- package/README.md +504 -0
- package/README.zhHans.md +501 -0
- package/README.zhHant.md +501 -0
- package/README.zhHantHK.md +501 -0
- package/dist/accountMessages.d.ts +12 -0
- package/dist/accountMessages.js +41 -0
- package/dist/auth.d.ts +13 -0
- package/dist/auth.js +70 -0
- package/dist/calendars.d.ts +84 -0
- package/dist/calendars.js +278 -0
- package/dist/callbacks.d.ts +384 -0
- package/dist/callbacks.js +712 -0
- package/dist/chats.d.ts +22 -0
- package/dist/chats.js +88 -0
- package/dist/client.d.ts +439 -0
- package/dist/client.js +712 -0
- package/dist/config.d.ts +14 -0
- package/dist/config.js +42 -0
- package/dist/constants.d.ts +30 -0
- package/dist/constants.js +187 -0
- package/dist/contacts.d.ts +38 -0
- package/dist/contacts.js +161 -0
- package/dist/departments.d.ts +18 -0
- package/dist/departments.js +69 -0
- package/dist/exceptions.d.ts +20 -0
- package/dist/exceptions.js +42 -0
- package/dist/groupMessages.d.ts +11 -0
- package/dist/groupMessages.js +39 -0
- package/dist/groups.d.ts +66 -0
- package/dist/groups.js +218 -0
- package/dist/http.d.ts +7 -0
- package/dist/http.js +67 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +189 -0
- package/dist/media.d.ts +16 -0
- package/dist/media.js +178 -0
- package/dist/models.d.ts +925 -0
- package/dist/models.js +991 -0
- package/dist/oauth.d.ts +17 -0
- package/dist/oauth.js +107 -0
- package/dist/persistence.d.ts +26 -0
- package/dist/persistence.js +210 -0
- package/dist/reminders.d.ts +10 -0
- package/dist/reminders.js +31 -0
- package/dist/streaming.d.ts +9 -0
- package/dist/streaming.js +40 -0
- package/dist/todos.d.ts +75 -0
- package/dist/todos.js +282 -0
- package/dist/urlHelpers.d.ts +7 -0
- package/dist/urlHelpers.js +22 -0
- package/dist/userMessages.d.ts +8 -0
- package/dist/userMessages.js +34 -0
- package/dist/users.d.ts +6 -0
- package/dist/users.js +32 -0
- package/package.json +33 -0
package/README.zhHant.md
ADDED
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
[English](README.md) | [简体中文](README.zhHans.md) | [繁体中文](README.zhHant.md) | [繁体中文香港](README.zhHantHK.md) | [Français](README.fr.md)
|
|
2
|
+
|
|
3
|
+
# lansenger-sdk-ts
|
|
4
|
+
|
|
5
|
+
藍信(Lansenger)平臺的 TypeScript SDK — 支援 藍信應用、組織機器人 及 個人機器人。
|
|
6
|
+
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
[](https://www.typescriptlang.org/)
|
|
9
|
+
[](https://nodejs.org/)
|
|
10
|
+
|
|
11
|
+
> 零框架依賴——僅依賴 `node-fetch` (v2,CommonJS 相容)。可適配任何 Node.js 專案。
|
|
12
|
+
|
|
13
|
+
## 支援的機器人類型
|
|
14
|
+
|
|
15
|
+
| 機器人類型 | 認證 | WebSocket 入站 | 所有 API |
|
|
16
|
+
|------------|------|-----------------|----------|
|
|
17
|
+
| **藍信應用** | appToken + userToken | ✗(使用 webhook) | ✓ |
|
|
18
|
+
| **組織機器人** | appToken + userToken | ✗(使用 webhook) | ✓ |
|
|
19
|
+
| **個人機器人** | appToken | ✓(WebSocket) | ✓(非機器人 API 有部分限制) |
|
|
20
|
+
|
|
21
|
+
三種機器人類型使用相同的認證機制:每次 API 呼叫都需要 `appToken`;`userToken` 僅在特定使用者級操作時需要(使用者資訊、員工搜尋、日曆等)。
|
|
22
|
+
|
|
23
|
+
## 功能特色
|
|
24
|
+
|
|
25
|
+
- **異步客戶端** — `LansengerClient` 提供 Promise 化 API
|
|
26
|
+
- **憑證與令牌持久化** — `CredentialStore` 將 app_id、app_secret、URL、appToken、userToken 保存至檔案(重啟不丟失)
|
|
27
|
+
- **OAuth2 使用者認證** — 構建授權 URL、換取 userToken、刷新令牌
|
|
28
|
+
- **組織與部門** — 組織資訊、部門詳情/子部門/員工
|
|
29
|
+
- **員工與通訊錄** — 基礎/詳細資訊、ID 映射、部門祖先鏈、搜尋
|
|
30
|
+
- **訊息傳遞** — 3 種私聊通道(機器人、公眾號、人→人)+ 群聊,支援所有訊息類型,含 @提及和真人/機器人發送身分,加急提醒
|
|
31
|
+
- **富卡片** — appCard(支援動態狀態更新)、oacard、linkCard、appArticles
|
|
32
|
+
- **流式訊息** — SSE 即時投遞,專為 AI Agent 設計
|
|
33
|
+
- **媒體上傳/下載** — 檔案、圖片、影片,自動偵測類型,媒體路徑取得
|
|
34
|
+
- **訊息管理** — 撤回、動態卡片更新
|
|
35
|
+
- **群組** — 建立、查詢資訊/成員/列表、檢查成員、更新設定與成員、解散
|
|
36
|
+
- **日曆日程** — 主日曆、日程 CRUD + 更新、參會人管理 + 參會人元資料
|
|
37
|
+
- **統一待辦** — 建立、更新、刪除、查詢、執行人管理、狀態統計
|
|
38
|
+
- **回調事件** — 25 種事件類型、結構化解析、AES 解密(4.10.1.4)、SHA1 簽名驗證
|
|
39
|
+
- **聊天讀取** — 取得聊天列表、取得聊天訊息(4.24 MCP)
|
|
40
|
+
|
|
41
|
+
## 快速安裝
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm install lansenger-sdk-ts
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
開發模式:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
git clone https://github.com/lansenger-pm/lansenger-sdk-ts.git
|
|
51
|
+
cd lansenger-sdk-ts
|
|
52
|
+
npm install
|
|
53
|
+
npm run build
|
|
54
|
+
npm test
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## 1. 認證
|
|
58
|
+
|
|
59
|
+
### appToken — 所有 API 呼叫均需
|
|
60
|
+
|
|
61
|
+
每個 SDK 方法都需要 `appToken`。客戶端使用 `app_id` + `app_secret` 自動取得並刷新 appToken,透過 `GET /v1/apptoken/create` 端點。你無需手動管理 appToken — `TokenManager` 負責整個生命週期:
|
|
62
|
+
|
|
63
|
+
1. **首次呼叫** → 使用 app_id + app_secret 請求 `GET /v1/apptoken/create` → 回傳 `appToken`(有效期 2 小時)
|
|
64
|
+
2. **後續呼叫** → 重用緩衝的 appToken 直到過期
|
|
65
|
+
3. **令牌過期** → 自動透過同一端點刷新
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
import { LansengerClient } from "lansenger-sdk-ts";
|
|
69
|
+
|
|
70
|
+
const client = new LansengerClient("你的-appid", "你的-secret");
|
|
71
|
+
|
|
72
|
+
// 也可以手動取得/失效令牌
|
|
73
|
+
const token = await client.getToken();
|
|
74
|
+
client.invalidateToken(); // 強制下次呼叫時刷新
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### userToken — 僅在特定端點需要
|
|
78
|
+
|
|
79
|
+
`userToken` 代表特定藍信使用者的授權(透過 OAuth2 取得)。僅在以下場景需要:
|
|
80
|
+
- 使用者級資訊(fetchUserInfo、fetchStaffDetail、searchStaff)
|
|
81
|
+
- 日曆與日程操作(fetchPrimaryCalendar、createSchedule 等)
|
|
82
|
+
- 作為真人發送者的群組操作
|
|
83
|
+
|
|
84
|
+
### 取得憑證
|
|
85
|
+
|
|
86
|
+
| 機器人類型 | 如何取得 app_id + app_secret |
|
|
87
|
+
|------------|-------------------------------|
|
|
88
|
+
| **個人機器人** | 藍信桌面端 → 通訊錄 → 智能機器人 → 個人機器人 → 點擊右側 ℹ️ 圖標(行動端不支援查看憑證) |
|
|
89
|
+
| **藍信應用** | 在藍信開發者中心建立,可能需要向組織管理員申請 |
|
|
90
|
+
| **組織機器人** | 在藍信開發者中心建立,可能需要向組織管理員申請 |
|
|
91
|
+
|
|
92
|
+
### OAuth2 使用者級認證
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
// 構建授權 URL——將使用者重定向到藍信通行證頁面
|
|
96
|
+
const url = client.buildAuthorizeUrl("https://myapp.com/callback");
|
|
97
|
+
|
|
98
|
+
// 使用者授權後,用 code 換取 userToken + refreshToken
|
|
99
|
+
const tokenResult = await client.exchangeCode("回調中的授權碼");
|
|
100
|
+
|
|
101
|
+
// 刷新過期的 userToken
|
|
102
|
+
const newToken = await client.refreshUserToken(tokenResult.refresh_token!);
|
|
103
|
+
|
|
104
|
+
// 取得使用者資料
|
|
105
|
+
const userInfo = await client.fetchUserInfo(tokenResult.user_token!);
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### 工廠方法
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
// 從環境變數(LANSENGER_APP_ID、LANSENGER_APP_SECRET 等)
|
|
112
|
+
const client = LansengerClient.fromEnv();
|
|
113
|
+
|
|
114
|
+
// 從 LansengerConfig 物件
|
|
115
|
+
const config = new LansengerConfig("appid", "secret", "https://open.e.lanxin.cn/open/apigw");
|
|
116
|
+
const client = LansengerClient.fromConfig(config);
|
|
117
|
+
|
|
118
|
+
// 從 CredentialStore(讀取持久化憑證)
|
|
119
|
+
const client = LansengerClient.fromStore();
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## 2. 組織與部門
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
// 組織資訊
|
|
126
|
+
const org = await client.fetchOrgInfo("orgId");
|
|
127
|
+
|
|
128
|
+
// 部門層級
|
|
129
|
+
const detail = await client.fetchDepartmentDetail("deptId");
|
|
130
|
+
const children = await client.fetchDepartmentChildren("deptId");
|
|
131
|
+
const staffs = await client.fetchDepartmentStaffs("deptId");
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## 3. 員工與通訊錄
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
// 基本員工資訊
|
|
138
|
+
const staff = await client.fetchStaffBasicInfo("staffOpenId");
|
|
139
|
+
|
|
140
|
+
// 詳細資料(建議使用 userToken)
|
|
141
|
+
const detail = await client.fetchStaffDetail("staffOpenId", { user_token: "ut" });
|
|
142
|
+
|
|
143
|
+
// 手機號 → staffId 映射
|
|
144
|
+
const mapping = await client.fetchStaffIdMapping("orgId", "mobile", "13800138000");
|
|
145
|
+
|
|
146
|
+
// 員工的部門祖先鏈
|
|
147
|
+
const ancestors = await client.fetchDepartmentAncestors("staffOpenId");
|
|
148
|
+
|
|
149
|
+
// 搜尋員工(需要 userToken)
|
|
150
|
+
const results = await client.searchStaff("張三", { user_token: "ut" });
|
|
151
|
+
|
|
152
|
+
// 組織擴展欄位 ID
|
|
153
|
+
const fields = await client.fetchOrgExtraFieldIds("orgId");
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## 4. 訊息與媒體
|
|
157
|
+
|
|
158
|
+
#### 機器人私聊——最常用
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
const result = await client.sendText("staff123", "你好!");
|
|
162
|
+
const result = await client.sendMarkdown("staff123", "**加粗**");
|
|
163
|
+
const result = await client.sendFile("staff123", "/path/to/report.pdf");
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
#### 公眾號通道
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
const result = await client.sendAccountMessage(
|
|
170
|
+
"text", { text: { content: "系統通知" } },
|
|
171
|
+
["staff1", "staff2"], undefined,
|
|
172
|
+
{ account_id: "524288-xxxx" },
|
|
173
|
+
);
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
#### 人→人代發通道(需要 userToken)
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
const result = await client.sendUserMessage(
|
|
180
|
+
"staff456", "text", { text: { content: "你好" } },
|
|
181
|
+
{ user_token: "ut" },
|
|
182
|
+
);
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
#### 群聊
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
// 機器人 → 群組
|
|
189
|
+
const result = await client.sendText("group123", "通知", { is_group: true });
|
|
190
|
+
|
|
191
|
+
// 真人 → 群組(需要 userToken)
|
|
192
|
+
const result = await client.sendGroupMessage(
|
|
193
|
+
"group123", "text", { text: { content: "我來處理" } },
|
|
194
|
+
{ user_token: "ut" },
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
// 群聊 @提及
|
|
198
|
+
const result = await client.sendText("group123", "重要!", { is_group: true, reminder_all: true });
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
#### 富卡片
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
const result = await client.sendAppCard("staff123", "審批", { is_dynamic: true });
|
|
205
|
+
const result = await client.sendLinkCard("staff123", "文章", "https://...");
|
|
206
|
+
const result = await client.sendAppArticles("staff123", [{ title: "文章1", link: "..." }]);
|
|
207
|
+
|
|
208
|
+
// 更新動態卡片狀態
|
|
209
|
+
const result = await client.updateDynamicCard("msg123", { is_last_update: true });
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
#### 流式訊息(用於 AI Agent)
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
const result = await client.createStreamMessage("staff1", "staff", "stream-id-1");
|
|
216
|
+
const result = await client.fetchStreamMessage("msg123");
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
#### 媒體
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
// 上傳
|
|
223
|
+
const upload = await client.uploadMediaFile("/path/to/file.pdf");
|
|
224
|
+
|
|
225
|
+
// 下載
|
|
226
|
+
const download = await client.downloadMediaFile("media123");
|
|
227
|
+
|
|
228
|
+
// 保存至檔案
|
|
229
|
+
const filePath = await client.downloadMediaToFile("media123", { target_path: "/tmp/file.pdf" });
|
|
230
|
+
|
|
231
|
+
// 取得下載 URL 路徑 (4.5.3)
|
|
232
|
+
const pathResult = await client.fetchMediaPathInfo("media123");
|
|
233
|
+
|
|
234
|
+
// 撤回訊息
|
|
235
|
+
const result = await client.revokeMessage(["msg1", "msg2"]);
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
#### 加急提醒 (4.6.14)
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
import { REMINDER_TYPE_POPUP, REMINDER_TYPE_SMS } from "lansenger-sdk-ts";
|
|
242
|
+
|
|
243
|
+
const result = await client.sendReminderMsg(
|
|
244
|
+
"msg123",
|
|
245
|
+
[REMINDER_TYPE_POPUP, REMINDER_TYPE_SMS],
|
|
246
|
+
["staff1", "staff2"],
|
|
247
|
+
);
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## 5. 群組
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
// 建立群組
|
|
254
|
+
const group = await client.createGroup("專案討論", "orgId", { staff_id_list: ["s1", "s2", "s3"] });
|
|
255
|
+
|
|
256
|
+
// 取得資訊與成員
|
|
257
|
+
const info = await client.fetchGroupInfo("groupOpenId");
|
|
258
|
+
const members = await client.fetchGroupMembers("groupOpenId");
|
|
259
|
+
const groups = await client.fetchGroupList();
|
|
260
|
+
|
|
261
|
+
// 成員檢查
|
|
262
|
+
const result = await client.checkIsInGroup("groupOpenId", { staff_id: "staff1" });
|
|
263
|
+
|
|
264
|
+
// 更新設定
|
|
265
|
+
await client.updateGroupInfo("groupId", { name: "新名稱", manage_mode: 1 });
|
|
266
|
+
|
|
267
|
+
// 新增/移除成員
|
|
268
|
+
await client.updateGroupMembers("groupId", { add_user_list: ["staff4"], del_user_list: ["staff3"] });
|
|
269
|
+
|
|
270
|
+
// 解散群組(僅群主,4.28.6)
|
|
271
|
+
await client.dismissGroup("groupId");
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## 6. 日曆日程
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
// 取得主日曆(需要 userToken 或 userId)
|
|
278
|
+
const cal = await client.fetchPrimaryCalendar({ user_token: "ut" });
|
|
279
|
+
|
|
280
|
+
// 建立日程
|
|
281
|
+
const schedule = await client.createSchedule(
|
|
282
|
+
cal.calendar_id!, "團隊會議",
|
|
283
|
+
{ date: "2024-01-15", time: "10:00", timeZone: "Asia/Shanghai" },
|
|
284
|
+
{ date: "2024-01-15", time: "11:00", timeZone: "Asia/Shanghai" },
|
|
285
|
+
[{ staffId: "staff1", attendeeFlag: "required" }],
|
|
286
|
+
{ user_token: "ut" },
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
// 取得/刪除日程
|
|
290
|
+
const info = await client.fetchSchedule("cal1", "sch1", { user_token: "ut" });
|
|
291
|
+
await client.deleteSchedule("cal1", "sch1", { user_token: "ut" });
|
|
292
|
+
|
|
293
|
+
// 時間範圍內的日程列表(最多 42 天)
|
|
294
|
+
const schedules = await client.fetchScheduleList("cal1", 1705276800000, 1707940800000, { user_token: "ut" });
|
|
295
|
+
|
|
296
|
+
// 參會人管理
|
|
297
|
+
const attendees = await client.fetchScheduleAttendees("cal1", "sch1", { user_token: "ut" });
|
|
298
|
+
await client.addScheduleAttendees("cal1", "sch1", ["staff2"], { user_token: "ut" });
|
|
299
|
+
await client.deleteScheduleAttendees("cal1", "sch1", ["staff2"], { user_token: "ut" });
|
|
300
|
+
|
|
301
|
+
// 更新日程 (4.23.12)
|
|
302
|
+
await client.updateSchedule("cal1", "sch1", { summary: "更新後的會議", user_token: "ut" });
|
|
303
|
+
|
|
304
|
+
// 更新參會人元資料 (4.23.17) — RSVP、顏色、忙/閒狀態、提醒
|
|
305
|
+
await client.updateScheduleAttendeeMeta("cal1", "sch1", {
|
|
306
|
+
rsvp_status: "accept", busy_free_state: "busy", remind_times: [5, 15], user_token: "ut",
|
|
307
|
+
});
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
## 7. 統一待辦
|
|
311
|
+
|
|
312
|
+
```typescript
|
|
313
|
+
import { TODO_TYPE_APPROVAL, TODO_TODO_STATUS_DONE } from "lansenger-sdk-ts";
|
|
314
|
+
|
|
315
|
+
// 建立待辦任務
|
|
316
|
+
const todo = await client.createTodoTask(
|
|
317
|
+
"審批請求", "https://app.com/a/1", "https://pc.app.com/a/1",
|
|
318
|
+
["staff1"], "org1", TODO_TYPE_APPROVAL,
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
// 更新狀態(11=待閱, 12=已閱, 21=待辦, 22=已辦)
|
|
322
|
+
await client.updateTodoTaskStatus("taskId", TODO_TODO_STATUS_DONE, "org1");
|
|
323
|
+
|
|
324
|
+
// 更新內容
|
|
325
|
+
await client.updateTodoTask("taskId", "已更新", "l", "p", "org1");
|
|
326
|
+
|
|
327
|
+
// 刪除(僅限發送者)
|
|
328
|
+
await client.deleteTodoTask("taskId", "org1");
|
|
329
|
+
|
|
330
|
+
// 查詢
|
|
331
|
+
const listResult = await client.fetchTodoTaskList("org1");
|
|
332
|
+
const task = await client.fetchTodoTaskById("taskId", "org1");
|
|
333
|
+
const task = await client.fetchTodoTaskBySourceId("src1", "org1");
|
|
334
|
+
const counts = await client.fetchTodoTaskStatusCounts("staff1", "org1");
|
|
335
|
+
|
|
336
|
+
// 執行人管理
|
|
337
|
+
await client.addExecutors(["staff2"], "org1", { todotask_id: "taskId" });
|
|
338
|
+
await client.deleteExecutors(["staff2"], "org1", { todotask_id: "taskId" });
|
|
339
|
+
const executors = await client.fetchExecutorList("taskId", "org1");
|
|
340
|
+
await client.updateExecutorStatus(
|
|
341
|
+
[{ executorId: "staff1", todotaskId: "taskId", status: "22" }],
|
|
342
|
+
"org1",
|
|
343
|
+
);
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
## 8. 聊天讀取 (4.24 MCP)
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
// 取得聊天列表(私聊 + 群聊)
|
|
350
|
+
const chatList = await client.fetchChatList({ user_token: "ut" });
|
|
351
|
+
|
|
352
|
+
// 取得聊天訊息
|
|
353
|
+
const messages = await client.fetchChatMessages({
|
|
354
|
+
staff_id: "staff1", // 或 group_id: "group1"
|
|
355
|
+
user_token: "ut",
|
|
356
|
+
});
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
## 9. 回調事件
|
|
360
|
+
|
|
361
|
+
SDK 同時支援純 JSON 和 AES 加密回調載荷(藍信 API 規範 4.10.1.4)。
|
|
362
|
+
|
|
363
|
+
### 配置
|
|
364
|
+
|
|
365
|
+
設定 `encoding_key` 和 `callback_token`(從藍信開發者中心回調設定取得):
|
|
366
|
+
|
|
367
|
+
```typescript
|
|
368
|
+
const client = new LansengerClient("appid", "secret", undefined, undefined, undefined, undefined, "BASE64_AES_KEY", "CALLBACK_TOKEN");
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
也可透過環境變數:`LANSENGER_ENCODING_KEY`、`LANSENGER_CALLBACK_TOKEN`。
|
|
372
|
+
|
|
373
|
+
### 解析回調載荷(自動辨識加密/明文)
|
|
374
|
+
|
|
375
|
+
```typescript
|
|
376
|
+
import { parseCallbackPayload } from "lansenger-sdk-ts";
|
|
377
|
+
|
|
378
|
+
// 純 JSON webhook
|
|
379
|
+
const events = parseCallbackPayload('{"events": [...]}');
|
|
380
|
+
|
|
381
|
+
// AES 加密載荷(使用 encodingKey 自動解密)
|
|
382
|
+
const events = parseCallbackPayload(encryptedData, {
|
|
383
|
+
encoding_key: "BASE64_AES_KEY",
|
|
384
|
+
known_app_id: "your-appid",
|
|
385
|
+
});
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### 驗證簽名
|
|
389
|
+
|
|
390
|
+
```typescript
|
|
391
|
+
import { verifyCallbackSignature } from "lansenger-sdk-ts";
|
|
392
|
+
|
|
393
|
+
// sha1(sort(token, timestamp, nonce, dataEncrypt))
|
|
394
|
+
const isValid = verifyCallbackSignature(
|
|
395
|
+
timestamp, nonce, signature, encodingKey,
|
|
396
|
+
{ data_encrypt: encryptedData, callback_token: "CALLBACK_TOKEN" },
|
|
397
|
+
);
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
### 事件類型
|
|
401
|
+
|
|
402
|
+
```typescript
|
|
403
|
+
const types = LansengerClient.getCallbackEventTypes(); // 25 種事件類型,13 個類別
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
## 訊息類型能力矩陣
|
|
407
|
+
|
|
408
|
+
| msgType | Markdown | @提及 | 附件 | 私聊通道 | 群聊 | 備註 |
|
|
409
|
+
|---------|----------|-------|------|----------|------|------|
|
|
410
|
+
| `text` | ✗ | ✓ (群) | ✓ | 機器人、公眾號、人→人 | ✓ | 上限 6000 字節 |
|
|
411
|
+
| `formatText` | ✓ | ✗ | ✗ | 僅人→人 | ✓ | formatType=1 支援 Markdown |
|
|
412
|
+
| `oacard` | ✗ | ✗ | ✗ | 機器人、公眾號、人→人 | ✓ | 簡單卡片含欄位 |
|
|
413
|
+
| `appCard` | ✓ (div 標籤) | ✗ | ✗ | 機器人、公眾號、人→人 | ✓ | 富卡片,支援動態更新 |
|
|
414
|
+
| `linkCard` | ✗ | ✗ | ✗ | 機器人、公眾號 | ✓ | 連結預覽卡片 |
|
|
415
|
+
| `appArticles` | ✗ | ✗ | ✗ | 僅機器人私聊 | ✓ | 文章列表(1+ 篇) |
|
|
416
|
+
|
|
417
|
+
**群聊**支援所有訊息類型。只有群聊支援 @提及。
|
|
418
|
+
|
|
419
|
+
## 配置
|
|
420
|
+
|
|
421
|
+
### 環境變數
|
|
422
|
+
|
|
423
|
+
| 變數 | 必填 | 說明 | 預設值 |
|
|
424
|
+
|------|------|------|--------|
|
|
425
|
+
| `LANSENGER_APP_ID` | ✓ | 應用/機器人 ID | — |
|
|
426
|
+
| `LANSENGER_APP_SECRET` | ✓ | 應用/機器人密鑰 | — |
|
|
427
|
+
| `LANSENGER_API_GATEWAY_URL` | ✗ | API 网關 URL | `https://open.e.lanxin.cn/open/apigw` |
|
|
428
|
+
| `LANSENGER_PASSPORT_URL` | ✗ | 通行證 URL(OAuth2) | — |
|
|
429
|
+
| `LANSENGER_ENCODING_KEY` | ✗ | 回調 AES 加密密鑰 (Base64) | — |
|
|
430
|
+
| `LANSENGER_CALLBACK_TOKEN` | ✗ | 回調簽名令牌 | — |
|
|
431
|
+
|
|
432
|
+
### 憑證與令牌持久化
|
|
433
|
+
|
|
434
|
+
預設情況下,憑證和令牌僅保留在記憶體中(程式退出即消失)。啟用檔案持久化使用 `storePath`:
|
|
435
|
+
|
|
436
|
+
```typescript
|
|
437
|
+
import { LansengerClient, CredentialStore } from "lansenger-sdk-ts";
|
|
438
|
+
|
|
439
|
+
// 自動持久化至 ~/.lansenger/sdk_state.json
|
|
440
|
+
const client = new LansengerClient("appid", "secret", undefined, undefined, undefined, "~/.lansenger/sdk_state.json", "BASE64_AES_KEY", "CALLBACK_TOKEN");
|
|
441
|
+
|
|
442
|
+
// 或從環境變數加持久化
|
|
443
|
+
const client = LansengerClient.fromEnv("~/.lansenger/sdk_state.json");
|
|
444
|
+
|
|
445
|
+
// 手動 Store 操作
|
|
446
|
+
const store = new CredentialStore("~/.lansenger/sdk_state.json");
|
|
447
|
+
store.saveCredentials("app_id", "app_secret", "https://open.e.lanxin.cn/open/apigw");
|
|
448
|
+
store.saveUserToken("user_token", "refresh_token");
|
|
449
|
+
const token = store.loadAppToken(); // 過期則為 null
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
## 專案結構
|
|
453
|
+
|
|
454
|
+
```
|
|
455
|
+
lansenger-sdk-ts/
|
|
456
|
+
├── src/
|
|
457
|
+
│ ├── index.ts # 所有导出
|
|
458
|
+
│ ├── client.ts # LansengerClient(異步)
|
|
459
|
+
│ ├── config.ts # LansengerConfig
|
|
460
|
+
│ ├── auth.ts # TokenManager — appToken 生命週期
|
|
461
|
+
│ ├── oauth.ts # OAuth2 輔助
|
|
462
|
+
│ ├── constants.ts # API 端點、媒體類型、OAuth scope
|
|
463
|
+
│ ├── exceptions.ts # LansengerError 層級
|
|
464
|
+
│ ├── models.ts # 38+ 結果類型
|
|
465
|
+
│ ├── http.ts # doGet、doPost、doPostMultipart、parseApiResponse
|
|
466
|
+
│ ├── urlHelpers.ts # buildApiUrl (支援 pathVars)
|
|
467
|
+
│ ├── contacts.ts # 員工與組織資訊 API
|
|
468
|
+
│ ├── departments.ts # 部門 API
|
|
469
|
+
│ ├── accountMessages.ts # 公眾號通道
|
|
470
|
+
│ ├── userMessages.ts # 人→人通道
|
|
471
|
+
│ ├── groupMessages.ts # 群聊通道
|
|
472
|
+
│ ├── media.ts # 上傳/下載
|
|
473
|
+
│ ├── streaming.ts # SSE 流式
|
|
474
|
+
│ ├── persistence.ts # CredentialStore — 檔案持久化
|
|
475
|
+
│ ├── callbacks.ts # 回調事件 — 25 種事件、AES 解密、SHA1 簽名驗證
|
|
476
|
+
│ ├── groups.ts # 群組 API(含解散 4.28.6)
|
|
477
|
+
│ ├── todos.ts # 統一待辦
|
|
478
|
+
│ ├── calendars.ts # 日曆與日程
|
|
479
|
+
│ ├── reminders.ts # 加急提醒 (4.6.14)
|
|
480
|
+
│ ├── chats.ts # 聊天讀取 (4.24 MCP)
|
|
481
|
+
│ └── users.ts # 使用者資訊
|
|
482
|
+
├── tests/ # 單元測試
|
|
483
|
+
├── package.json
|
|
484
|
+
├── tsconfig.json
|
|
485
|
+
├── jest.config.js
|
|
486
|
+
├── LICENSE
|
|
487
|
+
└── README*.md # 5 語言 README
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
## 開發
|
|
491
|
+
|
|
492
|
+
```bash
|
|
493
|
+
npm install
|
|
494
|
+
npm run build # 編譯 TypeScript → dist/
|
|
495
|
+
npm test # 執行 Jest 測試
|
|
496
|
+
npm run lint # 僅類型檢查 (tsc --noEmit)
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
## 授權
|
|
500
|
+
|
|
501
|
+
MIT — 見 [LICENSE](LICENSE)。
|