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.
Files changed (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.fr.md +501 -0
  3. package/README.md +504 -0
  4. package/README.zhHans.md +501 -0
  5. package/README.zhHant.md +501 -0
  6. package/README.zhHantHK.md +501 -0
  7. package/dist/accountMessages.d.ts +12 -0
  8. package/dist/accountMessages.js +41 -0
  9. package/dist/auth.d.ts +13 -0
  10. package/dist/auth.js +70 -0
  11. package/dist/calendars.d.ts +84 -0
  12. package/dist/calendars.js +278 -0
  13. package/dist/callbacks.d.ts +384 -0
  14. package/dist/callbacks.js +712 -0
  15. package/dist/chats.d.ts +22 -0
  16. package/dist/chats.js +88 -0
  17. package/dist/client.d.ts +439 -0
  18. package/dist/client.js +712 -0
  19. package/dist/config.d.ts +14 -0
  20. package/dist/config.js +42 -0
  21. package/dist/constants.d.ts +30 -0
  22. package/dist/constants.js +187 -0
  23. package/dist/contacts.d.ts +38 -0
  24. package/dist/contacts.js +161 -0
  25. package/dist/departments.d.ts +18 -0
  26. package/dist/departments.js +69 -0
  27. package/dist/exceptions.d.ts +20 -0
  28. package/dist/exceptions.js +42 -0
  29. package/dist/groupMessages.d.ts +11 -0
  30. package/dist/groupMessages.js +39 -0
  31. package/dist/groups.d.ts +66 -0
  32. package/dist/groups.js +218 -0
  33. package/dist/http.d.ts +7 -0
  34. package/dist/http.js +67 -0
  35. package/dist/index.d.ts +24 -0
  36. package/dist/index.js +189 -0
  37. package/dist/media.d.ts +16 -0
  38. package/dist/media.js +178 -0
  39. package/dist/models.d.ts +925 -0
  40. package/dist/models.js +991 -0
  41. package/dist/oauth.d.ts +17 -0
  42. package/dist/oauth.js +107 -0
  43. package/dist/persistence.d.ts +26 -0
  44. package/dist/persistence.js +210 -0
  45. package/dist/reminders.d.ts +10 -0
  46. package/dist/reminders.js +31 -0
  47. package/dist/streaming.d.ts +9 -0
  48. package/dist/streaming.js +40 -0
  49. package/dist/todos.d.ts +75 -0
  50. package/dist/todos.js +282 -0
  51. package/dist/urlHelpers.d.ts +7 -0
  52. package/dist/urlHelpers.js +22 -0
  53. package/dist/userMessages.d.ts +8 -0
  54. package/dist/userMessages.js +34 -0
  55. package/dist/users.d.ts +6 -0
  56. package/dist/users.js +32 -0
  57. 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
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
+ [![TypeScript 5+](https://img.shields.io/badge/TypeScript-5%2B-blue)](https://www.typescriptlang.org/)
9
+ [![Node 18+](https://img.shields.io/badge/Node-18%2B-green)](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).