@lansenger-pm/openclaw-lansenger-channel 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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lansenger OpenClaw Channel
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,301 @@
1
+ [English](README.md) | [简体中文](README.zhHans.md) | [繁体中文](README.zhHant.md) | [繁体中文香港](README.zhHantHK.md) | [Français](README.fr.md)
2
+
3
+ # @lansenger/openclaw-lansenger-channel
4
+
5
+ > 💠 Plugin de canal Lansenger (蓝信) pour OpenClaw — WebSocket en entrée, HTTP API en sortie.
6
+
7
+ Connecte OpenClaw à Lansenger — une plateforme de messagerie d'entreprise — via une connexion longue WebSocket pour la réception de messages en temps réel et via l'API HTTP pour l'envoi de messages.
8
+
9
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
10
+ [![TypeScript](https://img.shields.io/badge/TypeScript-6.0-blue)](https://www.typescriptlang.org/)
11
+
12
+ ## Fonctionnalités
13
+
14
+ - **Messagerie en temps réel** via connexion longue WebSocket
15
+ - **Support multi-bot** — lier plusieurs bots Lansenger à différents agents OpenClaw
16
+ - **Support Markdown** utilisant le msgType `formatText` (par défaut)
17
+ - **Fichiers/Images/Vocaux** via le msgType `text` avec upload de médias
18
+ - **Cartes d'approbation** — workflow d'approbation interactif avec mises à jour de statut en place (en attente → approuvé/refusé)
19
+ - **Détection de langue** — détection automatique de la langue de l'utilisateur pour des réponses localisées
20
+ - **Routage des messages de groupe** — détection automatique et routage vers les API groupe/privé
21
+ - **@Mentions** — support @tout et @utilisateurs spécifiques dans les chats de groupe
22
+ - **Traitement des médias entrants** — téléchargement d'images/fichiers/vocaux, détection d'extension, chemins de fichiers pour l'agent
23
+ - **Révocation de messages** — révoquer les messages précédemment envoyés
24
+ - **Démarrage automatique** — la passerelle connecte automatiquement tous les comptes de bots configurés au démarrage
25
+ - **Zéro modification du core** — mode plugin pur, `git diff HEAD` reste INTACT
26
+
27
+ ## Matrice de capacités des types de messages
28
+
29
+ | msgType | Markdown | @mention | Pièces jointes |
30
+ |-------------|----------|----------|-----------------|
31
+ | `text` | ✗ | ✓ | ✓ |
32
+ | `formatText`| ✓ | ✗ | ✗ |
33
+
34
+ **Stratégie par défaut** : utiliser `formatText` en priorité pour les réponses Markdown. Revenir à `text` pour les pièces jointes.
35
+
36
+ ## Installation rapide
37
+
38
+ ### Via npm (recommandé)
39
+
40
+ ```bash
41
+ npm install -g @lansenger/openclaw-lansenger-channel
42
+ openclaw plugins enable lansenger
43
+ ```
44
+
45
+ ### Installation manuelle
46
+
47
+ ```bash
48
+ cd ~/.openclaw/npm
49
+ npm install @lansenger/openclaw-lansenger-channel
50
+ openclaw plugins enable lansenger
51
+ openclaw gateway restart
52
+ ```
53
+
54
+ ### Installation de développement (lien local)
55
+
56
+ ```bash
57
+ cd /path/to/openclaw-lansenger-channel
58
+ npm install
59
+ openclaw plugins install --link
60
+ openclaw plugins enable lansenger
61
+ openclaw gateway restart
62
+ ```
63
+
64
+ ## Configuration
65
+
66
+ ### Variables d'environnement requises
67
+
68
+ Ajoutez ces variables à `~/.openclaw/.env` ou à votre environnement :
69
+
70
+ | Variable | Description | Exemple |
71
+ |----------|-------------|---------|
72
+ | `LANSENGER_APP_ID` | App ID du bot personnel | `your-appid` |
73
+ | `LANSENGER_APP_SECRET` | App Secret du bot personnel | `57E718CA1CAC20F2...` |
74
+ | `LANSENGER_API_GATEWAY_URL` | URL de la passerelle API Lansenger (remplacement) | `https://open.e.lanxin.cn/open/apigw` |
75
+
76
+ ### Obtenir les identifiants
77
+
78
+ **Client Lansenger (desktop)** → **Contacts** → **Bots** → **Bots personnels** → cliquer sur l'icône **ℹ️**
79
+
80
+ > ⚠️ **Le client mobile ne permet pas de voir les identifiants.** Utilisez uniquement le client desktop.
81
+
82
+ ### Configuration optionnelle
83
+
84
+ ```json
85
+ {
86
+ "channels": {
87
+ "lansenger": {
88
+ "appId": "your-appid",
89
+ "appSecret": "your-secret",
90
+ "apiGatewayUrl": "https://open.e.lanxin.cn/open/apigw",
91
+ "homeChannel": "lansenger",
92
+ "enabled": true,
93
+ "allowFrom": ["your-appid"],
94
+ "dmSecurity": "allowlist",
95
+ "accounts": {
96
+ "your-appid": {
97
+ "appId": "your-appid",
98
+ "appSecret": "...",
99
+ "agentId": "main",
100
+ "apiGatewayUrl": "https://open.e.lanxin.cn/open/apigw"
101
+ }
102
+ }
103
+ }
104
+ }
105
+ }
106
+ ```
107
+
108
+ | Champ | Description | Valeur par défaut |
109
+ |-------|-------------|-------------------|
110
+ | `appId` | App ID du bot personnel | — |
111
+ | `appSecret` | App Secret du bot personnel | — |
112
+ | `apiGatewayUrl` | URL de la passerelle API | `https://open.e.lanxin.cn/open/apigw` |
113
+ | `homeChannel` | Canal par défaut pour le routage de l'agent | `lansenger` |
114
+ | `enabled` | Activer/désactiver le canal | `true` |
115
+ | `allowFrom` | IDs d'utilisateurs autorisés en DM | `[]` |
116
+ | `dmSecurity` | Politique DM : `allowlist`, `open`, `paired` | `allowlist` |
117
+ | `accounts` | Configuration multi-bot | — |
118
+
119
+ ### Configuration multi-bot
120
+
121
+ Chaque bot peut être lié à un agent OpenClaw différent :
122
+
123
+ ```json
124
+ {
125
+ "channels": {
126
+ "lansenger": {
127
+ "accounts": {
128
+ "bot1-appid": {
129
+ "appId": "your-appid",
130
+ "appSecret": "...",
131
+ "agentId": "main-agent",
132
+ "apiGatewayUrl": "https://open.e.lanxin.cn/open/apigw"
133
+ },
134
+ "bot2-appid": {
135
+ "appId": "your-other-appid",
136
+ "appSecret": "...",
137
+ "agentId": "test-agent"
138
+ }
139
+ }
140
+ }
141
+ },
142
+ "bindings": [
143
+ { "match": { "channel": "lansenger", "accountId": "bot1-appid" }, "agentId": "main-agent" }
144
+ ]
145
+ }
146
+ ```
147
+
148
+ ## Utilisation
149
+
150
+ La passerelle démarre automatiquement tous les comptes configurés au démarrage. La méthode `lansenger.start` est disponible pour démarrer dynamiquement des comptes supplémentaires.
151
+
152
+ ### Démarrer la passerelle (dynamique)
153
+
154
+ ```bash
155
+ openclaw gateway call lansenger.start
156
+ ```
157
+
158
+ ### Arrêter la passerelle
159
+
160
+ ```bash
161
+ openclaw gateway call lansenger.stop
162
+ ```
163
+
164
+ ### Vérifier le statut
165
+
166
+ ```bash
167
+ openclaw channels status
168
+ # ou
169
+ openclaw gateway call lansenger.status
170
+ ```
171
+
172
+ ### Lier un bot à un agent (dynamique)
173
+
174
+ ```bash
175
+ openclaw gateway call lansenger.bind '{"botId":"your-appid","agentId":"main"}'
176
+ ```
177
+
178
+ ### Liste des liaisons
179
+
180
+ ```bash
181
+ openclaw gateway call lansenger.bindings
182
+ ```
183
+
184
+ ### Délier un bot
185
+
186
+ ```bash
187
+ openclaw gateway call lansenger.unbind '{"botId":"your-appid"}'
188
+ ```
189
+
190
+ ## Types de messages supportés
191
+
192
+ | Type | Description | Méthode API | Direction |
193
+ |------|-------------|-------------|-----------|
194
+ | `text` | Texte brut avec @mentions et pièces jointes optionnelles | `sendText()` | Sortant |
195
+ | `formatText` | Texte au format Markdown (par défaut) | `sendFormatText()` | Sortant |
196
+ | `image` | Image avec légende optionnelle | `sendFile()` | Sortant |
197
+ | `file` | Tout fichier joint | `sendFile()` | Sortant |
198
+ | `video` | Vidéo jointe | `sendFile()` | Sortant |
199
+ | `voice` | Message vocal | `sendFile()` | Sortant |
200
+ | `linkCard` | Carte de prévisualisation de lien enrichi | `sendLinkCard()` | Sortant |
201
+ | `i18nAppCard` | Réservé pour usage futur ; carte 5 langues | `sendI18nAppCard()` | Sortant |
202
+ | `appCard` | Cartes d'approbation avec mises à jour de statut | `sendAppCard()` | Sortant |
203
+ | `appArticles` | Carte multi-articles | `sendAppArticles()` | Sortant |
204
+ | `position` | Message de localisation/position | — | Entrant uniquement |
205
+ | `card` | Message de carte générique | — | Entrant uniquement |
206
+ | `sticker` | Message sticker/emoji | — | Entrant uniquement |
207
+
208
+ ## Traitement des médias entrants
209
+
210
+ Lorsque les utilisateurs envoient des images, vidéos, fichiers ou messages vocaux, le plugin :
211
+
212
+ 1. Télécharge tous les `mediaIds` via l'API média Lansenger
213
+ 2. Détecte l'extension de fichier depuis les en-têtes Content-Type/Content-Disposition (repli : octets magiques)
214
+ 3. Enregistre dans des fichiers temporaires et attache les chemins à `InboundEvent.mediaPaths[]`
215
+ 4. Ajoute un indice dans le texte de l'agent : « Fichiers joints sauvegardés localement — utilisez l'outil de lecture pour visualiser »
216
+
217
+ ## Flux d'approbation
218
+
219
+ Le plugin supporte les cartes d'approbation :
220
+ - Les demandes d'approbation sont envoyées via **appCard** avec `isDynamic=true`
221
+ - Les mises à jour de statut (en attente → approuvé/refusé) mettent à jour la carte en place via **DynamicMsg**
222
+ - La langue détectée automatiquement détermine la langue de la carte (chinois ou anglais)
223
+ - **i18nAppCard** (5 langues) est réservé pour un usage futur et n'est pas utilisé pour l'approbation
224
+
225
+ ## Développement
226
+
227
+ ### Compilation
228
+
229
+ ```bash
230
+ npm install
231
+ npx tsc
232
+ ```
233
+
234
+ ### Tests
235
+
236
+ ```bash
237
+ npx vitest run
238
+ ```
239
+
240
+ ### Vérification de types
241
+
242
+ ```bash
243
+ npx tsc --noEmit
244
+ ```
245
+
246
+ ### Structure du projet
247
+
248
+ ```
249
+ openclaw-lansenger-channel/
250
+ ├── src/
251
+ │ ├── client.ts # Client API Lansenger (WS, HTTP, médias)
252
+ │ ├── channel.ts # Plugin de canal OpenClaw
253
+ │ ├── channel.test.ts # Tests du plugin de canal
254
+ │ ├── runtime.ts # Runtime passerelle (méthodes, handler entrant)
255
+ │ └── bindings.ts # Gestionnaire de liaisons multi-bot
256
+ ├── skills/
257
+ │ └── lansenger-messaging/
258
+ │ └── SKILL.md # Stratégie de messagerie de l'agent
259
+ ├── dist/ # JavaScript compilé
260
+ ├── index.ts # Point d'entrée du plugin
261
+ ├── setup-entry.ts # Point d'entrée de l'assistant de configuration
262
+ ├── openclaw.plugin.json # Métadonnées du plugin & configuration GUI
263
+ ├── package.json
264
+ └── tsconfig.json
265
+ ```
266
+
267
+ ## Dépannage
268
+
269
+ ### "Le client mobile ne permet pas de voir les identifiants"
270
+
271
+ Utilisez uniquement le **client Lansenger (desktop)**. L'application mobile n'affiche pas les identifiants du bot.
272
+
273
+ ### "No binding for botId"
274
+
275
+ Exécutez `lansenger.bind` pour lier le bot à un agent, ou configurez `agentId` dans la configuration du compte.
276
+
277
+ ### Déconnexions WebSocket
278
+
279
+ Le plugin inclut une reconnexion automatique avec un backoff exponentiel (2s, 5s, 10s, 30s, 60s) et un heartbeat (ping toutes les 30s).
280
+
281
+ ### formatText vs text
282
+
283
+ - Utilisez `formatText` pour les réponses Markdown (par défaut)
284
+ - Utilisez `text` pour les @mentions ou pièces jointes
285
+ - Pour les deux, envoyez deux messages distincts
286
+
287
+ ### Échec de mise à jour dynamique de carte
288
+
289
+ Les mises à jour de statut d'approbation utilisent le format DynamicMsg appCard. La méthode `updateCardStatus()` gère cela automatiquement.
290
+
291
+ ## Licence
292
+
293
+ MIT — voir [LICENSE](LICENSE).
294
+
295
+ ## Contribuer
296
+
297
+ 1. Fork le dépôt
298
+ 2. Créer une branche de fonctionnalité
299
+ 3. Effectuer vos modifications
300
+ 4. Exécuter les tests : `npx vitest run`
301
+ 5. Soumettre une pull request
package/README.md ADDED
@@ -0,0 +1,299 @@
1
+ [English](README.md) | [简体中文](README.zhHans.md) | [繁体中文](README.zhHant.md) | [繁体中文香港](README.zhHantHK.md) | [Français](README.fr.md)
2
+
3
+ # @lansenger/openclaw-lansenger-channel
4
+
5
+ Lansenger (蓝信) channel plugin for OpenClaw — WebSocket inbound, HTTP API outbound.
6
+
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
+ [![TypeScript](https://img.shields.io/badge/TypeScript-6.0-blue)](https://www.typescriptlang.org/)
9
+
10
+ ## Features
11
+
12
+ - **Real-time messaging** via WebSocket long-connection
13
+ - **Multi-bot support** — bind multiple Lansenger bots to different OpenClaw agents
14
+ - **Markdown support** using `formatText` msgType (default)
15
+ - **File/Image/Voice attachments** via `text` msgType with media upload
16
+ - **Approval cards** — interactive approval workflow with in-place status updates (pending → approved/denied)
17
+ - **Language detection** — auto-detect user language from messages for localized responses
18
+ - **Group message routing** — auto-detect and route to group/private chat APIs
19
+ - **@Mentions** — support @all and @specific users in group chats
20
+ - **Inbound media processing** — download images/files/voice, detect extension, provide file paths to agent
21
+ - **Message revocation** — revoke previously sent messages
22
+ - **Auto-start** — gateway automatically connects all configured bot accounts on boot
23
+ - **Zero core modification** — pure plugin mode, `git diff HEAD` stays PRISTINE
24
+
25
+ ## Message Type Capability Matrix
26
+
27
+ | msgType | Markdown | @mention | Attachments |
28
+ |-------------|----------|----------|-------------|
29
+ | `text` | ✗ | ✓ | ✓ |
30
+ | `formatText`| ✓ | ✗ | ✗ |
31
+
32
+ **Default strategy**: Use `formatText` first for Markdown replies. Fall back to `text` for attachments.
33
+
34
+ ## Quick Install
35
+
36
+ ### Via npm (recommended)
37
+
38
+ ```bash
39
+ npm install -g @lansenger/openclaw-lansenger-channel
40
+ openclaw plugins enable lansenger
41
+ ```
42
+
43
+ ### Manual install
44
+
45
+ ```bash
46
+ cd ~/.openclaw/npm
47
+ npm install @lansenger/openclaw-lansenger-channel
48
+ openclaw plugins enable lansenger
49
+ openclaw gateway restart
50
+ ```
51
+
52
+ ### Development install (linked)
53
+
54
+ ```bash
55
+ cd /path/to/openclaw-lansenger-channel
56
+ npm install
57
+ openclaw plugins install --link
58
+ openclaw plugins enable lansenger
59
+ openclaw gateway restart
60
+ ```
61
+
62
+ ## Configuration
63
+
64
+ ### Required Environment Variables
65
+
66
+ Add these to `~/.openclaw/.env` or your environment:
67
+
68
+ | Variable | Description | Example |
69
+ |----------|-------------|---------|
70
+ | `LANSENGER_APP_ID` | Personal bot App ID | `your-appid` |
71
+ | `LANSENGER_APP_SECRET` | Personal bot App Secret | `57E718CA1CAC20F2...` |
72
+ | `LANSENGER_API_GATEWAY_URL` | Lansenger API Gateway URL override | `https://open.e.lanxin.cn/open/apigw` |
73
+
74
+ ### Get Credentials
75
+
76
+ **Lansenger Desktop** → **Contacts** → **Bots** → **Personal Bots** → click **ℹ️** icon
77
+
78
+ > ⚠️ **Mobile client does NOT support viewing credentials.** Use the desktop client only.
79
+
80
+ ### Optional Configuration
81
+
82
+ ```json
83
+ {
84
+ "channels": {
85
+ "lansenger": {
86
+ "appId": "your-appid",
87
+ "appSecret": "your-secret",
88
+ "apiGatewayUrl": "https://open.e.lanxin.cn/open/apigw",
89
+ "homeChannel": "lansenger",
90
+ "enabled": true,
91
+ "allowFrom": ["your-appid"],
92
+ "dmSecurity": "allowlist",
93
+ "accounts": {
94
+ "your-appid": {
95
+ "appId": "your-appid",
96
+ "appSecret": "...",
97
+ "agentId": "main",
98
+ "apiGatewayUrl": "https://open.e.lanxin.cn/open/apigw"
99
+ }
100
+ }
101
+ }
102
+ }
103
+ }
104
+ ```
105
+
106
+ | Field | Description | Default |
107
+ |-------|-------------|---------|
108
+ | `appId` | Personal bot App ID | — |
109
+ | `appSecret` | Personal bot App Secret | — |
110
+ | `apiGatewayUrl` | API Gateway URL | `https://open.e.lanxin.cn/open/apigw` |
111
+ | `homeChannel` | Default channel for agent routing | `lansenger` |
112
+ | `enabled` | Enable/disable the channel | `true` |
113
+ | `allowFrom` | User IDs allowed to DM the bot | `[]` |
114
+ | `dmSecurity` | DM policy: `allowlist`, `open`, `paired` | `allowlist` |
115
+ | `accounts` | Multi-bot configuration | — |
116
+
117
+ ### Multi-Bot Configuration
118
+
119
+ Each bot can be bound to a different OpenClaw agent:
120
+
121
+ ```json
122
+ {
123
+ "channels": {
124
+ "lansenger": {
125
+ "accounts": {
126
+ "bot1-appid": {
127
+ "appId": "your-appid",
128
+ "appSecret": "...",
129
+ "agentId": "main-agent",
130
+ "apiGatewayUrl": "https://open.e.lanxin.cn/open/apigw"
131
+ },
132
+ "bot2-appid": {
133
+ "appId": "your-other-appid",
134
+ "appSecret": "...",
135
+ "agentId": "test-agent"
136
+ }
137
+ }
138
+ }
139
+ },
140
+ "bindings": [
141
+ { "match": { "channel": "lansenger", "accountId": "bot1-appid" }, "agentId": "main-agent" }
142
+ ]
143
+ }
144
+ ```
145
+
146
+ ## Usage
147
+
148
+ The gateway auto-starts all configured accounts on boot. The `lansenger.start` method is available for dynamic start of additional accounts.
149
+
150
+ ### Start the gateway (dynamic)
151
+
152
+ ```bash
153
+ openclaw gateway call lansenger.start
154
+ ```
155
+
156
+ ### Stop the gateway
157
+
158
+ ```bash
159
+ openclaw gateway call lansenger.stop
160
+ ```
161
+
162
+ ### Check status
163
+
164
+ ```bash
165
+ openclaw channels status
166
+ # or
167
+ openclaw gateway call lansenger.status
168
+ ```
169
+
170
+ ### Bind a bot to an agent (dynamic)
171
+
172
+ ```bash
173
+ openclaw gateway call lansenger.bind '{"botId":"your-appid","agentId":"main"}'
174
+ ```
175
+
176
+ ### List bindings
177
+
178
+ ```bash
179
+ openclaw gateway call lansenger.bindings
180
+ ```
181
+
182
+ ### Unbind a bot
183
+
184
+ ```bash
185
+ openclaw gateway call lansenger.unbind '{"botId":"your-appid"}'
186
+ ```
187
+
188
+ ## Supported Message Types
189
+
190
+ | Type | Description | API Method | Direction |
191
+ |------|-------------|------------|-----------|
192
+ | `text` | Plain text with optional @mentions and attachments | `sendText()` | Outbound |
193
+ | `formatText` | Markdown-formatted text (default) | `sendFormatText()` | Outbound |
194
+ | `image` | Image with optional caption | `sendFile()` | Outbound |
195
+ | `file` | Any file attachment | `sendFile()` | Outbound |
196
+ | `video` | Video attachment | `sendFile()` | Outbound |
197
+ | `voice` | Voice message | `sendFile()` | Outbound |
198
+ | `linkCard` | Rich link preview card | `sendLinkCard()` | Outbound |
199
+ | `i18nAppCard` | Reserved for future use; 5-language card | `sendI18nAppCard()` | Outbound |
200
+ | `appCard` | Approval cards with status updates | `sendAppCard()` | Outbound |
201
+ | `appArticles` | Multi-article card | `sendAppArticles()` | Outbound |
202
+ | `position` | Location/position message | — | Inbound-only |
203
+ | `card` | Generic card message | — | Inbound-only |
204
+ | `sticker` | Sticker/emoji message | — | Inbound-only |
205
+
206
+ ## Inbound Media Handling
207
+
208
+ When users send images, videos, files, or voice messages, the plugin:
209
+
210
+ 1. Downloads all `mediaIds` via the Lansenger media API
211
+ 2. Detects file extension from Content-Type/Content-Disposition headers (fallback: magic bytes)
212
+ 3. Saves to temp files and attaches paths to `InboundEvent.mediaPaths[]`
213
+ 4. Adds a hint in agent text: "Attached files saved locally — use the read tool to view"
214
+
215
+ ## Approval Workflow
216
+
217
+ The plugin supports approval workflow cards:
218
+ - Approval requests are sent as **appCard** with `isDynamic=true`
219
+ - Status updates (pending → approved/denied) update the card in-place via **DynamicMsg**
220
+ - Language detection: the card is sent in the user's detected language (Chinese or English)
221
+ - **i18nAppCard** (5-language) is reserved for future use but not currently used for approval
222
+
223
+ ## Development
224
+
225
+ ### Build
226
+
227
+ ```bash
228
+ npm install
229
+ npx tsc
230
+ ```
231
+
232
+ ### Test
233
+
234
+ ```bash
235
+ npx vitest run
236
+ ```
237
+
238
+ ### Typecheck
239
+
240
+ ```bash
241
+ npx tsc --noEmit
242
+ ```
243
+
244
+ ### Project Structure
245
+
246
+ ```
247
+ openclaw-lansenger-channel/
248
+ ├── src/
249
+ │ ├── client.ts # Lansenger API client (WS, HTTP, media)
250
+ │ ├── channel.ts # OpenClaw channel plugin
251
+ │ ├── channel.test.ts # Channel plugin tests
252
+ │ ├── runtime.ts # Gateway runtime (methods, inbound handler)
253
+ │ └── bindings.ts # Multi-bot binding manager
254
+ ├── skills/
255
+ │ └── lansenger-messaging/
256
+ │ └── SKILL.md # Agent messaging strategy
257
+ ├── dist/ # Compiled JavaScript
258
+ ├── index.ts # Plugin entry point
259
+ ├── setup-entry.ts # Setup wizard entry
260
+ ├── openclaw.plugin.json # Plugin metadata & GUI config
261
+ ├── package.json
262
+ └── tsconfig.json
263
+ ```
264
+
265
+ ## Troubleshooting
266
+
267
+ ### "Mobile client does NOT support viewing credentials"
268
+
269
+ Use the **Lansenger Desktop** client only. The mobile app does not display bot credentials.
270
+
271
+ ### "No binding for botId"
272
+
273
+ Run `lansenger.bind` to bind the bot to an agent, or configure `agentId` in the account config.
274
+
275
+ ### WebSocket disconnects
276
+
277
+ The plugin includes automatic reconnection with exponential backoff (2s, 5s, 10s, 30s, 60s) and heartbeat (ping every 30s).
278
+
279
+ ### formatText vs text
280
+
281
+ - Use `formatText` for Markdown replies (default)
282
+ - Use `text` for @mentions or attachments
283
+ - For both, send two separate messages
284
+
285
+ ### Dynamic card update fails
286
+
287
+ Approval status updates use the DynamicMsg appCard format. The `updateCardStatus()` method handles this automatically.
288
+
289
+ ## License
290
+
291
+ MIT — see [LICENSE](LICENSE).
292
+
293
+ ## Contributing
294
+
295
+ 1. Fork the repository
296
+ 2. Create a feature branch
297
+ 3. Make your changes
298
+ 4. Run tests: `npx vitest run`
299
+ 5. Submit a pull request