@lansenger-pm/openclaw-lansenger-channel 2.8.3 → 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 -15
- package/README.md +15 -15
- package/README.zhHans.md +15 -15
- package/README.zhHant.md +15 -15
- package/README.zhHantHK.md +15 -15
- 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 +1 -1
- package/skills/lansenger-messaging/SKILL.md +1 -1
package/README.fr.md
CHANGED
|
@@ -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,7 +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
|
|
94
|
+
openclaw pairing approve lansenger <code>
|
|
95
95
|
```
|
|
96
96
|
|
|
97
97
|
## Configuration
|
|
@@ -117,11 +117,11 @@ Ajoutez ces variables à `~/.openclaw/.env` ou à votre environnement :
|
|
|
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",
|
|
@@ -142,7 +142,7 @@ Ajoutez ces variables à `~/.openclaw/.env` ou à votre environnement :
|
|
|
142
142
|
| `appId` | App ID du bot personnel | — |
|
|
143
143
|
| `appSecret` | App Secret du bot personnel | — |
|
|
144
144
|
| `apiGatewayUrl` | URL de la passerelle API | `https://open.e.lanxin.cn/open/apigw` |
|
|
145
|
-
| `homeChannel` | Canal par défaut pour le routage de l'agent | `
|
|
145
|
+
| `homeChannel` | Canal par défaut pour le routage de l'agent | `lansenger` |
|
|
146
146
|
| `enabled` | Activer/désactiver le canal | `true` |
|
|
147
147
|
| `allowFrom` | IDs d'utilisateurs autorisés en DM | `[]` |
|
|
148
148
|
| `dmSecurity` | Politique DM : `paired`, `allowlist`, `open` | `paired` |
|
|
@@ -157,9 +157,9 @@ Pour ajouter plusieurs bots, utilisez `openclaw config set` avec la structure `a
|
|
|
157
157
|
|
|
158
158
|
```bash
|
|
159
159
|
# Ajouter un deuxième bot (remplacez appid/appsecret/gateway par vos valeurs)
|
|
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
|
# Redémarrer pour appliquer
|
|
165
165
|
openclaw gateway restart
|
|
@@ -170,7 +170,7 @@ Structure de configuration résultante :
|
|
|
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 @@ Utilisez `bindings` pour router les DM Lansenger ou les conversations de groupe
|
|
|
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 @@ Utilisez `bindings` pour router les DM Lansenger ou les conversations de groupe
|
|
|
247
247
|
```
|
|
248
248
|
|
|
249
249
|
Champs de routage :
|
|
250
|
-
* `match.channel`: `"
|
|
250
|
+
* `match.channel`: `"lansenger"`
|
|
251
251
|
* `match.peer.kind`: `"direct"` (DM) ou `"group"` (chat de groupe)
|
|
252
252
|
* `match.peer.id`: ID utilisateur (`2285568-xxx`) ou ID de chat de groupe
|
|
253
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",
|
|
@@ -140,7 +140,7 @@ Add these to `~/.openclaw/.env` or your environment:
|
|
|
140
140
|
| `appId` | Personal bot App ID | — |
|
|
141
141
|
| `appSecret` | Personal bot App Secret | — |
|
|
142
142
|
| `apiGatewayUrl` | API Gateway URL | `https://open.e.lanxin.cn/open/apigw` |
|
|
143
|
-
| `homeChannel` | Default channel for agent routing | `
|
|
143
|
+
| `homeChannel` | Default channel for agent routing | `lansenger` |
|
|
144
144
|
| `enabled` | Enable/disable the channel | `true` |
|
|
145
145
|
| `allowFrom` | User IDs allowed to DM the bot | `[]` |
|
|
146
146
|
| `dmSecurity` | DM policy: `paired`, `allowlist`, `open` | `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",
|
|
@@ -142,7 +142,7 @@ openclaw pairing approve Lansenger <配对码>
|
|
|
142
142
|
| `appId` | 个人机器人 App ID | — |
|
|
143
143
|
| `appSecret` | 个人机器人 App Secret | — |
|
|
144
144
|
| `apiGatewayUrl` | API 网关 URL | `https://open.e.lanxin.cn/open/apigw` |
|
|
145
|
-
| `homeChannel` | 代理路由的默认频道 | `
|
|
145
|
+
| `homeChannel` | 代理路由的默认频道 | `lansenger` |
|
|
146
146
|
| `enabled` | 启用/禁用频道 | `true` |
|
|
147
147
|
| `allowFrom` | 允许私聊的用户 ID | `[]` |
|
|
148
148
|
| `dmSecurity` | 私聊策略:`paired`、`allowlist`、`open` | `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",
|
|
@@ -142,7 +142,7 @@ openclaw pairing approve Lansenger <配對碼>
|
|
|
142
142
|
| `appId` | 個人機器人 App ID | — |
|
|
143
143
|
| `appSecret` | 個人機器人 App Secret | — |
|
|
144
144
|
| `apiGatewayUrl` | API 閘道 URL | `https://open.e.lanxin.cn/open/apigw` |
|
|
145
|
-
| `homeChannel` | 代理路由的預設頻道 | `
|
|
145
|
+
| `homeChannel` | 代理路由的預設頻道 | `lansenger` |
|
|
146
146
|
| `enabled` | 啟用/禁用頻道 | `true` |
|
|
147
147
|
| `allowFrom` | 允許私聊的使用者 ID | `[]` |
|
|
148
148
|
| `dmSecurity` | 私聊策略:`paired`、`allowlist`、`open` | `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
|
@@ -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",
|
|
@@ -142,7 +142,7 @@ openclaw pairing approve Lansenger <配對碼>
|
|
|
142
142
|
| `appId` | 個人機械人 App ID | — |
|
|
143
143
|
| `appSecret` | 個人機械人 App Secret | — |
|
|
144
144
|
| `apiGatewayUrl` | API 網關 URL | `https://open.e.lanxin.cn/open/apigw` |
|
|
145
|
-
| `homeChannel` | 代理路由的預設頻道 | `
|
|
145
|
+
| `homeChannel` | 代理路由的預設頻道 | `lansenger` |
|
|
146
146
|
| `enabled` | 啟用/禁用頻道 | `true` |
|
|
147
147
|
| `allowFrom` | 允許私聊的使用者 ID | `[]` |
|
|
148
148
|
| `dmSecurity` | 私聊策略:`paired`、`allowlist`、`open` | `paired` |
|
|
@@ -159,9 +159,9 @@ openclaw pairing approve Lansenger <配對碼>
|
|
|
159
159
|
|
|
160
160
|
```bash
|
|
161
161
|
# 新增第二個機械人(替換 appid/appsecret/gateway 為你的值)
|
|
162
|
-
openclaw config set channels.
|
|
163
|
-
openclaw config set channels.
|
|
164
|
-
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"
|
|
165
165
|
|
|
166
166
|
# 重啟生效
|
|
167
167
|
openclaw gateway restart
|
|
@@ -172,7 +172,7 @@ openclaw gateway restart
|
|
|
172
172
|
```json
|
|
173
173
|
{
|
|
174
174
|
"channels": {
|
|
175
|
-
"
|
|
175
|
+
"lansenger": {
|
|
176
176
|
"appId": "your-appid-2",
|
|
177
177
|
"appSecret": "...",
|
|
178
178
|
"dmSecurity": "paired",
|
|
@@ -233,14 +233,14 @@ openclaw gateway call lansenger.status
|
|
|
233
233
|
{
|
|
234
234
|
agentId: "agent-a",
|
|
235
235
|
match: {
|
|
236
|
-
channel: "
|
|
236
|
+
channel: "lansenger",
|
|
237
237
|
peer: { kind: "direct", id: "2285568-xxx" },
|
|
238
238
|
},
|
|
239
239
|
},
|
|
240
240
|
{
|
|
241
241
|
agentId: "agent-a",
|
|
242
242
|
match: {
|
|
243
|
-
channel: "
|
|
243
|
+
channel: "lansenger",
|
|
244
244
|
peer: { kind: "group", id: "group-chat-id" },
|
|
245
245
|
},
|
|
246
246
|
},
|
|
@@ -249,7 +249,7 @@ openclaw gateway call lansenger.status
|
|
|
249
249
|
```
|
|
250
250
|
|
|
251
251
|
路由欄位:
|
|
252
|
-
* `match.channel`: `"
|
|
252
|
+
* `match.channel`: `"lansenger"`
|
|
253
253
|
* `match.peer.kind`: `"direct"`(私聊)或 `"group"`(羣組聊天)
|
|
254
254
|
* `match.peer.id`: 使用者 ID(`2285568-xxx`)或羣組聊天 ID
|
|
255
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,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
|