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/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Lansenger PM
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.fr.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
|
+
SDK TypeScript indépendant du framework pour la plateforme Lansenger (蓝信) — prend en charge les applications Lansenger, les bots d'organisation et les bots personnels.
|
|
6
|
+
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
[](https://www.typescriptlang.org/)
|
|
9
|
+
[](https://nodejs.org/)
|
|
10
|
+
|
|
11
|
+
> Zéro dépendance de framework — uniquement `node-fetch` (v2, compatible CommonJS). Fonctionne avec tout projet Node.js.
|
|
12
|
+
|
|
13
|
+
## Types de bots pris en charge
|
|
14
|
+
|
|
15
|
+
| Type de bot | Auth | WebSocket entrant | Toutes les API |
|
|
16
|
+
|-------------|------|-------------------|----------------|
|
|
17
|
+
| **Application Lansenger** | appToken + userToken | ✗ (utilise webhook) | ✓ |
|
|
18
|
+
| **Bot d'organisation** | appToken + userToken | ✗ (utilise webhook) | ✓ |
|
|
19
|
+
| **Bot personnel** | appToken | ✓ (WebSocket) | ✓ (limité pour les API non-bot) |
|
|
20
|
+
|
|
21
|
+
Les trois types de bots utilisent le même mécanisme d'authentification : `appToken` est requis pour chaque appel API ; `userToken` est uniquement nécessaire pour certaines opérations au niveau utilisateur (infos utilisateur, recherche de staff, calendrier, etc.).
|
|
22
|
+
|
|
23
|
+
## Fonctionnalités
|
|
24
|
+
|
|
25
|
+
- **Client async** — `LansengerClient` fournit une API basée sur des Promesses
|
|
26
|
+
- **Persistance des identifiants & tokens** — `CredentialStore` sauvegarde app_id, app_secret, URLs, appToken, userToken dans un fichier (survit aux redémarrages)
|
|
27
|
+
- **Authentification utilisateur OAuth2** — URL d'autorisation, échange de code, refresh de token
|
|
28
|
+
- **Organisation & départements** — infos org, détail/children/staff de département
|
|
29
|
+
- **Staff & contacts** — infos basiques/détaillées, mapping d'ID, ancêtres de département, recherche
|
|
30
|
+
- **Messagerie** — 3 canaux de chat privé (bot, compte officiel, impersonnation utilisateur) + chat de groupe, tous les types de messages, @mention, identité humain/bot, rappels urgents
|
|
31
|
+
- **Cartes riches** — appCard (avec mises à jour dynamiques), oacard, linkCard, appArticles
|
|
32
|
+
- **Messages en streaming** — delivery en temps réel basé sur SSE pour les agents IA
|
|
33
|
+
- **Upload/download de médias** — fichiers, images, vidéos avec detection automatique du type, récupération du chemin média
|
|
34
|
+
- **Gestion des messages** — révocation, mise à jour dynamique de carte
|
|
35
|
+
- **Groups** — créer, infos, membres, liste, vérification de membership, mise à jour des paramètres & membres, dissoudre
|
|
36
|
+
- **Calendrier & Schedule** — calendrier principal, CRUD de schedule + mise à jour, gestion des participants + métadonnées participants
|
|
37
|
+
- **Todo unifié** — créer, mettre à jour, supprimer, interroger, gestion d'exécuteur, comptes de statut
|
|
38
|
+
- **Événements de callback** — 25 types d'événements, parsing structuré, décryptage AES (spec 4.10.1.4), vérification de signature SHA1
|
|
39
|
+
- **Lecture de chats** — liste de chats, messages de chat (4.24 MCP)
|
|
40
|
+
|
|
41
|
+
## Installation rapide
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm install lansenger-sdk-ts
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Pour le développement :
|
|
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. Authentification
|
|
58
|
+
|
|
59
|
+
### appToken — Requis pour tous les appels API
|
|
60
|
+
|
|
61
|
+
Chaque méthode du SDK requiert `appToken`. Le client l'obtient et le refresh automatiquement à partir de votre `app_id` + `app_secret` via `GET /v1/apptoken/create`. Vous n'avez jamais besoin de gérer appToken manuellement — le `TokenManager` gère le cycle de vie :
|
|
62
|
+
|
|
63
|
+
1. **Premier appel** → `GET /v1/apptoken/create` avec app_id + app_secret → renvoie `appToken` (valide 2 heures)
|
|
64
|
+
2. **Appels suivants** → réutilise le appToken en cache jusqu'à expiration
|
|
65
|
+
3. **Token expiré** → refresh automatique via le même endpoint
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
import { LansengerClient } from "lansenger-sdk-ts";
|
|
69
|
+
|
|
70
|
+
const client = new LansengerClient("your-appid", "your-secret");
|
|
71
|
+
|
|
72
|
+
// Vous pouvez aussi obtenir/invalider le token manuellement
|
|
73
|
+
const token = await client.getToken();
|
|
74
|
+
client.invalidateToken(); // force le refresh au prochain appel
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### userToken — Uniquement nécessaire pour certains endpoints
|
|
78
|
+
|
|
79
|
+
`userToken` représente l'autorisation d'un utilisateur Lansenger spécifique (obtenu via OAuth2). Il est uniquement requis pour :
|
|
80
|
+
- Informations au niveau utilisateur (fetchUserInfo, fetchStaffDetail, searchStaff)
|
|
81
|
+
- Opérations de calendrier & schedule (fetchPrimaryCalendar, createSchedule, etc.)
|
|
82
|
+
- Opérations de groupe comme envoyeur humain
|
|
83
|
+
|
|
84
|
+
### Obtenir les identifiants
|
|
85
|
+
|
|
86
|
+
| Type de bot | Comment obtenir app_id + app_secret |
|
|
87
|
+
|-------------|--------------------------------------|
|
|
88
|
+
| **Bot personnel** | Client Lansenger (desktop) → Contacts → Bots intelligents → Bots personnels → cliquer sur l'icône ℹ️ (le client mobile ne permet pas de voir les identifiants) |
|
|
89
|
+
| **Application Lansenger** | Créer sur le Lansenger Developer Center — peut nécessiter l'approbation de l'administrateur de l'organisation |
|
|
90
|
+
| **Bot d'organisation** | Créer sur le Lansenger Developer Center — peut nécessiter l'approbation de l'administrateur de l'organisation |
|
|
91
|
+
|
|
92
|
+
### Authentification OAuth2 niveau utilisateur
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
// Construire l'URL d'autorisation — rediriger l'utilisateur vers Lansenger passport
|
|
96
|
+
const url = client.buildAuthorizeUrl("https://myapp.com/callback");
|
|
97
|
+
|
|
98
|
+
// Après autorisation de l'utilisateur, échanger le code pour userToken + refreshToken
|
|
99
|
+
const tokenResult = await client.exchangeCode("auth_code_from_callback");
|
|
100
|
+
|
|
101
|
+
// Refresh un userToken expiré
|
|
102
|
+
const newToken = await client.refreshUserToken(tokenResult.refresh_token!);
|
|
103
|
+
|
|
104
|
+
// Récupérer le profil utilisateur
|
|
105
|
+
const userInfo = await client.fetchUserInfo(tokenResult.user_token!);
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Méthodes factory
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
// Depuis les variables d'environnement (LANSENGER_APP_ID, LANSENGER_APP_SECRET, etc.)
|
|
112
|
+
const client = LansengerClient.fromEnv();
|
|
113
|
+
|
|
114
|
+
// Depuis un objet LansengerConfig
|
|
115
|
+
const config = new LansengerConfig("appid", "secret", "https://open.e.lanxin.cn/open/apigw");
|
|
116
|
+
const client = LansengerClient.fromConfig(config);
|
|
117
|
+
|
|
118
|
+
// Depuis CredentialStore (lecture des identifiants persistés)
|
|
119
|
+
const client = LansengerClient.fromStore();
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## 2. Organisation & Départements
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
// Infos organisation
|
|
126
|
+
const org = await client.fetchOrgInfo("orgId");
|
|
127
|
+
|
|
128
|
+
// Hiérarchie des départements
|
|
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
|
+
// Infos basiques du staff
|
|
138
|
+
const staff = await client.fetchStaffBasicInfo("staffOpenId");
|
|
139
|
+
|
|
140
|
+
// Profil détaillé (userToken recommandé)
|
|
141
|
+
const detail = await client.fetchStaffDetail("staffOpenId", { user_token: "ut" });
|
|
142
|
+
|
|
143
|
+
// Mapper téléphone → staffId
|
|
144
|
+
const mapping = await client.fetchStaffIdMapping("orgId", "mobile", "13800138000");
|
|
145
|
+
|
|
146
|
+
// Ancêtres de département pour un membre du staff
|
|
147
|
+
const ancestors = await client.fetchDepartmentAncestors("staffOpenId");
|
|
148
|
+
|
|
149
|
+
// Rechercher du staff (requiert userToken)
|
|
150
|
+
const results = await client.searchStaff("Zhang San", { user_token: "ut" });
|
|
151
|
+
|
|
152
|
+
// IDs de champs extra de l'org
|
|
153
|
+
const fields = await client.fetchOrgExtraFieldIds("orgId");
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## 4. Messagerie & Médias
|
|
157
|
+
|
|
158
|
+
#### Chat privé de bot — le plus courant
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
const result = await client.sendText("staff123", "Bonjour !");
|
|
162
|
+
const result = await client.sendMarkdown("staff123", "**Gras**");
|
|
163
|
+
const result = await client.sendFile("staff123", "/path/to/report.pdf");
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
#### Canal compte officiel
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
const result = await client.sendAccountMessage(
|
|
170
|
+
"text", { text: { content: "Notice système" } },
|
|
171
|
+
["staff1", "staff2"], undefined,
|
|
172
|
+
{ account_id: "524288-xxxx" },
|
|
173
|
+
);
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
#### Canal impersonnation utilisateur (requiert userToken)
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
const result = await client.sendUserMessage(
|
|
180
|
+
"staff456", "text", { text: { content: "Bonjour" } },
|
|
181
|
+
{ user_token: "ut" },
|
|
182
|
+
);
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
#### Chat de groupe
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
// Bot → groupe
|
|
189
|
+
const result = await client.sendText("group123", "Notice", { is_group: true });
|
|
190
|
+
|
|
191
|
+
// Humain → groupe (avec userToken)
|
|
192
|
+
const result = await client.sendGroupMessage(
|
|
193
|
+
"group123", "text", { text: { content: "Je vais le traiter" } },
|
|
194
|
+
{ user_token: "ut" },
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
// @mention dans un groupe
|
|
198
|
+
const result = await client.sendText("group123", "Important !", { is_group: true, reminder_all: true });
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
#### Cartes enrichies
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
const result = await client.sendAppCard("staff123", "Approbation", { is_dynamic: true });
|
|
205
|
+
const result = await client.sendLinkCard("staff123", "Article", "https://...");
|
|
206
|
+
const result = await client.sendAppArticles("staff123", [{ title: "Article 1", link: "..." }]);
|
|
207
|
+
|
|
208
|
+
// Mettre à jour le statut d'une carte dynamique
|
|
209
|
+
const result = await client.updateDynamicCard("msg123", { is_last_update: true });
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
#### Messages en streaming (pour agents IA)
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
const result = await client.createStreamMessage("staff1", "staff", "stream-id-1");
|
|
216
|
+
const result = await client.fetchStreamMessage("msg123");
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
#### Médias
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
// Upload
|
|
223
|
+
const upload = await client.uploadMediaFile("/path/to/file.pdf");
|
|
224
|
+
|
|
225
|
+
// Download
|
|
226
|
+
const download = await client.downloadMediaFile("media123");
|
|
227
|
+
|
|
228
|
+
// Sauvegarder dans un fichier
|
|
229
|
+
const filePath = await client.downloadMediaToFile("media123", { target_path: "/tmp/file.pdf" });
|
|
230
|
+
|
|
231
|
+
// Récupérer le chemin de téléchargement URL (4.5.3)
|
|
232
|
+
const pathResult = await client.fetchMediaPathInfo("media123");
|
|
233
|
+
|
|
234
|
+
// Révoquer des messages
|
|
235
|
+
const result = await client.revokeMessage(["msg1", "msg2"]);
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
#### Rappels urgents (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. Groups
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
// Créer un groupe
|
|
254
|
+
const group = await client.createGroup("Chat Projet", "orgId", { staff_id_list: ["s1", "s2", "s3"] });
|
|
255
|
+
|
|
256
|
+
// Récupérer infos & membres
|
|
257
|
+
const info = await client.fetchGroupInfo("groupOpenId");
|
|
258
|
+
const members = await client.fetchGroupMembers("groupOpenId");
|
|
259
|
+
const groups = await client.fetchGroupList();
|
|
260
|
+
|
|
261
|
+
// Vérifier le membership
|
|
262
|
+
const result = await client.checkIsInGroup("groupOpenId", { staff_id: "staff1" });
|
|
263
|
+
|
|
264
|
+
// Mettre à jour les paramètres
|
|
265
|
+
await client.updateGroupInfo("groupId", { name: "Nouveau nom", manage_mode: 1 });
|
|
266
|
+
|
|
267
|
+
// Ajouter/supprimer des membres
|
|
268
|
+
await client.updateGroupMembers("groupId", { add_user_list: ["staff4"], del_user_list: ["staff3"] });
|
|
269
|
+
|
|
270
|
+
// Dissoudre un groupe (propriétaire uniquement, 4.28.6)
|
|
271
|
+
await client.dismissGroup("groupId");
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## 6. Calendrier & Schedule
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
// Récupérer le calendrier principal (requiert userToken ou userId)
|
|
278
|
+
const cal = await client.fetchPrimaryCalendar({ user_token: "ut" });
|
|
279
|
+
|
|
280
|
+
// Créer un schedule
|
|
281
|
+
const schedule = await client.createSchedule(
|
|
282
|
+
cal.calendar_id!, "Réunion d'équipe",
|
|
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
|
+
// Récupérer/supprimer un schedule
|
|
290
|
+
const info = await client.fetchSchedule("cal1", "sch1", { user_token: "ut" });
|
|
291
|
+
await client.deleteSchedule("cal1", "sch1", { user_token: "ut" });
|
|
292
|
+
|
|
293
|
+
// Liste de schedules dans un intervalle de temps (max 42 jours)
|
|
294
|
+
const schedules = await client.fetchScheduleList("cal1", 1705276800000, 1707940800000, { user_token: "ut" });
|
|
295
|
+
|
|
296
|
+
// Gestion des participants
|
|
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
|
+
// Mettre à jour un schedule (4.23.12)
|
|
302
|
+
await client.updateSchedule("cal1", "sch1", { summary: "Réunion mise à jour", user_token: "ut" });
|
|
303
|
+
|
|
304
|
+
// Mettre à jour les métadonnées de participant (4.23.17) — RSVP, couleur, occupé/libre, rappels
|
|
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. Todo unifié
|
|
311
|
+
|
|
312
|
+
```typescript
|
|
313
|
+
import { TODO_TYPE_APPROVAL, TODO_TODO_STATUS_DONE } from "lansenger-sdk-ts";
|
|
314
|
+
|
|
315
|
+
// Créer une tâche todo
|
|
316
|
+
const todo = await client.createTodoTask(
|
|
317
|
+
"Demande d'approbation", "https://app.com/a/1", "https://pc.app.com/a/1",
|
|
318
|
+
["staff1"], "org1", TODO_TYPE_APPROVAL,
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
// Mettre à jour le statut (11=à lire, 12=lu, 21=à faire, 22=fait)
|
|
322
|
+
await client.updateTodoTaskStatus("taskId", TODO_TODO_STATUS_DONE, "org1");
|
|
323
|
+
|
|
324
|
+
// Mettre à jour le contenu
|
|
325
|
+
await client.updateTodoTask("taskId", "Mis à jour", "l", "p", "org1");
|
|
326
|
+
|
|
327
|
+
// Supprimer (envoyeur uniquement)
|
|
328
|
+
await client.deleteTodoTask("taskId", "org1");
|
|
329
|
+
|
|
330
|
+
// Interroger
|
|
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
|
+
// Gestion des exécuteurs
|
|
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. Lecture de chats (4.24 MCP)
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
// Récupérer la liste de chats (privé + groupe)
|
|
350
|
+
const chatList = await client.fetchChatList({ user_token: "ut" });
|
|
351
|
+
|
|
352
|
+
// Récupérer les messages de chat
|
|
353
|
+
const messages = await client.fetchChatMessages({
|
|
354
|
+
staff_id: "staff1", // ou group_id: "group1"
|
|
355
|
+
user_token: "ut",
|
|
356
|
+
});
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
## 9. Événements de callback
|
|
360
|
+
|
|
361
|
+
Le SDK prend en charge les payloads de callback en JSON brut et en AES chiffré (selon la spec API Lansenger 4.10.1.4).
|
|
362
|
+
|
|
363
|
+
### Configuration
|
|
364
|
+
|
|
365
|
+
Définissez `encoding_key` et `callback_token` (des paramètres de callback du Lansenger Developer Center) :
|
|
366
|
+
|
|
367
|
+
```typescript
|
|
368
|
+
const client = new LansengerClient("appid", "secret", undefined, undefined, undefined, undefined, "BASE64_AES_KEY", "CALLBACK_TOKEN");
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
Ou via les variables d'environnement : `LANSENGER_ENCODING_KEY`, `LANSENGER_CALLBACK_TOKEN`.
|
|
372
|
+
|
|
373
|
+
### Parser le payload de callback (auto-détecte chiffré vs JSON brut)
|
|
374
|
+
|
|
375
|
+
```typescript
|
|
376
|
+
import { parseCallbackPayload } from "lansenger-sdk-ts";
|
|
377
|
+
|
|
378
|
+
// Webhook JSON brut
|
|
379
|
+
const events = parseCallbackPayload('{"events": [...]}');
|
|
380
|
+
|
|
381
|
+
// Payload chiffré AES (auto-décryptage avec encodingKey)
|
|
382
|
+
const events = parseCallbackPayload(encryptedData, {
|
|
383
|
+
encoding_key: "BASE64_AES_KEY",
|
|
384
|
+
known_app_id: "your-appid",
|
|
385
|
+
});
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### Vérifier la signature
|
|
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
|
+
### Types d'événements
|
|
401
|
+
|
|
402
|
+
```typescript
|
|
403
|
+
const types = LansengerClient.getCallbackEventTypes(); // 25 types d'événements, 13 catégories
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
## Matrice de capacités des types de messages
|
|
407
|
+
|
|
408
|
+
| msgType | Markdown | @mention | Attachments | Canaux privés | Chat de groupe | Notes |
|
|
409
|
+
|---------|----------|----------|-------------|----------------|----------------|-------|
|
|
410
|
+
| `text` | ✗ | ✓ (groupe) | ✓ | Bot, Compte officiel, Impersonnation | ✓ | Max 6000 octets |
|
|
411
|
+
| `formatText` | ✓ | ✗ | ✗ | Impersonnation uniquement | ✓ | formatType=1 supporte Markdown |
|
|
412
|
+
| `oacard` | ✗ | ✗ | ✗ | Bot, Compte officiel, Impersonnation | ✓ | Carte simple avec champs |
|
|
413
|
+
| `appCard` | ✓ (div) | ✗ | ✗ | Bot, Compte officiel, Impersonnation | ✓ | Carte riche, mises à jour dynamiques |
|
|
414
|
+
| `linkCard` | ✗ | ✗ | ✗ | Bot, Compte officiel | ✓ | Carte de lien preview |
|
|
415
|
+
| `appArticles` | ✗ | ✗ | ✗ | Bot privé uniquement | ✓ | Liste d'articles (1+ articles) |
|
|
416
|
+
|
|
417
|
+
**Chat de groupe** supporte tous les types de messages. Seul le chat de groupe supporte @mention.
|
|
418
|
+
|
|
419
|
+
## Configuration
|
|
420
|
+
|
|
421
|
+
### Variables d'environnement
|
|
422
|
+
|
|
423
|
+
| Variable | Requis | Description | Défaut |
|
|
424
|
+
|----------|--------|-------------|--------|
|
|
425
|
+
| `LANSENGER_APP_ID` | ✓ | ID App/Bot | — |
|
|
426
|
+
| `LANSENGER_APP_SECRET` | ✓ | Secret App/Bot | — |
|
|
427
|
+
| `LANSENGER_API_GATEWAY_URL` | ✗ | URL de la passerelle API | `https://open.e.lanxin.cn/open/apigw` |
|
|
428
|
+
| `LANSENGER_PASSPORT_URL` | ✗ | URL Passport (pour OAuth2) | — |
|
|
429
|
+
| `LANSENGER_ENCODING_KEY` | ✗ | Clé de chiffrement AES callback (Base64) | — |
|
|
430
|
+
| `LANSENGER_CALLBACK_TOKEN` | ✗ | Token de signature callback | — |
|
|
431
|
+
|
|
432
|
+
### Persistance des identifiants & tokens
|
|
433
|
+
|
|
434
|
+
Par défaut, les identifiants et tokens restent en mémoire uniquement (perdus à la sortie du processus). Activez la persistance fichier avec `storePath` :
|
|
435
|
+
|
|
436
|
+
```typescript
|
|
437
|
+
import { LansengerClient, CredentialStore } from "lansenger-sdk-ts";
|
|
438
|
+
|
|
439
|
+
// Persistance auto vers ~/.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
|
+
// Ou depuis les variables d'environnement avec persistance
|
|
443
|
+
const client = LansengerClient.fromEnv("~/.lansenger/sdk_state.json");
|
|
444
|
+
|
|
445
|
+
// Opérations manuelles sur le 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(); // None si expiré
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
## Structure du projet
|
|
453
|
+
|
|
454
|
+
```
|
|
455
|
+
lansenger-sdk-ts/
|
|
456
|
+
├── src/
|
|
457
|
+
│ ├── index.ts # Toutes les exports
|
|
458
|
+
│ ├── client.ts # LansengerClient (async)
|
|
459
|
+
│ ├── config.ts # LansengerConfig
|
|
460
|
+
│ ├── auth.ts # TokenManager — cycle de vie appToken
|
|
461
|
+
│ ├── oauth.ts # Aides OAuth2
|
|
462
|
+
│ ├── constants.ts # Endpoints API, types de médias, scopes OAuth
|
|
463
|
+
│ ├── exceptions.ts # Hiérarchie LansengerError
|
|
464
|
+
│ ├── models.ts # 38+ types de résultat
|
|
465
|
+
│ ├── http.ts # doGet, doPost, doPostMultipart, parseApiResponse
|
|
466
|
+
│ ├── urlHelpers.ts # buildApiUrl (supporte pathVars)
|
|
467
|
+
│ ├── contacts.ts # API Staff & infos org
|
|
468
|
+
│ ├── departments.ts # API Départements
|
|
469
|
+
│ ├── accountMessages.ts # Canal compte officiel
|
|
470
|
+
│ ├── userMessages.ts # Canal impersonnation utilisateur
|
|
471
|
+
│ ├── groupMessages.ts # Canal Chat de groupe
|
|
472
|
+
│ ├── media.ts # Upload/download
|
|
473
|
+
│ ├── streaming.ts # Streaming SSE
|
|
474
|
+
│ ├── persistence.ts # CredentialStore — persistance fichier des identifiants & tokens
|
|
475
|
+
│ ├── callbacks.ts # Événements de callback — 25 types, décryptage AES, vérification SHA1
|
|
476
|
+
│ ├── groups.ts # API Groups (incluant dissoudre 4.28.6)
|
|
477
|
+
│ ├── todos.ts # Todo unifié
|
|
478
|
+
│ ├── calendars.ts # Calendrier & Schedule
|
|
479
|
+
│ ├── reminders.ts # Rappels urgents (4.6.14)
|
|
480
|
+
│ ├── chats.ts # Lecture de chats (4.24 MCP)
|
|
481
|
+
│ └── users.ts # Infos utilisateur
|
|
482
|
+
├── tests/ # Tests unitaires
|
|
483
|
+
├── package.json
|
|
484
|
+
├── tsconfig.json
|
|
485
|
+
├── jest.config.js
|
|
486
|
+
├── LICENSE
|
|
487
|
+
└── README*.md # READMEs en 5 langues
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
## Développement
|
|
491
|
+
|
|
492
|
+
```bash
|
|
493
|
+
npm install
|
|
494
|
+
npm run build # Compiler TypeScript → dist/
|
|
495
|
+
npm test # Exécuter les tests Jest
|
|
496
|
+
npm run lint # Vérification de types uniquement (tsc --noEmit)
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
## Licence
|
|
500
|
+
|
|
501
|
+
MIT — voir [LICENSE](LICENSE).
|