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.md
ADDED
|
@@ -0,0 +1,504 @@
|
|
|
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
|
+
Framework-independent TypeScript SDK for the Lansenger (蓝信) platform — supports Lansenger apps, organization bots, and personal bots.
|
|
6
|
+
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
[](https://www.typescriptlang.org/)
|
|
9
|
+
[](https://nodejs.org/)
|
|
10
|
+
|
|
11
|
+
> Zero framework dependencies — only `node-fetch` (v2, CommonJS compatible). Works with any Node.js project.
|
|
12
|
+
|
|
13
|
+
## Supported Bot Types
|
|
14
|
+
|
|
15
|
+
| Bot Type | Auth | WebSocket Inbound | All APIs |
|
|
16
|
+
|----------|------|-------------------|----------|
|
|
17
|
+
| **Lansenger App** | appToken + userToken | ✗ (uses webhook) | ✓ |
|
|
18
|
+
| **Organization Bot** | appToken + userToken | ✗ (uses webhook) | ✓ |
|
|
19
|
+
| **Personal Bot** | appToken | ✓ (WebSocket) | ✓ (limited for non-bot APIs) |
|
|
20
|
+
|
|
21
|
+
All three bot types use the same auth mechanism: `appToken` is required for every API call; `userToken` is only needed for specific user-level operations (user info, staff search, calendar, etc.).
|
|
22
|
+
|
|
23
|
+
## Features
|
|
24
|
+
|
|
25
|
+
- **Async client** — `LansengerClient` with Promise-based API
|
|
26
|
+
- **Credential & token persistence** — `CredentialStore` saves app_id, app_secret, URLs, appToken, userToken to file (survives restarts)
|
|
27
|
+
- **OAuth2 user authentication** — authorize URL, code exchange, token refresh
|
|
28
|
+
- **Organization & departments** — org info, department detail/children/staff
|
|
29
|
+
- **Staff & contacts** — basic/detailed info, ID mapping, department ancestors, search
|
|
30
|
+
- **Messaging** — 3 private chat channels (bot, official account, user impersonate) + group chat, all message types, @mention, human/bot sender identity, urgent reminders
|
|
31
|
+
- **Rich cards** — appCard (with dynamic status updates), oacard, linkCard, appArticles
|
|
32
|
+
- **Streaming messages** — SSE-based real-time delivery for AI agents
|
|
33
|
+
- **Media upload/download** — files, images, videos with auto type detection, media path fetch
|
|
34
|
+
- **Message management** — revoke, dynamic card update
|
|
35
|
+
- **Groups** — create, info, members, list, membership check, update settings & members, dismiss
|
|
36
|
+
- **Calendar & schedule** — primary calendar, schedule CRUD + update, attendee management + attendee metadata
|
|
37
|
+
- **Unified todo** — create, update, delete, query, executor management, status counts
|
|
38
|
+
- **Callback events** — 25 event types, structured parsing, AES decryption (per 4.10.1.4), SHA1 signature verification
|
|
39
|
+
- **Chat reading** — fetch chat list, fetch chat messages (4.24 MCP)
|
|
40
|
+
|
|
41
|
+
## Quick Install
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm install lansenger-sdk-ts
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
For development:
|
|
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. Authentication
|
|
58
|
+
|
|
59
|
+
### appToken — Required for all API calls
|
|
60
|
+
|
|
61
|
+
Every SDK method requires `appToken`. The client automatically obtains and refreshes it using your `app_id` + `app_secret`. You never need to manage appToken manually — the `TokenManager` handles the lifecycle:
|
|
62
|
+
|
|
63
|
+
1. **First call** → `GET /v1/apptoken/create` with app_id + app_secret → returns `appToken` (valid 2 hours)
|
|
64
|
+
2. **Subsequent calls** → reuse cached appToken until expiry
|
|
65
|
+
3. **Token expired** → automatically refresh via the same endpoint
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
import { LansengerClient } from "lansenger-sdk-ts";
|
|
69
|
+
|
|
70
|
+
const client = new LansengerClient("your-appid", "your-secret");
|
|
71
|
+
|
|
72
|
+
// You can also get/invalidate token manually
|
|
73
|
+
const token = await client.getToken();
|
|
74
|
+
client.invalidateToken(); // force refresh on next call
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### userToken — Only needed for specific endpoints
|
|
78
|
+
|
|
79
|
+
`userToken` represents a specific Lansenger user's authorization (obtained via OAuth2). It's only required for:
|
|
80
|
+
- User-level information (fetchUserInfo, fetchStaffDetail, searchStaff)
|
|
81
|
+
- Calendar & schedule operations (fetchPrimaryCalendar, createSchedule, etc.)
|
|
82
|
+
- Group operations as a human sender
|
|
83
|
+
|
|
84
|
+
### Getting Credentials
|
|
85
|
+
|
|
86
|
+
| Bot Type | How to get app_id + app_secret |
|
|
87
|
+
|----------|--------------------------------|
|
|
88
|
+
| **Personal Bot** | Lansenger desktop → Contacts → Smart Bots → Personal Bots → click ℹ️ icon (mobile client does NOT show credentials) |
|
|
89
|
+
| **Lansenger App** | Create at Lansenger Developer Center — may require organization admin approval |
|
|
90
|
+
| **Organization Bot** | Create at Lansenger Developer Center — may require organization admin approval |
|
|
91
|
+
|
|
92
|
+
### OAuth2 user-level auth
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
// Build authorize URL — redirect user to Lansenger passport
|
|
96
|
+
const url = client.buildAuthorizeUrl("https://myapp.com/callback");
|
|
97
|
+
|
|
98
|
+
// After user authorizes, exchange code for userToken + refreshToken
|
|
99
|
+
const tokenResult = await client.exchangeCode("auth_code_from_callback");
|
|
100
|
+
|
|
101
|
+
// Refresh expired userToken
|
|
102
|
+
const newToken = await client.refreshUserToken(tokenResult.refresh_token!);
|
|
103
|
+
|
|
104
|
+
// Fetch user profile
|
|
105
|
+
const userInfo = await client.fetchUserInfo(tokenResult.user_token!);
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Factory methods
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
// From environment variables (LANSENGER_APP_ID, LANSENGER_APP_SECRET, etc.)
|
|
112
|
+
const client = LansengerClient.fromEnv();
|
|
113
|
+
|
|
114
|
+
// From a LansengerConfig object
|
|
115
|
+
const config = new LansengerConfig("appid", "secret", "https://open.e.lanxin.cn/open/apigw");
|
|
116
|
+
const client = LansengerClient.fromConfig(config);
|
|
117
|
+
|
|
118
|
+
// From a CredentialStore (reads persisted credentials)
|
|
119
|
+
const client = LansengerClient.fromStore();
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## 2. Organization & Departments
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
// Organization info
|
|
126
|
+
const org = await client.fetchOrgInfo("orgId");
|
|
127
|
+
|
|
128
|
+
// Department hierarchy
|
|
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. Staff & Contacts
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
// Basic staff info
|
|
138
|
+
const staff = await client.fetchStaffBasicInfo("staffOpenId");
|
|
139
|
+
|
|
140
|
+
// Detailed profile (userToken recommended)
|
|
141
|
+
const detail = await client.fetchStaffDetail("staffOpenId", { user_token: "ut" });
|
|
142
|
+
|
|
143
|
+
// Map phone → staffId
|
|
144
|
+
const mapping = await client.fetchStaffIdMapping("orgId", "mobile", "13800138000");
|
|
145
|
+
|
|
146
|
+
// Department ancestors for a staff member
|
|
147
|
+
const ancestors = await client.fetchDepartmentAncestors("staffOpenId");
|
|
148
|
+
|
|
149
|
+
// Search staff (requires userToken)
|
|
150
|
+
const results = await client.searchStaff("Zhang San", { user_token: "ut" });
|
|
151
|
+
|
|
152
|
+
// Org extra field IDs
|
|
153
|
+
const fields = await client.fetchOrgExtraFieldIds("orgId");
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## 4. Messaging & Media
|
|
157
|
+
|
|
158
|
+
#### Bot private chat — most common
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
const result = await client.sendText("staff123", "Hello!");
|
|
162
|
+
const result = await client.sendMarkdown("staff123", "**Bold**");
|
|
163
|
+
const result = await client.sendFile("staff123", "/path/to/report.pdf");
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
#### Public account channel
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
const result = await client.sendAccountMessage(
|
|
170
|
+
"text", { text: { content: "System notice" } },
|
|
171
|
+
["staff1", "staff2"], // chat_ids
|
|
172
|
+
undefined, // department_ids
|
|
173
|
+
{ account_id: "524288-xxxx" },
|
|
174
|
+
);
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
#### User impersonate channel (requires userToken)
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
const result = await client.sendUserMessage(
|
|
181
|
+
"staff456", "text", { text: { content: "Hello" } },
|
|
182
|
+
{ user_token: "ut" }, // required
|
|
183
|
+
);
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
#### Group chat
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
// Bot → group
|
|
190
|
+
const result = await client.sendText("group123", "Notice", { is_group: true });
|
|
191
|
+
|
|
192
|
+
// Human → group (with userToken)
|
|
193
|
+
const result = await client.sendGroupMessage(
|
|
194
|
+
"group123", "text", { text: { content: "I'll handle it" } },
|
|
195
|
+
{ user_token: "ut" },
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
// @mention in group
|
|
199
|
+
const result = await client.sendText(
|
|
200
|
+
"group123", "Important!", { is_group: true, reminder_all: true },
|
|
201
|
+
);
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
#### Rich cards
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
const result = await client.sendAppCard("staff123", "Approval", { is_dynamic: true });
|
|
208
|
+
const result = await client.sendLinkCard("staff123", "Article", "https://...");
|
|
209
|
+
const result = await client.sendAppArticles("staff123", [{ title: "Article 1", link: "..." }]);
|
|
210
|
+
|
|
211
|
+
// Update dynamic card status
|
|
212
|
+
const result = await client.updateDynamicCard("msg123", { is_last_update: true });
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
#### Streaming messages (for AI agents)
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
const result = await client.createStreamMessage("staff1", "staff", "stream-id-1");
|
|
219
|
+
const result = await client.fetchStreamMessage("msg123");
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
#### Media
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
// Upload
|
|
226
|
+
const upload = await client.uploadMediaFile("/path/to/file.pdf");
|
|
227
|
+
|
|
228
|
+
// Download
|
|
229
|
+
const download = await client.downloadMediaFile("media123");
|
|
230
|
+
|
|
231
|
+
// Save to file
|
|
232
|
+
const filePath = await client.downloadMediaToFile("media123", { target_path: "/tmp/file.pdf" });
|
|
233
|
+
|
|
234
|
+
// Get download URL path (4.5.3)
|
|
235
|
+
const pathResult = await client.fetchMediaPathInfo("media123");
|
|
236
|
+
|
|
237
|
+
// Revoke messages
|
|
238
|
+
const result = await client.revokeMessage(["msg1", "msg2"]);
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
#### Urgent reminders (4.6.14)
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
import { REMINDER_TYPE_POPUP, REMINDER_TYPE_SMS } from "lansenger-sdk-ts";
|
|
245
|
+
|
|
246
|
+
const result = await client.sendReminderMsg(
|
|
247
|
+
"msg123",
|
|
248
|
+
[REMINDER_TYPE_POPUP, REMINDER_TYPE_SMS],
|
|
249
|
+
["staff1", "staff2"],
|
|
250
|
+
);
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## 5. Groups
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
// Create group
|
|
257
|
+
const group = await client.createGroup("Project Chat", "orgId", { staff_id_list: ["s1", "s2", "s3"] });
|
|
258
|
+
|
|
259
|
+
// Fetch info & members
|
|
260
|
+
const info = await client.fetchGroupInfo("groupOpenId");
|
|
261
|
+
const members = await client.fetchGroupMembers("groupOpenId");
|
|
262
|
+
const groups = await client.fetchGroupList();
|
|
263
|
+
|
|
264
|
+
// Check membership
|
|
265
|
+
const result = await client.checkIsInGroup("groupOpenId", { staff_id: "staff1" });
|
|
266
|
+
|
|
267
|
+
// Update settings
|
|
268
|
+
await client.updateGroupInfo("groupId", { name: "New Name", manage_mode: 1 });
|
|
269
|
+
|
|
270
|
+
// Add/remove members
|
|
271
|
+
await client.updateGroupMembers("groupId", { add_user_list: ["staff4"], del_user_list: ["staff3"] });
|
|
272
|
+
|
|
273
|
+
// Dismiss/delete group (owner only, 4.28.6)
|
|
274
|
+
await client.dismissGroup("groupId");
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## 6. Calendar & Schedule
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
// Get primary calendar (requires userToken or userId)
|
|
281
|
+
const cal = await client.fetchPrimaryCalendar({ user_token: "ut" });
|
|
282
|
+
|
|
283
|
+
// Create schedule
|
|
284
|
+
const schedule = await client.createSchedule(
|
|
285
|
+
cal.calendar_id!, "Team Meeting",
|
|
286
|
+
{ date: "2024-01-15", time: "10:00", timeZone: "Asia/Shanghai" },
|
|
287
|
+
{ date: "2024-01-15", time: "11:00", timeZone: "Asia/Shanghai" },
|
|
288
|
+
[{ staffId: "staff1", attendeeFlag: "required" }],
|
|
289
|
+
{ user_token: "ut" },
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
// Fetch/delete schedule
|
|
293
|
+
const info = await client.fetchSchedule("cal1", "sch1", { user_token: "ut" });
|
|
294
|
+
await client.deleteSchedule("cal1", "sch1", { user_token: "ut" });
|
|
295
|
+
|
|
296
|
+
// Schedule list in time range (max 42 days)
|
|
297
|
+
const schedules = await client.fetchScheduleList("cal1", 1705276800000, 1707940800000, { user_token: "ut" });
|
|
298
|
+
|
|
299
|
+
// Attendee management
|
|
300
|
+
const attendees = await client.fetchScheduleAttendees("cal1", "sch1", { user_token: "ut" });
|
|
301
|
+
await client.addScheduleAttendees("cal1", "sch1", ["staff2"], { user_token: "ut" });
|
|
302
|
+
await client.deleteScheduleAttendees("cal1", "sch1", ["staff2"], { user_token: "ut" });
|
|
303
|
+
|
|
304
|
+
// Update schedule (4.23.12)
|
|
305
|
+
await client.updateSchedule("cal1", "sch1", { summary: "Updated Meeting", user_token: "ut" });
|
|
306
|
+
|
|
307
|
+
// Update attendee metadata (4.23.17) — RSVP, color, busy/free, reminders
|
|
308
|
+
await client.updateScheduleAttendeeMeta("cal1", "sch1", {
|
|
309
|
+
rsvp_status: "accept", busy_free_state: "busy", remind_times: [5, 15], user_token: "ut",
|
|
310
|
+
});
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
## 7. Unified Todo
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
import { TODO_TYPE_APPROVAL, TODO_TODO_STATUS_DONE } from "lansenger-sdk-ts";
|
|
317
|
+
|
|
318
|
+
// Create todo task
|
|
319
|
+
const todo = await client.createTodoTask(
|
|
320
|
+
"Approval Request", "https://app.com/a/1", "https://pc.app.com/a/1",
|
|
321
|
+
["staff1"], "org1", TODO_TYPE_APPROVAL,
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
// Update status (11=pending-read, 12=read, 21=pending-do, 22=done)
|
|
325
|
+
await client.updateTodoTaskStatus("taskId", TODO_TODO_STATUS_DONE, "org1");
|
|
326
|
+
|
|
327
|
+
// Update content
|
|
328
|
+
await client.updateTodoTask("taskId", "Updated", "l", "p", "org1");
|
|
329
|
+
|
|
330
|
+
// Delete (sender only)
|
|
331
|
+
await client.deleteTodoTask("taskId", "org1");
|
|
332
|
+
|
|
333
|
+
// Query
|
|
334
|
+
const listResult = await client.fetchTodoTaskList("org1");
|
|
335
|
+
const task = await client.fetchTodoTaskById("taskId", "org1");
|
|
336
|
+
const task = await client.fetchTodoTaskBySourceId("src1", "org1");
|
|
337
|
+
const counts = await client.fetchTodoTaskStatusCounts("staff1", "org1");
|
|
338
|
+
|
|
339
|
+
// Executor management
|
|
340
|
+
await client.addExecutors(["staff2"], "org1", { todotask_id: "taskId" });
|
|
341
|
+
await client.deleteExecutors(["staff2"], "org1", { todotask_id: "taskId" });
|
|
342
|
+
const executors = await client.fetchExecutorList("taskId", "org1");
|
|
343
|
+
await client.updateExecutorStatus(
|
|
344
|
+
[{ executorId: "staff1", todotaskId: "taskId", status: "22" }],
|
|
345
|
+
"org1",
|
|
346
|
+
);
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
## 8. Chat Reading (4.24 MCP)
|
|
350
|
+
|
|
351
|
+
```typescript
|
|
352
|
+
// Fetch chat list (private + group conversations)
|
|
353
|
+
const chatList = await client.fetchChatList({ user_token: "ut" });
|
|
354
|
+
|
|
355
|
+
// Fetch chat messages
|
|
356
|
+
const messages = await client.fetchChatMessages({
|
|
357
|
+
staff_id: "staff1", // or group_id: "group1"
|
|
358
|
+
user_token: "ut",
|
|
359
|
+
});
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
## 9. Callback Events
|
|
363
|
+
|
|
364
|
+
The SDK supports both plain JSON and AES-encrypted callback payloads (per Lansenger API spec 4.10.1.4).
|
|
365
|
+
|
|
366
|
+
### Configuration
|
|
367
|
+
|
|
368
|
+
Set `encoding_key` and `callback_token` (from Lansenger Developer Center callback settings):
|
|
369
|
+
|
|
370
|
+
```typescript
|
|
371
|
+
const client = new LansengerClient("appid", "secret", undefined, undefined, undefined, undefined, "BASE64_AES_KEY", "CALLBACK_TOKEN");
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
Or via environment variables: `LANSENGER_ENCODING_KEY`, `LANSENGER_CALLBACK_TOKEN`.
|
|
375
|
+
|
|
376
|
+
### Parse callback payload (auto-detects encrypted vs plain JSON)
|
|
377
|
+
|
|
378
|
+
```typescript
|
|
379
|
+
import { parseCallbackPayload } from "lansenger-sdk-ts";
|
|
380
|
+
|
|
381
|
+
// Plain JSON webhook
|
|
382
|
+
const events = parseCallbackPayload('{"events": [...]}');
|
|
383
|
+
|
|
384
|
+
// AES-encrypted payload (auto-decrypts with encodingKey)
|
|
385
|
+
const events = parseCallbackPayload(encryptedData, {
|
|
386
|
+
encoding_key: "BASE64_AES_KEY",
|
|
387
|
+
known_app_id: "your-appid",
|
|
388
|
+
});
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### Verify signature
|
|
392
|
+
|
|
393
|
+
```typescript
|
|
394
|
+
import { verifyCallbackSignature } from "lansenger-sdk-ts";
|
|
395
|
+
|
|
396
|
+
// sha1(sort(token, timestamp, nonce, dataEncrypt))
|
|
397
|
+
const isValid = verifyCallbackSignature(
|
|
398
|
+
timestamp, nonce, signature, encodingKey,
|
|
399
|
+
{ data_encrypt: encryptedData, callback_token: "CALLBACK_TOKEN" },
|
|
400
|
+
);
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### Event types
|
|
404
|
+
|
|
405
|
+
```typescript
|
|
406
|
+
const types = LansengerClient.getCallbackEventTypes(); // 25 event types across 13 categories
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
## Message Type Capability Matrix
|
|
410
|
+
|
|
411
|
+
| msgType | Markdown | @mention | Attachments | Private Channels | Group Chat | Notes |
|
|
412
|
+
|---------|----------|----------|-------------|------------------|------------|-------|
|
|
413
|
+
| `text` | ✗ | ✓ (group) | ✓ | Bot, Official Account, User Impersonate | ✓ | Up to 6000 bytes |
|
|
414
|
+
| `formatText` | ✓ | ✗ | ✗ | User Impersonate only | ✓ | Markdown via formatType=1 |
|
|
415
|
+
| `oacard` | ✗ | ✗ | ✗ | Bot, Official Account, User Impersonate | ✓ | Simple card with fields |
|
|
416
|
+
| `appCard` | ✓ (div tags) | ✗ | ✗ | Bot, Official Account, User Impersonate | ✓ | Rich card, dynamic updates |
|
|
417
|
+
| `linkCard` | ✗ | ✗ | ✗ | Bot, Official Account | ✓ | Link preview card |
|
|
418
|
+
| `appArticles` | ✗ | ✗ | ✗ | Bot private only | ✓ | Article list (1+ articles) |
|
|
419
|
+
|
|
420
|
+
**Group chat** supports all message types. Only group chat supports @mention.
|
|
421
|
+
|
|
422
|
+
## Configuration
|
|
423
|
+
|
|
424
|
+
### Environment Variables
|
|
425
|
+
|
|
426
|
+
| Variable | Required | Description | Default |
|
|
427
|
+
|----------|----------|-------------|---------|
|
|
428
|
+
| `LANSENGER_APP_ID` | ✓ | App/Bot ID | — |
|
|
429
|
+
| `LANSENGER_APP_SECRET` | ✓ | App/Bot Secret | — |
|
|
430
|
+
| `LANSENGER_API_GATEWAY_URL` | ✗ | API Gateway URL | `https://open.e.lanxin.cn/open/apigw` |
|
|
431
|
+
| `LANSENGER_PASSPORT_URL` | ✗ | Passport URL (for OAuth2) | — |
|
|
432
|
+
| `LANSENGER_ENCODING_KEY` | ✗ | Callback AES encryption key (Base64) | — |
|
|
433
|
+
| `LANSENGER_CALLBACK_TOKEN` | ✗ | Callback signature token | — |
|
|
434
|
+
|
|
435
|
+
### Credential & Token Persistence
|
|
436
|
+
|
|
437
|
+
By default, credentials and tokens stay in memory only (lost on process exit). Enable file persistence with `storePath`:
|
|
438
|
+
|
|
439
|
+
```typescript
|
|
440
|
+
import { LansengerClient, CredentialStore } from "lansenger-sdk-ts";
|
|
441
|
+
|
|
442
|
+
// Auto-persist to ~/.lansenger/sdk_state.json
|
|
443
|
+
const client = new LansengerClient("appid", "secret", undefined, undefined, undefined, "~/.lansenger/sdk_state.json", "BASE64_AES_KEY", "CALLBACK_TOKEN");
|
|
444
|
+
|
|
445
|
+
// Or from env with persistence
|
|
446
|
+
const client = LansengerClient.fromEnv("~/.lansenger/sdk_state.json");
|
|
447
|
+
|
|
448
|
+
// Manual store operations
|
|
449
|
+
const store = new CredentialStore("~/.lansenger/sdk_state.json");
|
|
450
|
+
store.saveCredentials("app_id", "app_secret", "https://open.e.lanxin.cn/open/apigw");
|
|
451
|
+
store.saveUserToken("user_token", "refresh_token");
|
|
452
|
+
const token = store.loadAppToken(); // null if expired
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
## Project Structure
|
|
456
|
+
|
|
457
|
+
```
|
|
458
|
+
lansenger-sdk-ts/
|
|
459
|
+
├── src/
|
|
460
|
+
│ ├── index.ts # All exports
|
|
461
|
+
│ ├── client.ts # LansengerClient (async)
|
|
462
|
+
│ ├── config.ts # LansengerConfig
|
|
463
|
+
│ ├── auth.ts # TokenManager — appToken lifecycle
|
|
464
|
+
│ ├── oauth.ts # OAuth2 helpers
|
|
465
|
+
│ ├── constants.ts # API endpoints, media types, OAuth scopes
|
|
466
|
+
│ ├── exceptions.ts # LansengerError hierarchy
|
|
467
|
+
│ ├── models.ts # 38+ result class types
|
|
468
|
+
│ ├── http.ts # doGet, doPost, doPostMultipart, parseApiResponse
|
|
469
|
+
│ ├── urlHelpers.ts # buildApiUrl with pathVars support
|
|
470
|
+
│ ├── contacts.ts # Staff & org info APIs
|
|
471
|
+
│ ├── departments.ts # Department APIs
|
|
472
|
+
│ ├── accountMessages.ts # Public account channel
|
|
473
|
+
│ ├── userMessages.ts # User impersonate channel
|
|
474
|
+
│ ├── groupMessages.ts # Group chat channel
|
|
475
|
+
│ ├── media.ts # Upload/download
|
|
476
|
+
│ ├── streaming.ts # SSE streaming
|
|
477
|
+
│ ├── persistence.ts # CredentialStore — file-based persistence
|
|
478
|
+
│ ├── callbacks.ts # Callback events — 25 event types, AES decryption, SHA1 verification
|
|
479
|
+
│ ├── groups.ts # Group APIs (including dismiss 4.28.6)
|
|
480
|
+
│ ├── todos.ts # Unified Todo
|
|
481
|
+
│ ├── calendars.ts # Calendar & Schedule
|
|
482
|
+
│ ├── reminders.ts # Urgent message reminders (4.6.14)
|
|
483
|
+
│ ├── chats.ts # Chat reading (4.24 MCP)
|
|
484
|
+
│ └── users.ts # User info
|
|
485
|
+
├── tests/ # Unit tests
|
|
486
|
+
├── package.json
|
|
487
|
+
├── tsconfig.json
|
|
488
|
+
├── jest.config.js
|
|
489
|
+
├── LICENSE
|
|
490
|
+
└── README.md
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
## Development
|
|
494
|
+
|
|
495
|
+
```bash
|
|
496
|
+
npm install
|
|
497
|
+
npm run build # Compile TypeScript → dist/
|
|
498
|
+
npm test # Run Jest tests
|
|
499
|
+
npm run lint # Type-check only (tsc --noEmit)
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
## License
|
|
503
|
+
|
|
504
|
+
MIT — see [LICENSE](LICENSE).
|