@lansenger-pm/openclaw-lansenger-channel 2.8.2 → 2.8.4
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/README.fr.md +15 -66
- package/README.md +14 -14
- package/README.zhHans.md +14 -14
- package/README.zhHant.md +14 -14
- package/README.zhHantHK.md +15 -66
- package/dist/index.js +3 -3
- package/dist/src/channel.js +18 -18
- package/dist/src/channel.test.js +37 -37
- package/dist/src/runtime.js +8 -8
- package/dist/src/tools.js +1 -1
- package/index.ts +3 -3
- package/openclaw.plugin.json +15 -15
- package/package.json +2 -2
- package/skills/lansenger-messaging/SKILL.md +1 -1
package/README.fr.md
CHANGED
|
@@ -33,7 +33,7 @@ Connecte OpenClaw à Lansenger — une plateforme de messagerie d'entreprise —
|
|
|
33
33
|
|
|
34
34
|
**Stratégie par défaut** : utiliser `formatText` en priorité pour les réponses Markdown. Revenir à `text` pour les pièces jointes. Les deux types supportent @mention via le paramètre `reminder` — inclure « @姓名 » dans le texte pour les mentions.
|
|
35
35
|
|
|
36
|
-
## Outils de l'agent
|
|
36
|
+
## Outils de l'agent
|
|
37
37
|
|
|
38
38
|
| Outil | Description |
|
|
39
39
|
|-------|-------------|
|
|
@@ -56,18 +56,18 @@ Connecte OpenClaw à Lansenger — une plateforme de messagerie d'entreprise —
|
|
|
56
56
|
openclaw plugins install @lansenger-pm/openclaw-lansenger-channel
|
|
57
57
|
|
|
58
58
|
# 2. Activer le plugin (si non auto-activé)
|
|
59
|
-
openclaw config set plugins.entries.
|
|
59
|
+
openclaw config set plugins.entries.lansenger.enabled true
|
|
60
60
|
|
|
61
61
|
# 3. Configurer le canal (assistant interactif)
|
|
62
|
-
openclaw channels add --channel
|
|
62
|
+
openclaw channels add --channel lansenger
|
|
63
63
|
# OU non-interactif :
|
|
64
|
-
openclaw channels add --channel
|
|
64
|
+
openclaw channels add --channel lansenger --token "appId:appSecret"
|
|
65
65
|
|
|
66
66
|
# 4. Redémarrer la passerelle
|
|
67
67
|
openclaw gateway restart
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
-
Les métadonnées `openclaw.install` dans `package.json` (`npmSpec`, `localPath`, `defaultChoice`) permettent l'**installation à la demande** : si un utilisateur exécute `openclaw channels add --channel
|
|
70
|
+
Les métadonnées `openclaw.install` dans `package.json` (`npmSpec`, `localPath`, `defaultChoice`) permettent l'**installation à la demande** : si un utilisateur exécute `openclaw channels add --channel lansenger` avant que le plugin soit installé, OpenClaw peut l'installer automatiquement.
|
|
71
71
|
|
|
72
72
|
> **Passerelle personnalisée** : pour les déploiements entreprise (ex. 奇安信), configurez `apiGatewayUrl` dans `openclaw.json` ou via les variables d'environnement après la configuration — voir [Configuration optionnelle](#configuration-optionnelle).
|
|
73
73
|
|
|
@@ -91,58 +91,7 @@ openclaw gateway restart
|
|
|
91
91
|
Après le redémarrage, le bot se connecte automatiquement via WebSocket. Envoyez un DM au bot — vous recevrez un code de pairage. Approuvez-le :
|
|
92
92
|
|
|
93
93
|
```bash
|
|
94
|
-
openclaw pairing approve
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
> ⚠️ Step 2 is required because `openclaw channels add` only discovers plugins in the `extensions/` directory, not from npm-installed packages. This is an [OpenClaw upstream bug](https://docs.openclaw.ai), not a plugin issue.
|
|
98
|
-
|
|
99
|
-
### Via npm
|
|
100
|
-
|
|
101
|
-
```bash
|
|
102
|
-
# First install the npm package manually, then configure via CLI
|
|
103
|
-
npm install -g @lansenger-pm/openclaw-lansenger-channel
|
|
104
|
-
openclaw channels add --channel Lansenger --app-token "your-appid" --secret "your-appsecret"
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
### Installation de développement (lien local)
|
|
108
|
-
|
|
109
|
-
```bash
|
|
110
|
-
cd /path/to/openclaw-lansenger-channel
|
|
111
|
-
npm install
|
|
112
|
-
openclaw plugins install --link
|
|
113
|
-
openclaw gateway restart
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
## Configuration rapide
|
|
117
|
-
|
|
118
|
-
Après l'installation, configurez les identifiants :
|
|
119
|
-
|
|
120
|
-
> **Compte unique** : `channels add` crée un seul compte. Pour plusieurs bots, voir [Configuration multi-bot](#configuration-multi-bot) ci-dessous.
|
|
121
|
-
|
|
122
|
-
```bash
|
|
123
|
-
# Standard (utilise la passerelle par défaut https://open.e.lanxin.cn/open/apigw)
|
|
124
|
-
openclaw channels add --channel Lansenger \
|
|
125
|
-
--app-token "your-appid" \
|
|
126
|
-
--secret "your-appsecret"
|
|
127
|
-
|
|
128
|
-
# Déploiement entreprise (URL de passerelle personnalisée)
|
|
129
|
-
openclaw channels add --channel Lansenger \
|
|
130
|
-
--app-token "your-appid" \
|
|
131
|
-
--secret "your-appsecret" \
|
|
132
|
-
--base-url "https://apigw.lx.qianxin.com"
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
Puis redémarrez :
|
|
136
|
-
```bash
|
|
137
|
-
openclaw gateway restart
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
Obtenir les identifiants : **Lansenger Desktop** → **Contacts** → **Bots** → **Personal Bots** → cliquer sur l'icône **ℹ️** (le client mobile ne permet pas de voir les identifiants).
|
|
141
|
-
|
|
142
|
-
Après le redémarrage, le bot se connecte automatiquement via WebSocket. Envoyez un DM au bot — vous recevrez un code de pairage. Approuvez-le :
|
|
143
|
-
|
|
144
|
-
```bash
|
|
145
|
-
openclaw pairing approve Lansenger <code>
|
|
94
|
+
openclaw pairing approve lansenger <code>
|
|
146
95
|
```
|
|
147
96
|
|
|
148
97
|
## Configuration
|
|
@@ -168,11 +117,11 @@ Ajoutez ces variables à `~/.openclaw/.env` ou à votre environnement :
|
|
|
168
117
|
```json
|
|
169
118
|
{
|
|
170
119
|
"channels": {
|
|
171
|
-
"
|
|
120
|
+
"lansenger": {
|
|
172
121
|
"appId": "your-appid",
|
|
173
122
|
"appSecret": "your-secret",
|
|
174
123
|
"apiGatewayUrl": "https://open.e.lanxin.cn/open/apigw",
|
|
175
|
-
"homeChannel": "
|
|
124
|
+
"homeChannel": "lansenger",
|
|
176
125
|
"enabled": true,
|
|
177
126
|
"allowFrom": ["your-appid"],
|
|
178
127
|
"dmSecurity": "paired",
|
|
@@ -208,9 +157,9 @@ Pour ajouter plusieurs bots, utilisez `openclaw config set` avec la structure `a
|
|
|
208
157
|
|
|
209
158
|
```bash
|
|
210
159
|
# Ajouter un deuxième bot (remplacez appid/appsecret/gateway par vos valeurs)
|
|
211
|
-
openclaw config set channels.
|
|
212
|
-
openclaw config set channels.
|
|
213
|
-
openclaw config set channels.
|
|
160
|
+
openclaw config set channels.lansenger.accounts.your-appid-2.appId "your-appid-2"
|
|
161
|
+
openclaw config set channels.lansenger.accounts.your-appid-2.appSecret "your-appsecret"
|
|
162
|
+
openclaw config set channels.lansenger.accounts.your-appid-2.apiGatewayUrl "https://apigw.lx.qianxin.com"
|
|
214
163
|
|
|
215
164
|
# Redémarrer pour appliquer
|
|
216
165
|
openclaw gateway restart
|
|
@@ -221,7 +170,7 @@ Structure de configuration résultante :
|
|
|
221
170
|
```json
|
|
222
171
|
{
|
|
223
172
|
"channels": {
|
|
224
|
-
"
|
|
173
|
+
"lansenger": {
|
|
225
174
|
"appId": "your-appid-2",
|
|
226
175
|
"appSecret": "...",
|
|
227
176
|
"dmSecurity": "paired",
|
|
@@ -282,14 +231,14 @@ Utilisez `bindings` pour router les DM Lansenger ou les conversations de groupe
|
|
|
282
231
|
{
|
|
283
232
|
agentId: "agent-a",
|
|
284
233
|
match: {
|
|
285
|
-
channel: "
|
|
234
|
+
channel: "lansenger",
|
|
286
235
|
peer: { kind: "direct", id: "2285568-xxx" },
|
|
287
236
|
},
|
|
288
237
|
},
|
|
289
238
|
{
|
|
290
239
|
agentId: "agent-a",
|
|
291
240
|
match: {
|
|
292
|
-
channel: "
|
|
241
|
+
channel: "lansenger",
|
|
293
242
|
peer: { kind: "group", id: "group-chat-id" },
|
|
294
243
|
},
|
|
295
244
|
},
|
|
@@ -298,7 +247,7 @@ Utilisez `bindings` pour router les DM Lansenger ou les conversations de groupe
|
|
|
298
247
|
```
|
|
299
248
|
|
|
300
249
|
Champs de routage :
|
|
301
|
-
* `match.channel`: `"
|
|
250
|
+
* `match.channel`: `"lansenger"`
|
|
302
251
|
* `match.peer.kind`: `"direct"` (DM) ou `"group"` (chat de groupe)
|
|
303
252
|
* `match.peer.id`: ID utilisateur (`2285568-xxx`) ou ID de chat de groupe
|
|
304
253
|
|
package/README.md
CHANGED
|
@@ -54,18 +54,18 @@ Lansenger (蓝信) channel plugin for OpenClaw — WebSocket inbound, HTTP API o
|
|
|
54
54
|
openclaw plugins install @lansenger-pm/openclaw-lansenger-channel
|
|
55
55
|
|
|
56
56
|
# 2. Enable the plugin (if not auto-enabled)
|
|
57
|
-
openclaw config set plugins.entries.
|
|
57
|
+
openclaw config set plugins.entries.lansenger.enabled true
|
|
58
58
|
|
|
59
59
|
# 3. Configure the channel (interactive wizard)
|
|
60
|
-
openclaw channels add --channel
|
|
60
|
+
openclaw channels add --channel lansenger
|
|
61
61
|
# OR non-interactive:
|
|
62
|
-
openclaw channels add --channel
|
|
62
|
+
openclaw channels add --channel lansenger --token "appId:appSecret"
|
|
63
63
|
|
|
64
64
|
# 4. Restart the gateway
|
|
65
65
|
openclaw gateway restart
|
|
66
66
|
```
|
|
67
67
|
|
|
68
|
-
The `openclaw.install` metadata in `package.json` (`npmSpec`, `localPath`, `defaultChoice`) enables **install-on-demand**: if a user runs `openclaw channels add --channel
|
|
68
|
+
The `openclaw.install` metadata in `package.json` (`npmSpec`, `localPath`, `defaultChoice`) enables **install-on-demand**: if a user runs `openclaw channels add --channel lansenger` before the plugin is installed, OpenClaw can automatically install it using this metadata.
|
|
69
69
|
|
|
70
70
|
> **Custom gateway**: For enterprise deployments (e.g. 奇安信), set `apiGatewayUrl` in `openclaw.json` or environment after configuration — see [Optional Configuration](#optional-configuration).
|
|
71
71
|
|
|
@@ -89,7 +89,7 @@ openclaw gateway restart
|
|
|
89
89
|
The bot auto-connects via WebSocket on gateway restart. Send a DM to the bot — you'll receive a pairing code. Approve it:
|
|
90
90
|
|
|
91
91
|
```bash
|
|
92
|
-
openclaw pairing approve
|
|
92
|
+
openclaw pairing approve lansenger <code>
|
|
93
93
|
```
|
|
94
94
|
|
|
95
95
|
## Configuration
|
|
@@ -115,11 +115,11 @@ Add these to `~/.openclaw/.env` or your environment:
|
|
|
115
115
|
```json
|
|
116
116
|
{
|
|
117
117
|
"channels": {
|
|
118
|
-
"
|
|
118
|
+
"lansenger": {
|
|
119
119
|
"appId": "your-appid",
|
|
120
120
|
"appSecret": "your-secret",
|
|
121
121
|
"apiGatewayUrl": "https://open.e.lanxin.cn/open/apigw",
|
|
122
|
-
"homeChannel": "
|
|
122
|
+
"homeChannel": "lansenger",
|
|
123
123
|
"enabled": true,
|
|
124
124
|
"allowFrom": ["your-appid"],
|
|
125
125
|
"dmSecurity": "paired",
|
|
@@ -155,9 +155,9 @@ For multiple bots, add additional accounts using `openclaw config set`:
|
|
|
155
155
|
|
|
156
156
|
```bash
|
|
157
157
|
# Add a second bot (replace appid/appsecret/gateway with your values)
|
|
158
|
-
openclaw config set channels.
|
|
159
|
-
openclaw config set channels.
|
|
160
|
-
openclaw config set channels.
|
|
158
|
+
openclaw config set channels.lansenger.accounts.your-appid-2.appId "your-appid-2"
|
|
159
|
+
openclaw config set channels.lansenger.accounts.your-appid-2.appSecret "your-appsecret"
|
|
160
|
+
openclaw config set channels.lansenger.accounts.your-appid-2.apiGatewayUrl "https://apigw.lx.qianxin.com"
|
|
161
161
|
|
|
162
162
|
# Restart to apply
|
|
163
163
|
openclaw gateway restart
|
|
@@ -168,7 +168,7 @@ The resulting config structure:
|
|
|
168
168
|
```json
|
|
169
169
|
{
|
|
170
170
|
"channels": {
|
|
171
|
-
"
|
|
171
|
+
"lansenger": {
|
|
172
172
|
"appId": "your-appid-2",
|
|
173
173
|
"appSecret": "...",
|
|
174
174
|
"dmSecurity": "paired",
|
|
@@ -229,14 +229,14 @@ Use `bindings` to route Lansenger DMs or groups to different agents (same patter
|
|
|
229
229
|
{
|
|
230
230
|
agentId: "agent-a",
|
|
231
231
|
match: {
|
|
232
|
-
channel: "
|
|
232
|
+
channel: "lansenger",
|
|
233
233
|
peer: { kind: "direct", id: "2285568-xxx" },
|
|
234
234
|
},
|
|
235
235
|
},
|
|
236
236
|
{
|
|
237
237
|
agentId: "agent-a",
|
|
238
238
|
match: {
|
|
239
|
-
channel: "
|
|
239
|
+
channel: "lansenger",
|
|
240
240
|
peer: { kind: "group", id: "group-chat-id" },
|
|
241
241
|
},
|
|
242
242
|
},
|
|
@@ -245,7 +245,7 @@ Use `bindings` to route Lansenger DMs or groups to different agents (same patter
|
|
|
245
245
|
```
|
|
246
246
|
|
|
247
247
|
Routing fields:
|
|
248
|
-
* `match.channel`: `"
|
|
248
|
+
* `match.channel`: `"lansenger"`
|
|
249
249
|
* `match.peer.kind`: `"direct"` (DM) or `"group"` (group chat)
|
|
250
250
|
* `match.peer.id`: user ID (`2285568-xxx`) or group chat ID
|
|
251
251
|
|
package/README.zhHans.md
CHANGED
|
@@ -56,18 +56,18 @@
|
|
|
56
56
|
openclaw plugins install @lansenger-pm/openclaw-lansenger-channel
|
|
57
57
|
|
|
58
58
|
# 2. 启用插件(如未自动启用)
|
|
59
|
-
openclaw config set plugins.entries.
|
|
59
|
+
openclaw config set plugins.entries.lansenger.enabled true
|
|
60
60
|
|
|
61
61
|
# 3. 配置频道(交互式向导)
|
|
62
|
-
openclaw channels add --channel
|
|
62
|
+
openclaw channels add --channel lansenger
|
|
63
63
|
# 或非交互式:
|
|
64
|
-
openclaw channels add --channel
|
|
64
|
+
openclaw channels add --channel lansenger --token "appId:appSecret"
|
|
65
65
|
|
|
66
66
|
# 4. 重启网关
|
|
67
67
|
openclaw gateway restart
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
-
`package.json` 中的 `openclaw.install` 元数据(`npmSpec`、`localPath`、`defaultChoice`)支持**按需安装**:如果用户在插件安装前运行 `openclaw channels add --channel
|
|
70
|
+
`package.json` 中的 `openclaw.install` 元数据(`npmSpec`、`localPath`、`defaultChoice`)支持**按需安装**:如果用户在插件安装前运行 `openclaw channels add --channel lansenger`,OpenClaw 可自动安装该插件。
|
|
71
71
|
|
|
72
72
|
> **自定义网关**:企业私有化部署(如奇安信)需在配置后通过 `openclaw.json` 或环境变量设置 `apiGatewayUrl` — 见[可选配置](#可选配置)。
|
|
73
73
|
|
|
@@ -91,7 +91,7 @@ openclaw gateway restart
|
|
|
91
91
|
重启后机器人自动通过 WebSocket 连接。给机器人发私聊消息,会收到配对码,审批配对:
|
|
92
92
|
|
|
93
93
|
```bash
|
|
94
|
-
openclaw pairing approve
|
|
94
|
+
openclaw pairing approve lansenger <配对码>
|
|
95
95
|
```
|
|
96
96
|
|
|
97
97
|
## 配置
|
|
@@ -117,11 +117,11 @@ openclaw pairing approve Lansenger <配对码>
|
|
|
117
117
|
```json
|
|
118
118
|
{
|
|
119
119
|
"channels": {
|
|
120
|
-
"
|
|
120
|
+
"lansenger": {
|
|
121
121
|
"appId": "your-appid",
|
|
122
122
|
"appSecret": "your-secret",
|
|
123
123
|
"apiGatewayUrl": "https://open.e.lanxin.cn/open/apigw",
|
|
124
|
-
"homeChannel": "
|
|
124
|
+
"homeChannel": "lansenger",
|
|
125
125
|
"enabled": true,
|
|
126
126
|
"allowFrom": ["your-appid"],
|
|
127
127
|
"dmSecurity": "paired",
|
|
@@ -157,9 +157,9 @@ openclaw pairing approve Lansenger <配对码>
|
|
|
157
157
|
|
|
158
158
|
```bash
|
|
159
159
|
# 添加第二个机器人(替换 appid/appsecret/gateway 为你的值)
|
|
160
|
-
openclaw config set channels.
|
|
161
|
-
openclaw config set channels.
|
|
162
|
-
openclaw config set channels.
|
|
160
|
+
openclaw config set channels.lansenger.accounts.your-appid-2.appId "your-appid-2"
|
|
161
|
+
openclaw config set channels.lansenger.accounts.your-appid-2.appSecret "your-appsecret"
|
|
162
|
+
openclaw config set channels.lansenger.accounts.your-appid-2.apiGatewayUrl "https://apigw.lx.qianxin.com"
|
|
163
163
|
|
|
164
164
|
# 重启生效
|
|
165
165
|
openclaw gateway restart
|
|
@@ -170,7 +170,7 @@ openclaw gateway restart
|
|
|
170
170
|
```json
|
|
171
171
|
{
|
|
172
172
|
"channels": {
|
|
173
|
-
"
|
|
173
|
+
"lansenger": {
|
|
174
174
|
"appId": "your-appid-2",
|
|
175
175
|
"appSecret": "...",
|
|
176
176
|
"dmSecurity": "paired",
|
|
@@ -231,14 +231,14 @@ openclaw gateway call lansenger.status
|
|
|
231
231
|
{
|
|
232
232
|
agentId: "agent-a",
|
|
233
233
|
match: {
|
|
234
|
-
channel: "
|
|
234
|
+
channel: "lansenger",
|
|
235
235
|
peer: { kind: "direct", id: "2285568-xxx" },
|
|
236
236
|
},
|
|
237
237
|
},
|
|
238
238
|
{
|
|
239
239
|
agentId: "agent-a",
|
|
240
240
|
match: {
|
|
241
|
-
channel: "
|
|
241
|
+
channel: "lansenger",
|
|
242
242
|
peer: { kind: "group", id: "group-chat-id" },
|
|
243
243
|
},
|
|
244
244
|
},
|
|
@@ -247,7 +247,7 @@ openclaw gateway call lansenger.status
|
|
|
247
247
|
```
|
|
248
248
|
|
|
249
249
|
路由字段:
|
|
250
|
-
* `match.channel`: `"
|
|
250
|
+
* `match.channel`: `"lansenger"`
|
|
251
251
|
* `match.peer.kind`: `"direct"`(私聊)或 `"group"`(群聊)
|
|
252
252
|
* `match.peer.id`: 用户 ID(`2285568-xxx`)或群聊 ID
|
|
253
253
|
|
package/README.zhHant.md
CHANGED
|
@@ -56,18 +56,18 @@
|
|
|
56
56
|
openclaw plugins install @lansenger-pm/openclaw-lansenger-channel
|
|
57
57
|
|
|
58
58
|
# 2. 啟用插件(如未自動啟用)
|
|
59
|
-
openclaw config set plugins.entries.
|
|
59
|
+
openclaw config set plugins.entries.lansenger.enabled true
|
|
60
60
|
|
|
61
61
|
# 3. 配置頻道(互動式向導)
|
|
62
|
-
openclaw channels add --channel
|
|
62
|
+
openclaw channels add --channel lansenger
|
|
63
63
|
# 或非互動式:
|
|
64
|
-
openclaw channels add --channel
|
|
64
|
+
openclaw channels add --channel lansenger --token "appId:appSecret"
|
|
65
65
|
|
|
66
66
|
# 4. 重啟閘道
|
|
67
67
|
openclaw gateway restart
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
-
`package.json` 中的 `openclaw.install` 元資料(`npmSpec`、`localPath`、`defaultChoice`)支援**按需安裝**:如果使用者在插件安裝前執行 `openclaw channels add --channel
|
|
70
|
+
`package.json` 中的 `openclaw.install` 元資料(`npmSpec`、`localPath`、`defaultChoice`)支援**按需安裝**:如果使用者在插件安裝前執行 `openclaw channels add --channel lansenger`,OpenClaw 可自動安裝該插件。
|
|
71
71
|
|
|
72
72
|
> **自訂閘道**:企業私有化部署(如奇安信)需在設定後透過 `openclaw.json` 或環境變數設定 `apiGatewayUrl` — 見[可選設定](#可選設定)。
|
|
73
73
|
|
|
@@ -91,7 +91,7 @@ openclaw gateway restart
|
|
|
91
91
|
重啟後機器人自動透過 WebSocket 連線。給機器人發私聊訊息,會收到配對碼,核准配對:
|
|
92
92
|
|
|
93
93
|
```bash
|
|
94
|
-
openclaw pairing approve
|
|
94
|
+
openclaw pairing approve lansenger <配對碼>
|
|
95
95
|
```
|
|
96
96
|
|
|
97
97
|
## 設定
|
|
@@ -117,11 +117,11 @@ openclaw pairing approve Lansenger <配對碼>
|
|
|
117
117
|
```json
|
|
118
118
|
{
|
|
119
119
|
"channels": {
|
|
120
|
-
"
|
|
120
|
+
"lansenger": {
|
|
121
121
|
"appId": "your-appid",
|
|
122
122
|
"appSecret": "your-secret",
|
|
123
123
|
"apiGatewayUrl": "https://open.e.lanxin.cn/open/apigw",
|
|
124
|
-
"homeChannel": "
|
|
124
|
+
"homeChannel": "lansenger",
|
|
125
125
|
"enabled": true,
|
|
126
126
|
"allowFrom": ["your-appid"],
|
|
127
127
|
"dmSecurity": "paired",
|
|
@@ -157,9 +157,9 @@ openclaw pairing approve Lansenger <配對碼>
|
|
|
157
157
|
|
|
158
158
|
```bash
|
|
159
159
|
# 新增第二個機器人(替換 appid/appsecret/gateway 為你的值)
|
|
160
|
-
openclaw config set channels.
|
|
161
|
-
openclaw config set channels.
|
|
162
|
-
openclaw config set channels.
|
|
160
|
+
openclaw config set channels.lansenger.accounts.your-appid-2.appId "your-appid-2"
|
|
161
|
+
openclaw config set channels.lansenger.accounts.your-appid-2.appSecret "your-appsecret"
|
|
162
|
+
openclaw config set channels.lansenger.accounts.your-appid-2.apiGatewayUrl "https://apigw.lx.qianxin.com"
|
|
163
163
|
|
|
164
164
|
# 重啟生效
|
|
165
165
|
openclaw gateway restart
|
|
@@ -170,7 +170,7 @@ openclaw gateway restart
|
|
|
170
170
|
```json
|
|
171
171
|
{
|
|
172
172
|
"channels": {
|
|
173
|
-
"
|
|
173
|
+
"lansenger": {
|
|
174
174
|
"appId": "your-appid-2",
|
|
175
175
|
"appSecret": "...",
|
|
176
176
|
"dmSecurity": "paired",
|
|
@@ -231,14 +231,14 @@ openclaw gateway call lansenger.status
|
|
|
231
231
|
{
|
|
232
232
|
agentId: "agent-a",
|
|
233
233
|
match: {
|
|
234
|
-
channel: "
|
|
234
|
+
channel: "lansenger",
|
|
235
235
|
peer: { kind: "direct", id: "2285568-xxx" },
|
|
236
236
|
},
|
|
237
237
|
},
|
|
238
238
|
{
|
|
239
239
|
agentId: "agent-a",
|
|
240
240
|
match: {
|
|
241
|
-
channel: "
|
|
241
|
+
channel: "lansenger",
|
|
242
242
|
peer: { kind: "group", id: "group-chat-id" },
|
|
243
243
|
},
|
|
244
244
|
},
|
|
@@ -247,7 +247,7 @@ openclaw gateway call lansenger.status
|
|
|
247
247
|
```
|
|
248
248
|
|
|
249
249
|
路由欄位:
|
|
250
|
-
* `match.channel`: `"
|
|
250
|
+
* `match.channel`: `"lansenger"`
|
|
251
251
|
* `match.peer.kind`: `"direct"`(私聊)或 `"group"`(群組聊天)
|
|
252
252
|
* `match.peer.id`: 使用者 ID(`2285568-xxx`)或群組聊天 ID
|
|
253
253
|
|
package/README.zhHantHK.md
CHANGED
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
|
|
34
34
|
**預設策略**:優先使用 `formatText` 發送 Markdown 回覆。附件使用 `text` 回退。兩種類型均支援 @mention(透過 `reminder` 參數)—提及使用者時在文字中包含「@姓名」。
|
|
35
35
|
|
|
36
|
-
##
|
|
36
|
+
## 代理工具
|
|
37
37
|
|
|
38
38
|
| 工具 | 說明 |
|
|
39
39
|
|------|------|
|
|
@@ -56,18 +56,18 @@
|
|
|
56
56
|
openclaw plugins install @lansenger-pm/openclaw-lansenger-channel
|
|
57
57
|
|
|
58
58
|
# 2. 啟用插件(如未自動啟用)
|
|
59
|
-
openclaw config set plugins.entries.
|
|
59
|
+
openclaw config set plugins.entries.lansenger.enabled true
|
|
60
60
|
|
|
61
61
|
# 3. 配置頻道(互動式向導)
|
|
62
|
-
openclaw channels add --channel
|
|
62
|
+
openclaw channels add --channel lansenger
|
|
63
63
|
# 或非互動式:
|
|
64
|
-
openclaw channels add --channel
|
|
64
|
+
openclaw channels add --channel lansenger --token "appId:appSecret"
|
|
65
65
|
|
|
66
66
|
# 4. 重啟網關
|
|
67
67
|
openclaw gateway restart
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
-
`package.json` 中的 `openclaw.install` 元資料(`npmSpec`、`localPath`、`defaultChoice`)支援**按需安裝**:如果使用者在插件安裝前執行 `openclaw channels add --channel
|
|
70
|
+
`package.json` 中的 `openclaw.install` 元資料(`npmSpec`、`localPath`、`defaultChoice`)支援**按需安裝**:如果使用者在插件安裝前執行 `openclaw channels add --channel lansenger`,OpenClaw 可自動安裝該插件。
|
|
71
71
|
|
|
72
72
|
> **自訂閘道**:企業私有化部署(如奇安信)需在設定後透過 `openclaw.json` 或環境變數設定 `apiGatewayUrl` — 見[可選設定](#可選設定)。
|
|
73
73
|
|
|
@@ -91,58 +91,7 @@ openclaw gateway restart
|
|
|
91
91
|
重啟後機械人自動透過 WebSocket 連線。給機械人發私聊訊息,會收到配對碼,核准配對:
|
|
92
92
|
|
|
93
93
|
```bash
|
|
94
|
-
openclaw pairing approve
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
> ⚠️ 第2步是必需的,因為 `openclaw channels add` 只發現 `extensions/` 目錄下的插件。這是 [OpenClaw 上游 bug](https://docs.openclaw.ai)。
|
|
98
|
-
|
|
99
|
-
### 透過 npm
|
|
100
|
-
|
|
101
|
-
```bash
|
|
102
|
-
# First install the npm package manually, then configure via CLI
|
|
103
|
-
npm install -g @lansenger-pm/openclaw-lansenger-channel
|
|
104
|
-
openclaw channels add --channel Lansenger --app-token "your-appid" --secret "your-appsecret"
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
### 開發安裝(本地連結)
|
|
108
|
-
|
|
109
|
-
```bash
|
|
110
|
-
cd /path/to/openclaw-lansenger-channel
|
|
111
|
-
npm install
|
|
112
|
-
openclaw plugins install --link
|
|
113
|
-
openclaw gateway restart
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
## 快速配置
|
|
117
|
-
|
|
118
|
-
安裝後,配置憑證:
|
|
119
|
-
|
|
120
|
-
> **單帳號**:`channels add` 僅建立一個帳號。如需多個機械人,見下方[多機械人設定](#多機械人設定)。
|
|
121
|
-
|
|
122
|
-
```bash
|
|
123
|
-
# 標準安裝(使用預設閘道 https://open.e.lanxin.cn/open/apigw)
|
|
124
|
-
openclaw channels add --channel Lansenger \
|
|
125
|
-
--app-token "你的-appid" \
|
|
126
|
-
--secret "你的-appsecret"
|
|
127
|
-
|
|
128
|
-
# 企業私有化部署(自訂閘道地址)
|
|
129
|
-
openclaw channels add --channel Lansenger \
|
|
130
|
-
--app-token "你的-appid" \
|
|
131
|
-
--secret "你的-appsecret" \
|
|
132
|
-
--base-url "https://apigw.lx.qianxin.com"
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
然後重啟:
|
|
136
|
-
```bash
|
|
137
|
-
openclaw gateway restart
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
取得憑證:**藍信桌面端** → **通訊錄** → **智能機器人** → **個人機械人** → 點擊右側 **ℹ️** 圖標(行動端不支援查看憑證)。
|
|
141
|
-
|
|
142
|
-
重啟後機械人自動透過 WebSocket 連線。給機械人發私聊訊息,會收到配對碼,核准配對:
|
|
143
|
-
|
|
144
|
-
```bash
|
|
145
|
-
openclaw pairing approve Lansenger <配對碼>
|
|
94
|
+
openclaw pairing approve lansenger <配對碼>
|
|
146
95
|
```
|
|
147
96
|
|
|
148
97
|
## 設定
|
|
@@ -168,11 +117,11 @@ openclaw pairing approve Lansenger <配對碼>
|
|
|
168
117
|
```json
|
|
169
118
|
{
|
|
170
119
|
"channels": {
|
|
171
|
-
"
|
|
120
|
+
"lansenger": {
|
|
172
121
|
"appId": "your-appid",
|
|
173
122
|
"appSecret": "your-secret",
|
|
174
123
|
"apiGatewayUrl": "https://open.e.lanxin.cn/open/apigw",
|
|
175
|
-
"homeChannel": "
|
|
124
|
+
"homeChannel": "lansenger",
|
|
176
125
|
"enabled": true,
|
|
177
126
|
"allowFrom": ["your-appid"],
|
|
178
127
|
"dmSecurity": "paired",
|
|
@@ -210,9 +159,9 @@ openclaw pairing approve Lansenger <配對碼>
|
|
|
210
159
|
|
|
211
160
|
```bash
|
|
212
161
|
# 新增第二個機械人(替換 appid/appsecret/gateway 為你的值)
|
|
213
|
-
openclaw config set channels.
|
|
214
|
-
openclaw config set channels.
|
|
215
|
-
openclaw config set channels.
|
|
162
|
+
openclaw config set channels.lansenger.accounts.your-appid-2.appId "your-appid-2"
|
|
163
|
+
openclaw config set channels.lansenger.accounts.your-appid-2.appSecret "your-appsecret"
|
|
164
|
+
openclaw config set channels.lansenger.accounts.your-appid-2.apiGatewayUrl "https://apigw.lx.qianxin.com"
|
|
216
165
|
|
|
217
166
|
# 重啟生效
|
|
218
167
|
openclaw gateway restart
|
|
@@ -223,7 +172,7 @@ openclaw gateway restart
|
|
|
223
172
|
```json
|
|
224
173
|
{
|
|
225
174
|
"channels": {
|
|
226
|
-
"
|
|
175
|
+
"lansenger": {
|
|
227
176
|
"appId": "your-appid-2",
|
|
228
177
|
"appSecret": "...",
|
|
229
178
|
"dmSecurity": "paired",
|
|
@@ -284,14 +233,14 @@ openclaw gateway call lansenger.status
|
|
|
284
233
|
{
|
|
285
234
|
agentId: "agent-a",
|
|
286
235
|
match: {
|
|
287
|
-
channel: "
|
|
236
|
+
channel: "lansenger",
|
|
288
237
|
peer: { kind: "direct", id: "2285568-xxx" },
|
|
289
238
|
},
|
|
290
239
|
},
|
|
291
240
|
{
|
|
292
241
|
agentId: "agent-a",
|
|
293
242
|
match: {
|
|
294
|
-
channel: "
|
|
243
|
+
channel: "lansenger",
|
|
295
244
|
peer: { kind: "group", id: "group-chat-id" },
|
|
296
245
|
},
|
|
297
246
|
},
|
|
@@ -300,7 +249,7 @@ openclaw gateway call lansenger.status
|
|
|
300
249
|
```
|
|
301
250
|
|
|
302
251
|
路由欄位:
|
|
303
|
-
* `match.channel`: `"
|
|
252
|
+
* `match.channel`: `"lansenger"`
|
|
304
253
|
* `match.peer.kind`: `"direct"`(私聊)或 `"group"`(羣組聊天)
|
|
305
254
|
* `match.peer.id`: 使用者 ID(`2285568-xxx`)或羣組聊天 ID
|
|
306
255
|
|
package/dist/index.js
CHANGED
|
@@ -3,17 +3,17 @@ import { lansengerPlugin } from "./src/channel.js";
|
|
|
3
3
|
import { startLansengerGateway } from "./src/runtime.js";
|
|
4
4
|
import { registerLansengerTools } from "./src/tools.js";
|
|
5
5
|
export default defineChannelPluginEntry({
|
|
6
|
-
id: "
|
|
6
|
+
id: "lansenger",
|
|
7
7
|
name: "Lansenger (蓝信)",
|
|
8
8
|
description: "Lansenger enterprise messaging channel plugin for OpenClaw",
|
|
9
9
|
plugin: lansengerPlugin,
|
|
10
10
|
registerCliMetadata(api) {
|
|
11
11
|
api.registerCli(({ program }) => {
|
|
12
|
-
program.command("
|
|
12
|
+
program.command("lansenger").description("Lansenger (蓝信) management");
|
|
13
13
|
}, {
|
|
14
14
|
descriptors: [
|
|
15
15
|
{
|
|
16
|
-
name: "
|
|
16
|
+
name: "lansenger",
|
|
17
17
|
description: "Lansenger (蓝信) management",
|
|
18
18
|
hasSubcommands: false,
|
|
19
19
|
},
|
package/dist/src/channel.js
CHANGED
|
@@ -7,7 +7,7 @@ import * as path from "node:path";
|
|
|
7
7
|
import * as crypto from "node:crypto";
|
|
8
8
|
import { LansengerClient, DEFAULT_API_GATEWAY_URL } from "./client.js";
|
|
9
9
|
function resolveAccount(cfg, accountId) {
|
|
10
|
-
const section = cfg.channels?.["
|
|
10
|
+
const section = cfg.channels?.["lansenger"];
|
|
11
11
|
const accounts = section?.accounts;
|
|
12
12
|
const defaultAccount = section?.defaultAccount;
|
|
13
13
|
let resolvedAccountId = accountId ?? defaultAccount ?? null;
|
|
@@ -63,7 +63,7 @@ const approverAuth = createResolvedApproverActionAuthAdapter({
|
|
|
63
63
|
});
|
|
64
64
|
const chatPlugin = createChatChannelPlugin({
|
|
65
65
|
base: createChannelPluginBase({
|
|
66
|
-
id: "
|
|
66
|
+
id: "lansenger",
|
|
67
67
|
meta: {
|
|
68
68
|
label: "Lansenger (蓝信)",
|
|
69
69
|
selectionLabel: "Lansenger (蓝信)",
|
|
@@ -79,7 +79,7 @@ const chatPlugin = createChatChannelPlugin({
|
|
|
79
79
|
config: {
|
|
80
80
|
resolveAccount,
|
|
81
81
|
listAccountIds: (cfg) => {
|
|
82
|
-
const section = cfg.channels?.["
|
|
82
|
+
const section = cfg.channels?.["lansenger"];
|
|
83
83
|
const accounts = section?.accounts;
|
|
84
84
|
const ids = [];
|
|
85
85
|
// Single account mode (appId as key)
|
|
@@ -99,7 +99,7 @@ const chatPlugin = createChatChannelPlugin({
|
|
|
99
99
|
return ids;
|
|
100
100
|
},
|
|
101
101
|
inspectAccount: (cfg, accountId) => {
|
|
102
|
-
const section = cfg.channels?.["
|
|
102
|
+
const section = cfg.channels?.["lansenger"];
|
|
103
103
|
const accounts = section?.accounts;
|
|
104
104
|
// Find account by appId
|
|
105
105
|
let account = undefined;
|
|
@@ -130,7 +130,7 @@ const chatPlugin = createChatChannelPlugin({
|
|
|
130
130
|
setup: {
|
|
131
131
|
applyAccountConfig: ({ cfg, accountId, input }) => {
|
|
132
132
|
const channels = { ...(cfg.channels ?? {}) };
|
|
133
|
-
const current = channels.
|
|
133
|
+
const current = channels.lansenger ?? {};
|
|
134
134
|
const updated = { ...current };
|
|
135
135
|
if (input.appToken)
|
|
136
136
|
updated.appId = input.appToken;
|
|
@@ -138,14 +138,14 @@ const chatPlugin = createChatChannelPlugin({
|
|
|
138
138
|
updated.appSecret = input.secret;
|
|
139
139
|
if (input.baseUrl)
|
|
140
140
|
updated.apiGatewayUrl = input.baseUrl;
|
|
141
|
-
channels.
|
|
141
|
+
channels.lansenger = updated;
|
|
142
142
|
return { ...cfg, channels };
|
|
143
143
|
},
|
|
144
144
|
},
|
|
145
145
|
}),
|
|
146
146
|
security: {
|
|
147
147
|
dm: {
|
|
148
|
-
channelKey: "
|
|
148
|
+
channelKey: "lansenger",
|
|
149
149
|
resolvePolicy: (account) => account.dmPolicy ?? "paired",
|
|
150
150
|
resolveAllowFrom: (account) => account.allowFrom,
|
|
151
151
|
defaultPolicy: "paired",
|
|
@@ -165,7 +165,7 @@ const chatPlugin = createChatChannelPlugin({
|
|
|
165
165
|
threading: { topLevelReplyToMode: "reply" },
|
|
166
166
|
outbound: {
|
|
167
167
|
attachedResults: {
|
|
168
|
-
channel: "
|
|
168
|
+
channel: "lansenger",
|
|
169
169
|
sendText: async (ctx) => {
|
|
170
170
|
const account = resolveAccount(ctx.cfg, ctx.accountId ?? undefined);
|
|
171
171
|
const client = makeClient(account);
|
|
@@ -212,14 +212,14 @@ const chatPlugin = createChatChannelPlugin({
|
|
|
212
212
|
const account = resolveAccount(ctx.cfg, ctx.accountId ?? undefined);
|
|
213
213
|
const client = makeClient(account);
|
|
214
214
|
const result = await client.sendFormatText(ctx.to, ctx.text);
|
|
215
|
-
return [{ channel: "
|
|
215
|
+
return [{ channel: "lansenger", messageId: result.messageId ?? "" }];
|
|
216
216
|
},
|
|
217
217
|
},
|
|
218
218
|
},
|
|
219
219
|
});
|
|
220
220
|
const lansengerOnboarding = {
|
|
221
221
|
configuredCheck: (cfg) => {
|
|
222
|
-
const section = cfg.channels?.["
|
|
222
|
+
const section = cfg.channels?.["lansenger"];
|
|
223
223
|
const accounts = section?.accounts;
|
|
224
224
|
if (accounts && Object.keys(accounts).length > 0) {
|
|
225
225
|
return Object.values(accounts).some((a) => a?.appId && a?.appSecret);
|
|
@@ -228,13 +228,13 @@ const lansengerOnboarding = {
|
|
|
228
228
|
},
|
|
229
229
|
setDmPolicy: (cfg, policy) => {
|
|
230
230
|
const channels = { ...(cfg.channels ?? {}) };
|
|
231
|
-
const current = channels.
|
|
232
|
-
channels.
|
|
231
|
+
const current = channels.lansenger ?? {};
|
|
232
|
+
channels.lansenger = { ...current, dmSecurity: policy };
|
|
233
233
|
return { ...cfg, channels };
|
|
234
234
|
},
|
|
235
235
|
promptAllowFrom: async (params) => {
|
|
236
236
|
const { cfg, prompter, accountId } = params;
|
|
237
|
-
const section = cfg.channels?.["
|
|
237
|
+
const section = cfg.channels?.["lansenger"] ?? {};
|
|
238
238
|
const accounts = section?.accounts;
|
|
239
239
|
const account = accountId ? accounts?.[accountId] : section;
|
|
240
240
|
const currentAllowFrom = account?.allowFrom ?? [];
|
|
@@ -248,7 +248,7 @@ const lansengerOnboarding = {
|
|
|
248
248
|
const merged = [...currentAllowFrom.map(String).filter(Boolean), newId];
|
|
249
249
|
const unique = [...new Set(merged)];
|
|
250
250
|
const channels = { ...(cfg.channels ?? {}) };
|
|
251
|
-
const current = channels.
|
|
251
|
+
const current = channels.lansenger ?? {};
|
|
252
252
|
const accountsCopy = current.accounts ? { ...current.accounts } : {};
|
|
253
253
|
if (accountId && accountsCopy[accountId]) {
|
|
254
254
|
accountsCopy[accountId] = { ...accountsCopy[accountId], allowFrom: unique };
|
|
@@ -256,7 +256,7 @@ const lansengerOnboarding = {
|
|
|
256
256
|
else {
|
|
257
257
|
current.allowFrom = unique;
|
|
258
258
|
}
|
|
259
|
-
channels.
|
|
259
|
+
channels.lansenger = { ...current, enabled: true, accounts: accountsCopy, dmSecurity: current.dmSecurity ?? "paired" };
|
|
260
260
|
return { ...cfg, channels };
|
|
261
261
|
},
|
|
262
262
|
noteSetupHelp: async (params) => {
|
|
@@ -310,7 +310,7 @@ const lansengerOnboarding = {
|
|
|
310
310
|
apiGatewayUrl = rest.join(":").trim();
|
|
311
311
|
}
|
|
312
312
|
}
|
|
313
|
-
const section = cfg.channels?.["
|
|
313
|
+
const section = cfg.channels?.["lansenger"] ?? {};
|
|
314
314
|
const accounts = section?.accounts;
|
|
315
315
|
const alreadyConfigured = Boolean(section.appId && section.appSecret) || (accounts && Object.keys(accounts).length > 0);
|
|
316
316
|
if (!alreadyConfigured && !appId) {
|
|
@@ -345,7 +345,7 @@ const lansengerOnboarding = {
|
|
|
345
345
|
})).trim();
|
|
346
346
|
}
|
|
347
347
|
const channels = { ...(cfg.channels ?? {}) };
|
|
348
|
-
const current = channels.
|
|
348
|
+
const current = channels.lansenger ?? {};
|
|
349
349
|
const accountsCopy = current.accounts ? { ...current.accounts } : {};
|
|
350
350
|
if (appId && appSecret) {
|
|
351
351
|
const accountEntry = {
|
|
@@ -378,7 +378,7 @@ const lansengerOnboarding = {
|
|
|
378
378
|
Object.assign(current, accountEntry);
|
|
379
379
|
}
|
|
380
380
|
}
|
|
381
|
-
channels.
|
|
381
|
+
channels.lansenger = current;
|
|
382
382
|
return { ...cfg, channels };
|
|
383
383
|
},
|
|
384
384
|
};
|
package/dist/src/channel.test.js
CHANGED
|
@@ -6,7 +6,7 @@ describe("Lansenger plugin", () => {
|
|
|
6
6
|
it("resolves account from config", () => {
|
|
7
7
|
const cfg = {
|
|
8
8
|
channels: {
|
|
9
|
-
|
|
9
|
+
lansenger: { appId: "test-app-id", appSecret: "test-secret", allowFrom: ["user1"] },
|
|
10
10
|
},
|
|
11
11
|
};
|
|
12
12
|
const account = resolveAccount(cfg, undefined);
|
|
@@ -18,7 +18,7 @@ describe("Lansenger plugin", () => {
|
|
|
18
18
|
it("resolves account from accounts by accountId", () => {
|
|
19
19
|
const cfg = {
|
|
20
20
|
channels: {
|
|
21
|
-
|
|
21
|
+
lansenger: {
|
|
22
22
|
appId: "default-id",
|
|
23
23
|
appSecret: "default-secret",
|
|
24
24
|
accounts: {
|
|
@@ -36,7 +36,7 @@ describe("Lansenger plugin", () => {
|
|
|
36
36
|
it("falls back to top-level when accountId not in accounts", () => {
|
|
37
37
|
const cfg = {
|
|
38
38
|
channels: {
|
|
39
|
-
|
|
39
|
+
lansenger: {
|
|
40
40
|
appId: "default-id",
|
|
41
41
|
appSecret: "default-secret",
|
|
42
42
|
accounts: {
|
|
@@ -87,7 +87,7 @@ describe("Lansenger plugin", () => {
|
|
|
87
87
|
process.env.LANSENGER_APP_SECRET = origSecret;
|
|
88
88
|
});
|
|
89
89
|
it("has correct plugin id", () => {
|
|
90
|
-
expect(lansengerPlugin.id).toBe("
|
|
90
|
+
expect(lansengerPlugin.id).toBe("lansenger");
|
|
91
91
|
});
|
|
92
92
|
});
|
|
93
93
|
describe("LansengerClient", () => {
|
|
@@ -196,17 +196,17 @@ describe("processRawMessage", () => {
|
|
|
196
196
|
expect(events.length).toBe(0);
|
|
197
197
|
});
|
|
198
198
|
});
|
|
199
|
-
describe("
|
|
199
|
+
describe("LansengerOnboarding", () => {
|
|
200
200
|
it("configuredCheck returns true when both appId and appSecret present", () => {
|
|
201
|
-
const cfg = { channels: {
|
|
201
|
+
const cfg = { channels: { lansenger: { appId: "id", appSecret: "secret" } } };
|
|
202
202
|
expect(lansengerOnboarding.configuredCheck(cfg)).toBe(true);
|
|
203
203
|
});
|
|
204
204
|
it("configuredCheck returns false when missing appId", () => {
|
|
205
|
-
const cfg = { channels: {
|
|
205
|
+
const cfg = { channels: { lansenger: { appSecret: "secret" } } };
|
|
206
206
|
expect(lansengerOnboarding.configuredCheck(cfg)).toBe(false);
|
|
207
207
|
});
|
|
208
208
|
it("configuredCheck returns false when missing appSecret", () => {
|
|
209
|
-
const cfg = { channels: {
|
|
209
|
+
const cfg = { channels: { lansenger: { appId: "id" } } };
|
|
210
210
|
expect(lansengerOnboarding.configuredCheck(cfg)).toBe(false);
|
|
211
211
|
});
|
|
212
212
|
it("configuredCheck returns false when section missing", () => {
|
|
@@ -214,16 +214,16 @@ describe("lansengerOnboarding", () => {
|
|
|
214
214
|
expect(lansengerOnboarding.configuredCheck(cfg)).toBe(false);
|
|
215
215
|
});
|
|
216
216
|
it("setDmPolicy merges dmSecurity into config", () => {
|
|
217
|
-
const cfg = { channels: {
|
|
217
|
+
const cfg = { channels: { lansenger: { appId: "id", appSecret: "secret" } } };
|
|
218
218
|
const result = lansengerOnboarding.setDmPolicy(cfg, "open");
|
|
219
|
-
expect(result.channels.
|
|
219
|
+
expect(result.channels.lansenger.dmSecurity).toBe("open");
|
|
220
220
|
});
|
|
221
221
|
it("setDmPolicy preserves existing fields", () => {
|
|
222
|
-
const cfg = { channels: {
|
|
222
|
+
const cfg = { channels: { lansenger: { appId: "id", appSecret: "secret", allowFrom: ["u1"] } } };
|
|
223
223
|
const result = lansengerOnboarding.setDmPolicy(cfg, "paired");
|
|
224
|
-
expect(result.channels.
|
|
225
|
-
expect(result.channels.
|
|
226
|
-
expect(result.channels.
|
|
224
|
+
expect(result.channels.lansenger.appId).toBe("id");
|
|
225
|
+
expect(result.channels.lansenger.allowFrom).toEqual(["u1"]);
|
|
226
|
+
expect(result.channels.lansenger.dmSecurity).toBe("paired");
|
|
227
227
|
});
|
|
228
228
|
it("promptAllowFrom adds new ID with dedup", async () => {
|
|
229
229
|
const mockPrompter = {
|
|
@@ -232,11 +232,11 @@ describe("lansengerOnboarding", () => {
|
|
|
232
232
|
select: async () => "",
|
|
233
233
|
note: async () => { },
|
|
234
234
|
};
|
|
235
|
-
const cfg = { channels: {
|
|
235
|
+
const cfg = { channels: { lansenger: { appId: "id", appSecret: "secret", allowFrom: ["user-old"] } } };
|
|
236
236
|
const result = await lansengerOnboarding.promptAllowFrom({ cfg, prompter: mockPrompter, accountId: null });
|
|
237
|
-
expect(result.channels.
|
|
238
|
-
expect(result.channels.
|
|
239
|
-
expect(result.channels.
|
|
237
|
+
expect(result.channels.lansenger.allowFrom).toEqual(["user-old", "user-new"]);
|
|
238
|
+
expect(result.channels.lansenger.enabled).toBe(true);
|
|
239
|
+
expect(result.channels.lansenger.dmSecurity).toBe("paired");
|
|
240
240
|
});
|
|
241
241
|
it("promptAllowFrom deduplicates existing entries", async () => {
|
|
242
242
|
const mockPrompter = {
|
|
@@ -245,9 +245,9 @@ describe("lansengerOnboarding", () => {
|
|
|
245
245
|
select: async () => "",
|
|
246
246
|
note: async () => { },
|
|
247
247
|
};
|
|
248
|
-
const cfg = { channels: {
|
|
248
|
+
const cfg = { channels: { lansenger: { appId: "id", appSecret: "secret", allowFrom: ["user-old"] } } };
|
|
249
249
|
const result = await lansengerOnboarding.promptAllowFrom({ cfg, prompter: mockPrompter, accountId: null });
|
|
250
|
-
expect(result.channels.
|
|
250
|
+
expect(result.channels.lansenger.allowFrom).toEqual(["user-old"]);
|
|
251
251
|
});
|
|
252
252
|
it("promptAllowFrom sets dmSecurity to paired when missing", async () => {
|
|
253
253
|
const mockPrompter = {
|
|
@@ -256,9 +256,9 @@ describe("lansengerOnboarding", () => {
|
|
|
256
256
|
select: async () => "",
|
|
257
257
|
note: async () => { },
|
|
258
258
|
};
|
|
259
|
-
const cfg = { channels: {
|
|
259
|
+
const cfg = { channels: { lansenger: { appId: "id", appSecret: "secret" } } };
|
|
260
260
|
const result = await lansengerOnboarding.promptAllowFrom({ cfg, prompter: mockPrompter, accountId: null });
|
|
261
|
-
expect(result.channels.
|
|
261
|
+
expect(result.channels.lansenger.dmSecurity).toBe("paired");
|
|
262
262
|
});
|
|
263
263
|
it("noteSetupHelp calls prompter.note", async () => {
|
|
264
264
|
let noted = false;
|
|
@@ -280,11 +280,11 @@ describe("lansengerOnboarding", () => {
|
|
|
280
280
|
};
|
|
281
281
|
const cfg = { channels: {} };
|
|
282
282
|
const result = await lansengerOnboarding.runSetupWizard({ cfg, prompter: mockPrompter, token: "my-app-id:my-secret" });
|
|
283
|
-
expect(result.channels.
|
|
284
|
-
expect(result.channels.
|
|
285
|
-
expect(result.channels.
|
|
286
|
-
expect(result.channels.
|
|
287
|
-
expect(result.channels.
|
|
283
|
+
expect(result.channels.lansenger.appId).toBe("my-app-id");
|
|
284
|
+
expect(result.channels.lansenger.appSecret).toBe("my-secret");
|
|
285
|
+
expect(result.channels.lansenger.enabled).toBe(true);
|
|
286
|
+
expect(result.channels.lansenger.dmSecurity).toBe("paired");
|
|
287
|
+
expect(result.channels.lansenger.approval.enabled).toBe(true);
|
|
288
288
|
});
|
|
289
289
|
it("runSetupWizard prompts for credentials when not configured and no token", async () => {
|
|
290
290
|
let prompts = 0;
|
|
@@ -296,9 +296,9 @@ describe("lansengerOnboarding", () => {
|
|
|
296
296
|
};
|
|
297
297
|
const cfg = { channels: {} };
|
|
298
298
|
const result = await lansengerOnboarding.runSetupWizard({ cfg, prompter: mockPrompter, token: undefined });
|
|
299
|
-
expect(result.channels.
|
|
300
|
-
expect(result.channels.
|
|
301
|
-
expect(result.channels.
|
|
299
|
+
expect(result.channels.lansenger.appId).toBe("new-id");
|
|
300
|
+
expect(result.channels.lansenger.appSecret).toBe("new-secret");
|
|
301
|
+
expect(result.channels.lansenger.enabled).toBe(true);
|
|
302
302
|
});
|
|
303
303
|
it("runSetupWizard keeps existing credentials when user confirms", async () => {
|
|
304
304
|
const mockPrompter = {
|
|
@@ -307,11 +307,11 @@ describe("lansengerOnboarding", () => {
|
|
|
307
307
|
select: async () => "",
|
|
308
308
|
note: async () => { },
|
|
309
309
|
};
|
|
310
|
-
const cfg = { channels: {
|
|
310
|
+
const cfg = { channels: { lansenger: { appId: "existing-id", appSecret: "existing-secret", allowFrom: ["u1"] } } };
|
|
311
311
|
const result = await lansengerOnboarding.runSetupWizard({ cfg, prompter: mockPrompter, token: undefined });
|
|
312
|
-
expect(result.channels.
|
|
313
|
-
expect(result.channels.
|
|
314
|
-
expect(result.channels.
|
|
312
|
+
expect(result.channels.lansenger.appId).toBe("existing-id");
|
|
313
|
+
expect(result.channels.lansenger.appSecret).toBe("existing-secret");
|
|
314
|
+
expect(result.channels.lansenger.allowFrom).toEqual(["u1"]);
|
|
315
315
|
});
|
|
316
316
|
it("runSetupWizard replaces credentials when user declines keep", async () => {
|
|
317
317
|
let prompts = 0;
|
|
@@ -321,10 +321,10 @@ describe("lansengerOnboarding", () => {
|
|
|
321
321
|
select: async () => "",
|
|
322
322
|
note: async () => { },
|
|
323
323
|
};
|
|
324
|
-
const cfg = { channels: {
|
|
324
|
+
const cfg = { channels: { lansenger: { appId: "old-id", appSecret: "old-secret" } } };
|
|
325
325
|
const result = await lansengerOnboarding.runSetupWizard({ cfg, prompter: mockPrompter, token: undefined });
|
|
326
|
-
expect(result.channels.
|
|
327
|
-
expect(result.channels.
|
|
326
|
+
expect(result.channels.lansenger.appId).toBe("replaced-id");
|
|
327
|
+
expect(result.channels.lansenger.appSecret).toBe("replaced-secret");
|
|
328
328
|
});
|
|
329
329
|
});
|
|
330
330
|
//# sourceMappingURL=channel.test.js.map
|
package/dist/src/runtime.js
CHANGED
|
@@ -64,7 +64,7 @@ export function getRunningAccount() {
|
|
|
64
64
|
return null;
|
|
65
65
|
}
|
|
66
66
|
export function startLansengerGateway(api) {
|
|
67
|
-
const section = api.config.channels?.["
|
|
67
|
+
const section = api.config.channels?.["lansenger"];
|
|
68
68
|
const accounts = section?.accounts;
|
|
69
69
|
api.registerGatewayMethod("lansenger.start", async (opts) => {
|
|
70
70
|
log.info("lansenger.start called");
|
|
@@ -223,7 +223,7 @@ export function startLansengerGateway(api) {
|
|
|
223
223
|
autoStart(api, accounts);
|
|
224
224
|
}
|
|
225
225
|
function autoStart(api, accounts) {
|
|
226
|
-
const section = api.config.channels?.["
|
|
226
|
+
const section = api.config.channels?.["lansenger"];
|
|
227
227
|
const accountIds = new Set();
|
|
228
228
|
if (accounts && Object.keys(accounts).length > 0) {
|
|
229
229
|
for (const [accountId] of Object.entries(accounts)) {
|
|
@@ -243,7 +243,7 @@ async function handleInbound(api, event, account, runningKey) {
|
|
|
243
243
|
if (event.isGroup) {
|
|
244
244
|
const groupPolicy = api.runtime.channel.groups.resolveGroupPolicy({
|
|
245
245
|
cfg: api.config,
|
|
246
|
-
channel: "
|
|
246
|
+
channel: "lansenger",
|
|
247
247
|
groupId: event.chatId,
|
|
248
248
|
accountId: account.accountId,
|
|
249
249
|
});
|
|
@@ -253,7 +253,7 @@ async function handleInbound(api, event, account, runningKey) {
|
|
|
253
253
|
}
|
|
254
254
|
const requireMention = api.runtime.channel.groups.resolveRequireMention({
|
|
255
255
|
cfg: api.config,
|
|
256
|
-
channel: "
|
|
256
|
+
channel: "lansenger",
|
|
257
257
|
groupId: event.chatId,
|
|
258
258
|
accountId: account.accountId,
|
|
259
259
|
});
|
|
@@ -261,7 +261,7 @@ async function handleInbound(api, event, account, runningKey) {
|
|
|
261
261
|
}
|
|
262
262
|
const route = api.runtime.channel.routing.resolveAgentRoute({
|
|
263
263
|
cfg: api.config,
|
|
264
|
-
channel: "
|
|
264
|
+
channel: "lansenger",
|
|
265
265
|
accountId: account.accountId,
|
|
266
266
|
peer: { kind: chatType, id: event.chatId },
|
|
267
267
|
});
|
|
@@ -277,7 +277,7 @@ async function handleInbound(api, event, account, runningKey) {
|
|
|
277
277
|
try {
|
|
278
278
|
log.info(`turn.run starting: sessionKey=${sessionKey} agentId=${agentId} accountId=${account.accountId} matchedBy=${route.matchedBy}`);
|
|
279
279
|
await api.runtime.channel.turn.run({
|
|
280
|
-
channel: "
|
|
280
|
+
channel: "lansenger",
|
|
281
281
|
accountId: account.accountId ?? undefined,
|
|
282
282
|
raw: event,
|
|
283
283
|
adapter: {
|
|
@@ -294,7 +294,7 @@ async function handleInbound(api, event, account, runningKey) {
|
|
|
294
294
|
const storePath = api.runtime.channel.session.resolveStorePath(undefined, { agentId });
|
|
295
295
|
return {
|
|
296
296
|
cfg: api.config,
|
|
297
|
-
channel: "
|
|
297
|
+
channel: "lansenger",
|
|
298
298
|
accountId: account.accountId ?? undefined,
|
|
299
299
|
agentId,
|
|
300
300
|
routeSessionKey: sessionKey,
|
|
@@ -306,7 +306,7 @@ async function handleInbound(api, event, account, runningKey) {
|
|
|
306
306
|
FromName: event.userName,
|
|
307
307
|
SessionKey: sessionKey,
|
|
308
308
|
ChatType: chatType,
|
|
309
|
-
Channel: "
|
|
309
|
+
Channel: "lansenger",
|
|
310
310
|
To: replyTo,
|
|
311
311
|
},
|
|
312
312
|
recordInboundSession: api.runtime.channel.session.recordInboundSession,
|
package/dist/src/tools.js
CHANGED
|
@@ -5,7 +5,7 @@ import * as fs from "node:fs/promises";
|
|
|
5
5
|
function resolveAccountFromConfig(config) {
|
|
6
6
|
if (!config)
|
|
7
7
|
return null;
|
|
8
|
-
const section = config.channels?.["
|
|
8
|
+
const section = config.channels?.["lansenger"];
|
|
9
9
|
if (!section)
|
|
10
10
|
return null;
|
|
11
11
|
const accounts = section.accounts;
|
package/index.ts
CHANGED
|
@@ -4,19 +4,19 @@ import { startLansengerGateway } from "./src/runtime.js";
|
|
|
4
4
|
import { registerLansengerTools } from "./src/tools.js";
|
|
5
5
|
|
|
6
6
|
export default defineChannelPluginEntry({
|
|
7
|
-
id: "
|
|
7
|
+
id: "lansenger",
|
|
8
8
|
name: "Lansenger (蓝信)",
|
|
9
9
|
description: "Lansenger enterprise messaging channel plugin for OpenClaw",
|
|
10
10
|
plugin: lansengerPlugin,
|
|
11
11
|
registerCliMetadata(api) {
|
|
12
12
|
api.registerCli(
|
|
13
13
|
({ program }) => {
|
|
14
|
-
program.command("
|
|
14
|
+
program.command("lansenger").description("Lansenger (蓝信) management");
|
|
15
15
|
},
|
|
16
16
|
{
|
|
17
17
|
descriptors: [
|
|
18
18
|
{
|
|
19
|
-
name: "
|
|
19
|
+
name: "lansenger",
|
|
20
20
|
description: "Lansenger (蓝信) management",
|
|
21
21
|
hasSubcommands: false,
|
|
22
22
|
},
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"id": "
|
|
3
|
-
"channels": ["
|
|
2
|
+
"id": "lansenger",
|
|
3
|
+
"channels": ["lansenger"],
|
|
4
4
|
"name": "Lansenger (蓝信)",
|
|
5
5
|
"description": "Lansenger enterprise messaging channel plugin for OpenClaw / 蓝信企业即时通讯频道插件",
|
|
6
6
|
"configSchema": {
|
|
@@ -9,12 +9,12 @@
|
|
|
9
9
|
"properties": {}
|
|
10
10
|
},
|
|
11
11
|
"channelEnvVars": {
|
|
12
|
-
"
|
|
12
|
+
"lansenger": ["LANSENGER_APP_ID", "LANSENGER_APP_SECRET", "LANSENGER_API_GATEWAY_URL"]
|
|
13
13
|
},
|
|
14
14
|
"activation": {
|
|
15
15
|
"onStartup": true,
|
|
16
|
-
"onChannels": ["
|
|
17
|
-
"onConfigPaths": ["channels.
|
|
16
|
+
"onChannels": ["lansenger"],
|
|
17
|
+
"onConfigPaths": ["channels.lansenger"]
|
|
18
18
|
},
|
|
19
19
|
"contracts": {
|
|
20
20
|
"tools": [
|
|
@@ -31,36 +31,36 @@
|
|
|
31
31
|
},
|
|
32
32
|
"toolMetadata": {
|
|
33
33
|
"lansenger_send_file": {
|
|
34
|
-
"configSignals": [{ "rootPath": "channels.
|
|
34
|
+
"configSignals": [{ "rootPath": "channels.lansenger", "required": ["appId", "appSecret"] }]
|
|
35
35
|
},
|
|
36
36
|
"lansenger_send_text": {
|
|
37
|
-
"configSignals": [{ "rootPath": "channels.
|
|
37
|
+
"configSignals": [{ "rootPath": "channels.lansenger", "required": ["appId", "appSecret"] }]
|
|
38
38
|
},
|
|
39
39
|
"lansenger_send_image_url": {
|
|
40
|
-
"configSignals": [{ "rootPath": "channels.
|
|
40
|
+
"configSignals": [{ "rootPath": "channels.lansenger", "required": ["appId", "appSecret"] }]
|
|
41
41
|
},
|
|
42
42
|
"lansenger_revoke_message": {
|
|
43
|
-
"configSignals": [{ "rootPath": "channels.
|
|
43
|
+
"configSignals": [{ "rootPath": "channels.lansenger", "required": ["appId", "appSecret"] }]
|
|
44
44
|
},
|
|
45
45
|
"lansenger_send_link_card": {
|
|
46
|
-
"configSignals": [{ "rootPath": "channels.
|
|
46
|
+
"configSignals": [{ "rootPath": "channels.lansenger", "required": ["appId", "appSecret"] }]
|
|
47
47
|
},
|
|
48
48
|
"lansenger_send_app_articles": {
|
|
49
|
-
"configSignals": [{ "rootPath": "channels.
|
|
49
|
+
"configSignals": [{ "rootPath": "channels.lansenger", "required": ["appId", "appSecret"] }]
|
|
50
50
|
},
|
|
51
51
|
"lansenger_send_app_card": {
|
|
52
|
-
"configSignals": [{ "rootPath": "channels.
|
|
52
|
+
"configSignals": [{ "rootPath": "channels.lansenger", "required": ["appId", "appSecret"] }]
|
|
53
53
|
},
|
|
54
54
|
"lansenger_update_dynamic_card": {
|
|
55
|
-
"configSignals": [{ "rootPath": "channels.
|
|
55
|
+
"configSignals": [{ "rootPath": "channels.lansenger", "required": ["appId", "appSecret"] }]
|
|
56
56
|
},
|
|
57
57
|
"lansenger_query_groups": {
|
|
58
|
-
"configSignals": [{ "rootPath": "channels.
|
|
58
|
+
"configSignals": [{ "rootPath": "channels.lansenger", "required": ["appId", "appSecret"] }]
|
|
59
59
|
}
|
|
60
60
|
},
|
|
61
61
|
"skills": ["./skills"],
|
|
62
62
|
"channelConfigs": {
|
|
63
|
-
"
|
|
63
|
+
"lansenger": {
|
|
64
64
|
"label": "Lansenger (蓝信)",
|
|
65
65
|
"description": "Lansenger enterprise messaging channel / 蓝信企业即时通讯频道",
|
|
66
66
|
"schema": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lansenger-pm/openclaw-lansenger-channel",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"files": [
|
|
@@ -90,13 +90,13 @@
|
|
|
90
90
|
]
|
|
91
91
|
},
|
|
92
92
|
"devDependencies": {
|
|
93
|
+
"openclaw": "^2026.5.7",
|
|
93
94
|
"@types/node": "^25.7.0",
|
|
94
95
|
"@types/ws": "^8.18.1",
|
|
95
96
|
"typescript": "^6.0.3",
|
|
96
97
|
"vitest": "^4.1.6"
|
|
97
98
|
},
|
|
98
99
|
"dependencies": {
|
|
99
|
-
"openclaw": "^2026.5.7",
|
|
100
100
|
"ws": "^8.20.0"
|
|
101
101
|
}
|
|
102
102
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: lansenger-messaging
|
|
3
3
|
description: How to communicate effectively on Lansenger (蓝信) — message types, formatting rules, media, cards, approvals, and pitfalls
|
|
4
|
-
metadata: {"openclaw":{"requires":{"config":["channels.
|
|
4
|
+
metadata: {"openclaw":{"requires":{"config":["channels.lansenger"]},"primaryEnv":"LANSENGER_APP_ID"}}
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Lansenger (蓝信) Messaging Guide for Agents
|