@lansenger-pm/openclaw-lansenger-channel 3.15.0 → 3.15.2
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/CHANGELOG.md +136 -44
- package/LICENSE +1 -1
- package/README.fr.md +15 -15
- package/README.zhHant.md +9 -9
- package/README.zhHantHK.md +3 -3
- package/dist/src/channel.js +19 -56
- package/dist/src/channel.js.map +1 -1
- package/dist/src/client.js +1 -1
- package/dist/src/client.js.map +1 -1
- package/dist/src/persistent-store.js +53 -0
- package/dist/src/persistent-store.js.map +1 -0
- package/dist/src/runtime.js +89 -57
- package/dist/src/runtime.js.map +1 -1
- package/dist/src/tools.js +233 -256
- package/dist/src/tools.js.map +1 -1
- package/package.json +3 -2
- package/dist/index.d.ts +0 -11
- package/dist/setup-entry.d.ts +0 -8
- package/dist/src/channel.d.ts +0 -23
- package/dist/src/client.d.ts +0 -208
- package/dist/src/runtime.d.ts +0 -8
- package/dist/src/setup-wizard.d.ts +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,112 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [3.15.2] - 2026-06-18
|
|
8
|
+
|
|
9
|
+
> **Compatible with OpenClaw `^2026.6.1`** (tested against `2026.6.1`).
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
- **Slash commands produce no reply**: `ctxPayload.CommandAuthorized` was hardcoded to `undefined`, causing the agent core to skip command execution and treat `/reset`/`/new` as plain agent text. Now set to the actual `resolveChannelMessageIngress` authorization result.
|
|
14
|
+
|
|
15
|
+
## [3.15.1] - 2026-06-18
|
|
16
|
+
|
|
17
|
+
> **Compatible with OpenClaw `^2026.6.1`** (tested against `2026.6.1`).
|
|
18
|
+
>
|
|
19
|
+
> Supersedes the deprecated `3.15.0` (published by mistake on 2026-06-04). Includes all fixes from `3.14.5` through `3.14.8`.
|
|
20
|
+
|
|
21
|
+
### Bug Fixes (v3.14.5 → v3.14.8)
|
|
22
|
+
|
|
23
|
+
- **Group chat reply routing**: Outbound adapters created fresh client instances with empty `chatTypeMap`, causing group replies to route via private-message API.
|
|
24
|
+
- **`lansenger_send_*` tools multi-account routing**: Tools used `getRunningClient()` which always returned the first account's client. Changed to factory pattern with `ctx.agentAccountId`.
|
|
25
|
+
- **Command authorization ignores global `commands.ownerAllowFrom`**: Slash commands blocked when owner configured globally rather than per-account.
|
|
26
|
+
- **Multi-account audit**: Outbound adapters, status snapshot, reply fallback delivery, and approval card store all now use per-account lookup via `getRunningEntryByAccount()`.
|
|
27
|
+
|
|
28
|
+
### Chore
|
|
29
|
+
|
|
30
|
+
- Removed dead imports (`getRunningClient`, `getLastInboundTime`) from channel.ts after multi-account refactor.
|
|
31
|
+
|
|
32
|
+
## [3.14.8] - 2026-06-18
|
|
33
|
+
|
|
34
|
+
> **Compatible with OpenClaw `^2026.6.1`** (tested against `2026.6.1`).
|
|
35
|
+
|
|
36
|
+
### Bug Fixes
|
|
37
|
+
|
|
38
|
+
- **Command authorization ignores global `commands.ownerAllowFrom`**: Slash commands (`/reset`, etc.) were blocked when `allowFrom` was only configured at the global `commands.ownerAllowFrom` level rather than per-account. Fixed by extracting `lansenger:`-prefixed entries from the global config and passing them as `commandOwnerAllowFrom` to `resolveChannelMessageIngress`.
|
|
39
|
+
- **Multi-account audit — outbound adapters**: 7 outbound adapter methods (`sendText`, `sendMedia`, `sendFormattedText`, etc.) used `getRunningClient()` which returns the first running account's client. In multi-bot setups, this could deliver messages using the wrong account's credentials. Changed to `getRunningEntryByAccount(account.accountId)` to match the correct running entry.
|
|
40
|
+
- **Multi-account audit — status snapshot**: `buildAccountSnapshot` used `getRunningClient()` for connected state and `getLastInboundTime()` for last activity, both returning data from the first running account instead of the account being probed. Added `getLastInboundTimeByAccount()` and switched to per-account lookup.
|
|
41
|
+
- **Multi-account audit — reply fallback delivery**: The `reply_payload_sending` fallback hook used `getRunningClient()` with no account resolution, potentially delivering replies via the wrong bot. Now prefers per-account resolution via `getRunningEntryByAccount()`.
|
|
42
|
+
- **Multi-account audit — approval card store collision**: `pendingApprovalCards` was keyed by chatId only, risking cross-account overwrites when different Lansenger instances shared chatIds. Now keyed by `accountId:chatId`.
|
|
43
|
+
|
|
44
|
+
## [3.14.7] - 2026-06-18
|
|
45
|
+
|
|
46
|
+
> **Compatible with OpenClaw `^2026.6.1`** (tested against `2026.6.1`).
|
|
47
|
+
|
|
48
|
+
### Bug Fixes
|
|
49
|
+
|
|
50
|
+
- **`lansenger_send_*` tools multi-account routing**: All `lansenger_send_*` tools used `getRunningClient()` which always returned the first entry from `runningAccounts` Map, causing cross-account message sending in multi-bot setups. Fixed by:
|
|
51
|
+
- Added `getRunningEntryByAccount(accountId)` in runtime.ts that matches by account key/ID, with single-account fallback.
|
|
52
|
+
- Changed tool registration to use OpenClaw's tool factory pattern (`api.registerTool((ctx) => {...})`), resolving the correct client via `ctx.agentAccountId` from the OpenAI-compatible tool context.
|
|
53
|
+
|
|
54
|
+
## [3.14.6] - 2026-06-17
|
|
55
|
+
|
|
56
|
+
> **Hotfix**: v3.14.5 was published with a broken `dist/src/runtime.js` that imports `./persistent-store.js` but the file was missing.
|
|
57
|
+
|
|
58
|
+
### Bug Fixes
|
|
59
|
+
|
|
60
|
+
- **Missing `persistent-store.js` in npm bundle**: `src/persistent-store.ts` was uncommitted but referenced by modified `src/runtime.ts`. `prepublishOnly` ran `tsc` against the working directory, producing `dist/src/runtime.js` with the import, but the compiled `dist/src/persistent-store.js` was excluded from the npm tarball. Now committed and included.
|
|
61
|
+
|
|
62
|
+
## [3.14.5] - 2026-06-17
|
|
63
|
+
|
|
64
|
+
> **Compatible with OpenClaw `^2026.6.1`** (tested against `2026.6.1`).
|
|
65
|
+
|
|
66
|
+
### Bug Fixes
|
|
67
|
+
|
|
68
|
+
- **Group chat reply routing**: Outbound adapters (`sendText`, `sendMedia`, `sendFormattedText`, `sendPayload`, `beforeDeliverPayload`) created fresh `LansengerClient` instances via `makeClient()`, which had an empty `chatTypeMap` cache. `isGroupChat()` then fell back to `chatId.startsWith("group:")` — which never matches Lansenger group IDs — causing all group replies to be sent via private-message API instead of group-message API. Fixed by using `getRunningClient() ?? makeClient()` so the cached client (with populated `chatTypeMap`) handles routing.
|
|
69
|
+
|
|
70
|
+
## [3.14.4] - 2026-06-15
|
|
71
|
+
|
|
72
|
+
> **Compatible with OpenClaw `^2026.6.1`** (tested against `2026.6.1`).
|
|
73
|
+
|
|
74
|
+
### Bug Fixes
|
|
75
|
+
|
|
76
|
+
- **`recoverPendingInboundContexts` duplicate restart notice**: Gateway restart-recovery sent multiple "system restart" notices to the same chat due to two issues:
|
|
77
|
+
1. `startLansengerGateway` was called from both runtime extension and channel extension initialization paths, causing `recoverPendingInboundContexts` to execute twice. Added a module-level `recoveryGuard` flag for idempotency.
|
|
78
|
+
2. Even within a single invocation, multiple pending contexts for the same `chatId` would each trigger a separate notice. Added `chatId` deduplication — only one notice per chat per recovery cycle.
|
|
79
|
+
- **`recoverPendingInboundContexts` duplicate ack revoke**: Same guard prevents the stale ack message from being revoked twice.
|
|
80
|
+
- **WS reconnection without exponential backoff**: `backoffIdx` was reset to 0 immediately after `new WebSocket()` instead of on actual connection (`ws.onopen`). This caused every reconnect attempt to use a fixed 2s delay and log `attempt 1` regardless of retry count. Moved `this.backoffIdx = 0` into `ws.onopen` so the `[2, 5, 10, 30, 60]` backoff array is properly utilized.
|
|
81
|
+
|
|
82
|
+
## [3.14.3] - 2026-06-04
|
|
83
|
+
|
|
84
|
+
> **Compatible with OpenClaw `^2026.6.1`** (tested against `2026.6.1`).
|
|
85
|
+
|
|
86
|
+
### Features
|
|
87
|
+
|
|
88
|
+
- **Gateway restart-recovery inbound context persistence**: Active inbound sessions are persisted to `~/.openclaw/lansenger-inbound-contexts.json` before `inbound.run`. On gateway restart/plugin upgrade, `recoverPendingInboundContexts` detects interrupted sessions, revokes stale ack messages, and sends a "system restart, reprocessing..." notification. Contexts older than 5 minutes are discarded.
|
|
89
|
+
- **User notification**: Sends a locale-aware restart notice (`zh`/`en`) to the affected chat after recovery.
|
|
90
|
+
|
|
91
|
+
## [3.14.2] - 2026-06-04
|
|
92
|
+
|
|
93
|
+
> **Compatible with OpenClaw `^2026.6.1`** (tested against `2026.6.1`).
|
|
94
|
+
|
|
95
|
+
### Features
|
|
96
|
+
|
|
97
|
+
- **`reply_payload_sending` fallback delivery**: Tracks active `inbound.run` sessions via `activeDeliverySessions` set. When `reply_payload_sending` fires for a session without an active `inbound.run` (e.g., after gateway restart/turn timeout), the reply is delivered directly via the Lansenger client instead of being silently dropped. Extracts `chatId` from `sessionKey` to resolve delivery target.
|
|
98
|
+
|
|
99
|
+
## [3.14.1] - 2026-06-04
|
|
100
|
+
|
|
101
|
+
> **Compatible with OpenClaw `^2026.6.1`** (tested against `2026.6.1`).
|
|
102
|
+
|
|
103
|
+
### Bug Fixes
|
|
104
|
+
|
|
105
|
+
- **`ackMessage` default**: Changed from `false` to `true`; `revokeAckMessage` from `true` to `false`, aligning with the `after_agent_dispatch` ack policy.
|
|
106
|
+
- **`pendingApprovalCards` persistence**: Migrated from in-memory `Map` to `~/.openclaw/lansenger-approval-cards.json` file-backed store, surviving gateway restarts.
|
|
107
|
+
- **Group ingress fallback**: Added `requireMention` + `isAtMe` check to prevent non-@ messages from triggering the bot.
|
|
108
|
+
- **`isPathAllowed` security hardening**: Empty `mediaLocalRoots` now defaults to `[cwd, tmpdir]` instead of allowing any path.
|
|
109
|
+
- **Dead code removal**: Removed unused `debounceMs` statement in `runtime.ts`.
|
|
110
|
+
- **`convertPxToPtDeep` → `convertPxToPtCard`**: Only transforms known style fields; skips URL/link fields to avoid corruption.
|
|
111
|
+
- **Documentation**: All 5 locale READMEs updated with new defaults and `mediaLocalRoots` docs.
|
|
112
|
+
|
|
7
113
|
## [3.14.0] - 2026-06-04
|
|
8
114
|
|
|
9
115
|
> **Compatible with OpenClaw `^2026.6.1`** (tested against `2026.6.1`).
|
|
@@ -25,60 +131,43 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/), and this
|
|
|
25
131
|
- **Test coverage**: 4 new tests via `verifyChannelMessageReceiveAckPolicyAdapterProofs` and `listDeclaredReceiveAckPolicies`, verifying all declared policies pass SDK contract proofs.
|
|
26
132
|
- This replaces the previously implicit ack timing embedded in the `runtime.ts` monitor-local state with a standardized, SDK-verifiable contract.
|
|
27
133
|
|
|
28
|
-
## [3.13.
|
|
134
|
+
## [3.13.1] - 2026-06-03
|
|
29
135
|
|
|
30
136
|
> **Compatible with OpenClaw `^2026.5.28`** (tested against `2026.5.28`).
|
|
31
137
|
|
|
32
|
-
|
|
138
|
+
- migrate `message_sending` from `api.registerHook` to `api.on("message_sending", ...)` (OpenClaw 2026.5.28 rejects `registerHook` for typed hooks that lack a name)
|
|
139
|
+
|
|
140
|
+
## [3.13.0] - 2026-06-03
|
|
33
141
|
|
|
34
|
-
|
|
35
|
-
- **`message_sending` hook registration missing name**: `api.registerHook("message_sending", ...)` now fails at gateway startup with "hook registration missing name" (OpenClaw 2026.5.28 rejects `registerHook` calls for typed hooks that lack a name). Migrated `message_sending` to `api.on("message_sending", ...)` — the typed lifecycle hook API — alongside the existing `reply_payload_sending` hook.
|
|
142
|
+
> **Compatible with OpenClaw `^2026.5.28`** (tested against `2026.5.28`).
|
|
36
143
|
|
|
37
|
-
|
|
144
|
+
- Add `dist/src/setup-i18n.*` to `files` field in package.json (fixes `Cannot find module './setup-i18n.js'` at gateway startup)
|
|
38
145
|
|
|
39
|
-
|
|
40
|
-
- **`message_sending` hook**: Register typed plugin hook via `api.on()` for early-stage reply interception.
|
|
41
|
-
- **`normalizePayload` — code-block detection**: Payloads containing `\`\`\`` code fences are now marked `_lansengerFormatText: true` even when they also carry `mediaUrl` or `presentation`. Previously, code-rich agent replies with attachments fell back to plain-text `msgType`, losing Markdown formatting. Now they always render via `formatText`.
|
|
42
|
-
- **`beforeDeliverPayload` — approval-resolved crash recovery**: When `hint.kind === "approval-resolved"`, the delivery pipeline now proactively updates the original appCard status via `updateDynamicCard`. Because the delivery pipeline replays on gateway restart, this provides crash-recovery safety.
|
|
43
|
-
- **`shouldSuppressLocalPayloadPrompt`**: Suppresses the local text prompt when the native approval route is active (`hint.kind === "approval-pending"` with `nativeRouteActive === true`). Prevents duplicate approval prompts.
|
|
44
|
-
- **`pendingApprovalCards` Map**: Tracks sent approval card `messageId`s keyed by `chatId`. Enables `beforeDeliverPayload` to correlate approval-resolved payloads with the original card for status updates.
|
|
45
|
-
- **Text chunking**: Add `textChunkLimit: 4000`, `chunkerMode: "markdown"`, and `chunker` (using SDK `chunkMarkdownTextWithMode`). Long agent replies are automatically split into multiple Lansenger messages, preserving Markdown structure across chunks.
|
|
46
|
-
- **`resolveEffectiveTextChunkLimit`**: Uses SDK `resolveTextChunkLimit` to allow config-level override of the chunk limit.
|
|
47
|
-
- Add `normalizePayload` and `beforeDeliverPayload` callbacks on outbound base (structural preparation for outbound hooks).
|
|
48
|
-
- Add session-scoped delivery dedup (`sessionDeliveryTracker`) to prevent duplicate sends across turns in the same session.
|
|
146
|
+
## [3.12.2] - 2026-06-03
|
|
49
147
|
|
|
50
|
-
|
|
148
|
+
> **Compatible with OpenClaw `^2026.5.28`** (tested against `2026.5.28`).
|
|
51
149
|
|
|
52
|
-
-
|
|
53
|
-
-
|
|
54
|
-
-
|
|
55
|
-
- Pin npm install spec to exact version (`@lansenger-pm/openclaw-lansenger-channel@3.13.0`) to prevent supply-chain attacks and accidental upgrades.
|
|
56
|
-
- Bump `openclaw` devDependency from `^2026.5.20` to `^2026.5.28`.
|
|
57
|
-
- Split changelog out of READMEs into standalone `CHANGELOG.md` (all 5 locale READMEs now reference this file).
|
|
150
|
+
- **Outbound Hook Expansion**: `reply_payload_sending` hook, `message_sending` hook, `normalizePayload` code-block detection, `beforeDeliverPayload` approval-resolved crash recovery, `shouldSuppressLocalPayloadPrompt`, `pendingApprovalCards` Map, text chunking (`textChunkLimit: 4000`, `chunkerMode: "markdown"`), `resolveEffectiveTextChunkLimit`, session-scoped delivery dedup (`sessionDeliveryTracker`)
|
|
151
|
+
- **Approval Flow Enhancement**: `approvalCapability.transport.send` stores card `messageId` in `pendingApprovalCards`; card status updates via both `transport.update` and `beforeDeliverPayload` (crash-recovery safety)
|
|
152
|
+
- **Setup Wizard i18n**: `createSetupTranslator()` + plugin-owned dictionary, locales `en`/`zh-CN`/`zh-TW`, `src/setup-i18n.ts`
|
|
58
153
|
|
|
59
|
-
|
|
154
|
+
## [3.12.1] - 2026-06-03
|
|
60
155
|
|
|
61
|
-
|
|
62
|
-
- Card status updates (pending → approved/denied) now happen via both `transport.update` (approval framework) and `beforeDeliverPayload` (delivery pipeline). The latter provides crash-recovery safety.
|
|
156
|
+
> **Compatible with OpenClaw `^2026.5.28`** (tested against `2026.5.28`).
|
|
63
157
|
|
|
64
|
-
|
|
158
|
+
- **Security**: SecretRef support for appSecret, plaintext appSecret warning, SSRF protection for `sendImageUrl`, `dangerouslyAllowPrivateNetwork` config, `mediaLocalRoots` config, path validation (`isPathAllowed`)
|
|
159
|
+
- Pin npm install spec to exact version (`@lansenger-pm/openclaw-lansenger-channel@3.12.1`)
|
|
160
|
+
- Thread canonical `sessionKey` into outbound hooks for multi-session/multi-agent routing
|
|
161
|
+
- Fallback `sessionKey` includes `accountId` for multi-bot scenarios
|
|
162
|
+
- Bump `openclaw` devDependency from `^2026.5.20` to `^2026.5.28`
|
|
65
163
|
|
|
66
|
-
|
|
67
|
-
- Supported locales: **`en`**, **`zh-CN`**, **`zh-TW`**. French and other locales fall back to English for wizard prompts (READMEs remain 5-language).
|
|
68
|
-
- Common strings (status labels, "Docs:" prefix, etc.) use the built-in OpenClaw catalog keys (`wizard.channels.*`). Plugin-specific strings use a local dictionary with `lt()`.
|
|
69
|
-
- New file: `src/setup-i18n.ts` — locale resolver + 40+ entry dictionary.
|
|
164
|
+
## [3.12.0] - 2026-06-03
|
|
70
165
|
|
|
71
|
-
|
|
166
|
+
> **Compatible with OpenClaw `^2026.5.27`** (tested against `2026.5.27`).
|
|
72
167
|
|
|
73
|
-
- **
|
|
74
|
-
-
|
|
75
|
-
-
|
|
76
|
-
- **`securityNote` in setup wizard**: A conditional note that appears only when a plaintext appSecret exists, with locale-appropriate migration instructions.
|
|
77
|
-
- **README security advisory**: All 5 locale READMEs now include a prominent `⚠️ Security: Migrate appSecret to SecretRef storage` block.
|
|
78
|
-
- **SSRF protection for sendImageUrl**: `assertHttpUrlTargetsPrivateNetwork()` blocks RFC1918/link-local/metadata-IP targets by default.
|
|
79
|
-
- **`dangerouslyAllowPrivateNetwork` config**: Opt-in at top-level or per-account to allow private network image URLs. Audit finding (`lansenger/dangerously-allow-private-network`) warns when enabled.
|
|
80
|
-
- **`mediaLocalRoots` config**: Restrict local file delivery to configured directories. Prevents agents from accessing arbitrary files outside allowed roots. Available at top-level and per-account.
|
|
81
|
-
- **Path validation**: `isPathAllowed()` validates local file paths against `mediaLocalRoots` before delivery; blocked paths are logged and skipped.
|
|
168
|
+
- **Breaking**: Migrate `api.runtime.channel.turn` → `api.runtime.channel.inbound` (OpenClaw removed the old `turn` runtime alias)
|
|
169
|
+
- Split changelog out of READMEs into standalone `CHANGELOG.md`
|
|
170
|
+
- All 5 locale READMEs now reference this file
|
|
82
171
|
|
|
83
172
|
## [3.11.0] - 2026-05-26
|
|
84
173
|
|
|
@@ -120,12 +209,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/), and this
|
|
|
120
209
|
|
|
121
210
|
> **Compatible with OpenClaw `^2026.5.20`** (tested against `2026.5.20`).
|
|
122
211
|
|
|
123
|
-
|
|
212
|
+
- Fix apiGatewayUrl lint check to skip when accounts have gateway set.
|
|
124
213
|
|
|
125
|
-
|
|
214
|
+
## [3.8.1] - 2026-05-20
|
|
215
|
+
|
|
216
|
+
> **Compatible with OpenClaw `^2026.5.20`** (tested against `2026.5.20`).
|
|
217
|
+
|
|
218
|
+
- Add `security.collectWarnings` and `security.collectAuditFindings` for `openclaw doctor --lint` integration.
|
|
126
219
|
- Add `doctor.repairConfig` to auto-fix dmPolicy to pairing.
|
|
127
220
|
- Bump `openclaw` devDependency from `^2026.5.7` to `^2026.5.20`.
|
|
128
|
-
- Fix apiGatewayUrl lint check to skip when accounts have gateway set.
|
|
129
221
|
|
|
130
222
|
## [3.7.9] - 2026-05-18
|
|
131
223
|
|
|
@@ -388,4 +480,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/), and this
|
|
|
388
480
|
- Multi-language READMEs (en, zhHans, zhHant, zhHantHK, fr).
|
|
389
481
|
- DM security: pairing mode (default), allowlist, open, disabled.
|
|
390
482
|
- Approval workflow: appCard with `headStatusInfo` for pending/approved/denied states.
|
|
391
|
-
- Auto-start WebSocket gateway on plugin activation.
|
|
483
|
+
- Auto-start WebSocket gateway on plugin activation.
|
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2026
|
|
3
|
+
Copyright (c) 2026 Lanxin Mobile (Beijing) Technology Co., ltd.
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
package/README.fr.md
CHANGED
|
@@ -12,7 +12,7 @@ Connecte OpenClaw à Lansenger — une plateforme de messagerie d'entreprise —
|
|
|
12
12
|
## Fonctionnalités
|
|
13
13
|
|
|
14
14
|
- **Messagerie en temps réel** via connexion longue WebSocket
|
|
15
|
-
- **Support multi-
|
|
15
|
+
- **Support multi-robot** — lier plusieurs robots Lansenger à différents agents OpenClaw
|
|
16
16
|
- **Support Markdown** utilisant le msgType `formatText` (par défaut)
|
|
17
17
|
- **Fichiers/Images/Vocaux** via le msgType `text` avec upload de médias
|
|
18
18
|
- **Cartes d'approbation** — workflow d'approbation interactif avec mises à jour de statut en place (en attente → approuvé/refusé)
|
|
@@ -20,8 +20,8 @@ Connecte OpenClaw à Lansenger — une plateforme de messagerie d'entreprise —
|
|
|
20
20
|
- **Auto-routage via msgTarget** — toutes les méthodes d'envoi routent automatiquement vers les API groupe ou DM (privé) ; pas de méthodes groupe/privé séparées
|
|
21
21
|
- **@Mentions** — support @tout et @utilisateurs spécifiques dans les chats de groupe
|
|
22
22
|
- **Traitement des médias entrants** — téléchargement d'images/fichiers/vocaux, détection d'extension, chemins de fichiers pour l'agent
|
|
23
|
-
- **Révocation de messages** — révoquer les messages précédemment envoyés (chatType :
|
|
24
|
-
- **Démarrage automatique** — la passerelle connecte automatiquement tous les comptes de
|
|
23
|
+
- **Révocation de messages** — révoquer les messages précédemment envoyés (chatType : robot ou groupe uniquement)
|
|
24
|
+
- **Démarrage automatique** — la passerelle connecte automatiquement tous les comptes de robots configurés au démarrage
|
|
25
25
|
- **Debounce entrant** — fusionner les messages consécutifs rapides du même émetteur via la config OpenClaw `messages.inbound.debounceMs`
|
|
26
26
|
- **Message ack** — envoyer un message « reçu, en cours de traitement... » avant le traitement de l'agent ; auto-révoqué après la réponse ; langue auto-détectée
|
|
27
27
|
- **Zéro modification du core** — mode plugin pur, `git diff HEAD` reste INTACT
|
|
@@ -102,7 +102,7 @@ openclaw gateway restart
|
|
|
102
102
|
|
|
103
103
|
### Premier message
|
|
104
104
|
|
|
105
|
-
Après le redémarrage, le
|
|
105
|
+
Après le redémarrage, le robot se connecte automatiquement via WebSocket. Envoyez un DM au robot — vous recevrez un code de pairage. Approuvez-le :
|
|
106
106
|
|
|
107
107
|
```bash
|
|
108
108
|
openclaw pairing approve lansenger <code>
|
|
@@ -116,8 +116,8 @@ Ajoutez ces variables à `~/.openclaw/.env` ou à votre environnement :
|
|
|
116
116
|
|
|
117
117
|
| Variable | Description | Exemple |
|
|
118
118
|
|----------|-------------|---------|
|
|
119
|
-
| `LANSENGER_APP_ID` | App ID du
|
|
120
|
-
| `LANSENGER_APP_SECRET` | App Secret du
|
|
119
|
+
| `LANSENGER_APP_ID` | App ID du robot personnel | `your-appid` |
|
|
120
|
+
| `LANSENGER_APP_SECRET` | App Secret du robot personnel | `ABCDEF123456...` |
|
|
121
121
|
| `LANSENGER_API_GATEWAY_URL` | URL de la passerelle API Lansenger (remplacement) | `https://open.e.lanxin.cn/open/apigw` |
|
|
122
122
|
|
|
123
123
|
Les identifiants peuvent aussi être fournis via la configuration `openclaw.json` (voir Configuration optionnelle ci-dessous). Les valeurs de configuration sont prioritaires ; les variables d'environnement sont utilisées comme repli lorsque la configuration n'est pas définie.
|
|
@@ -165,8 +165,8 @@ Les identifiants peuvent aussi être fournis via la configuration `openclaw.json
|
|
|
165
165
|
|
|
166
166
|
| Champ | Description | Valeur par défaut |
|
|
167
167
|
|-------|-------------|-------------------|
|
|
168
|
-
| `appId` | App ID du
|
|
169
|
-
| `appSecret` | App Secret du
|
|
168
|
+
| `appId` | App ID du robot personnel | — |
|
|
169
|
+
| `appSecret` | App Secret du robot personnel | — |
|
|
170
170
|
| `apiGatewayUrl` | URL de la passerelle API | `https://open.e.lanxin.cn/open/apigw` |
|
|
171
171
|
| `homeChannel` | Chat ID par défaut pour la livraison cron/notification | — |
|
|
172
172
|
| `enabled` | Activer/désactiver le canal (défaut runtime : false sans identifiants) | `true` |
|
|
@@ -174,9 +174,9 @@ Les identifiants peuvent aussi être fournis via la configuration `openclaw.json
|
|
|
174
174
|
| `dmPolicy` | Politique DM : `pairing`, `allowlist`, `open`, `disabled` | `pairing` |
|
|
175
175
|
| `configWrites` | Autoriser Lansenger à écrire la config en réponse aux événements du canal | `true` |
|
|
176
176
|
| `name` | Nom d'affichage pour ce compte | — |
|
|
177
|
-
| `accounts` | Configuration multi-
|
|
177
|
+
| `accounts` | Configuration multi-robot | — |
|
|
178
178
|
| `groupPolicy` | Politique de groupe : `open` (tous les groupes), `allowlist` (groupes autorisés uniquement), `disabled` (messages de groupe désactivés) | `allowlist` |
|
|
179
|
-
| `groupAllowFrom` | IDs de groupes autorisés à déclencher le
|
|
179
|
+
| `groupAllowFrom` | IDs de groupes autorisés à déclencher le robot | `[]` |
|
|
180
180
|
| `groups` | Configuration par groupe (requireMention, enabled, allowFrom) | — |
|
|
181
181
|
| `ackMessage` | Envoyer un message de confirmation avant le traitement de l'agent | `true` |
|
|
182
182
|
| `revokeAckMessage` | Révoquer automatiquement le message ack après la réponse de l'agent. Mettre `false` pour garder le message visible (certains utilisateurs préfèrent voir le message plutôt qu'une notification « message révoqué ») | `false` |
|
|
@@ -208,12 +208,12 @@ Lorsqu'un utilisateur envoie plusieurs messages rapides consécutifs, le mécani
|
|
|
208
208
|
- Les messages média et commandes de contrôle ne sont PAS debounceés — ils sont traités immédiatement
|
|
209
209
|
- Quand le debounce est actif, les textes fusionnés sont joints par `\n` ; les chemins média sont concaténés ; les métadonnées du dernier message sont utilisées
|
|
210
210
|
|
|
211
|
-
### Configuration multi-
|
|
211
|
+
### Configuration multi-robot
|
|
212
212
|
|
|
213
|
-
Pour ajouter plusieurs
|
|
213
|
+
Pour ajouter plusieurs robots, utilisez `openclaw config set` avec la structure `accounts` :
|
|
214
214
|
|
|
215
215
|
```bash
|
|
216
|
-
# Ajouter un deuxième
|
|
216
|
+
# Ajouter un deuxième robot (remplacez appid/appsecret/gateway par vos valeurs)
|
|
217
217
|
openclaw config set channels.lansenger.accounts.your-appid-2.appId "your-appid-2"
|
|
218
218
|
openclaw config set channels.lansenger.accounts.your-appid-2.appSecret "your-appsecret"
|
|
219
219
|
openclaw config set channels.lansenger.accounts.your-appid-2.apiGatewayUrl "https://apigw.lx.qianxin.com"
|
|
@@ -348,7 +348,7 @@ Le plugin supporte les cartes d'approbation :
|
|
|
348
348
|
## Notes importantes
|
|
349
349
|
|
|
350
350
|
- **Pas de chat staff** — Lansenger n'a que les chats de groupe et DM (privé) ; il n'existe pas de concept de « chat staff ».
|
|
351
|
-
- **Révocation chatType** — uniquement `
|
|
351
|
+
- **Révocation chatType** — uniquement `robot` ou `group` ; pas de chatType `staff`.
|
|
352
352
|
- **Pas de sysMsg sur révocation** — l'API accepte `sysMsg` mais ne l'affiche pas.
|
|
353
353
|
- **Pas de deleteMessage** — l'API retourne l'erreur 10000 ; la suppression n'est pas disponible.
|
|
354
354
|
- **appArticles** — utilise le champ `summary` (pas `description`).
|
|
@@ -432,7 +432,7 @@ Voir [Documentation des appareils OpenClaw](https://docs.openclaw.ai/cli/devices
|
|
|
432
432
|
|
|
433
433
|
### "Le client mobile ne permet pas de voir les identifiants"
|
|
434
434
|
|
|
435
|
-
Utilisez uniquement le **client Lansenger (desktop)**. L'application mobile n'affiche pas les identifiants du
|
|
435
|
+
Utilisez uniquement le **client Lansenger (desktop)**. L'application mobile n'affiche pas les identifiants du robot.
|
|
436
436
|
|
|
437
437
|
### "No binding for botId"
|
|
438
438
|
|
package/README.zhHant.md
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
- **入站媒體處理**——下載圖片/檔案/語音,偵測副檔名,向代理提供檔案路徑
|
|
23
23
|
- **訊息撤回**——撤回已傳送的訊息(chatType 僅支援 bot 與 group)
|
|
24
24
|
- **自動啟動**——閘道啟動時自動連接所有已設定的機器人帳戶
|
|
25
|
-
- **入站防抖合併**——利用 OpenClaw 的 `messages.inbound.debounceMs`
|
|
25
|
+
- **入站防抖合併**——利用 OpenClaw 的 `messages.inbound.debounceMs` 配置,合併同一傳送者的連續快速訊息
|
|
26
26
|
- **確認訊息**——在代理處理前傳送「收到,正在處理...」確認訊息,代理回覆後自動撤回,語言自動偵測
|
|
27
27
|
- **零核心修改**——純插件模式,`git diff HEAD` 保持 PRISTINE
|
|
28
28
|
|
|
@@ -50,13 +50,13 @@
|
|
|
50
50
|
|
|
51
51
|
| 工具 | 說明 |
|
|
52
52
|
|------|------|
|
|
53
|
-
| `lansenger_send_text` |
|
|
54
|
-
| `lansenger_send_format_text` |
|
|
55
|
-
| `lansenger_send_file` |
|
|
56
|
-
| `lansenger_send_image_url` | 透過 URL
|
|
57
|
-
| `lansenger_send_link_card` |
|
|
58
|
-
| `lansenger_send_app_card` |
|
|
59
|
-
| `lansenger_send_app_articles` |
|
|
53
|
+
| `lansenger_send_text` | 傳送純文字訊息,不支援 Markdown |
|
|
54
|
+
| `lansenger_send_format_text` | 傳送 Markdown 格式文字,支援 @提及 |
|
|
55
|
+
| `lansenger_send_file` | 傳送檔案/圖片/影片/語音(工作區或外部路徑) |
|
|
56
|
+
| `lansenger_send_image_url` | 透過 URL 傳送圖片 |
|
|
57
|
+
| `lansenger_send_link_card` | 傳送富連結預覽卡片 |
|
|
58
|
+
| `lansenger_send_app_card` | 傳送互動/審批卡片 |
|
|
59
|
+
| `lansenger_send_app_articles` | 傳送多文章卡片 |
|
|
60
60
|
| `lansenger_update_dynamic_card` | 原地更新動態卡片狀態 |
|
|
61
61
|
| `lansenger_revoke_message` | 撤回已傳送的訊息 |
|
|
62
62
|
| `lansenger_query_groups` | 查詢可用群組 |
|
|
@@ -201,7 +201,7 @@ openclaw pairing approve lansenger <配對碼>
|
|
|
201
201
|
|
|
202
202
|
| 欄位 | 說明 | 預設值 |
|
|
203
203
|
|------|------|--------|
|
|
204
|
-
| `messages.inbound.debounceMs` |
|
|
204
|
+
| `messages.inbound.debounceMs` | 全域防抖視窗(毫秒);同一傳送者在視窗內的連續訊息會被合併 | `0`(停用) |
|
|
205
205
|
| `messages.inbound.byChannel.lansenger` | 藍信頻道專屬覆蓋(優先於全域) | — |
|
|
206
206
|
| `messages.queue.mode` | 代理處理中的佇列模式:`steer`、`followup`、`collect`、`queue`、`interrupt` | `steer`(推薦) |
|
|
207
207
|
|
package/README.zhHantHK.md
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
- **即時訊息** — 透過 WebSocket 長連線實現
|
|
15
15
|
- **多機械人支援** — 將多個藍信機械人綁定至不同的 OpenClaw 代理
|
|
16
16
|
- **Markdown 支援** — 使用 `formatText` msgType(預設)
|
|
17
|
-
- **檔案/圖片/語音附件** — 透過 `text` msgType
|
|
17
|
+
- **檔案/圖片/語音附件** — 透過 `text` msgType 上載媒體
|
|
18
18
|
- **審批卡片**——互動式審批流程,支援原地狀態更新(待審批 → 已通過/已拒絕)
|
|
19
19
|
- **語言偵測** — 自動偵測使用者語言,提供本地化回應
|
|
20
20
|
- **msgTarget 自動路由** — 所有發送方法自動路由至群組聊天或私聊(DM)API;無需分別的群組/私聊方法
|
|
@@ -83,7 +83,7 @@ openclaw gateway restart
|
|
|
83
83
|
|
|
84
84
|
> **可選**:安裝 `lansenger-cli` 作為 CLI 替代方案:`pipx install lansenger-cli`。
|
|
85
85
|
|
|
86
|
-
>
|
|
86
|
+
> **自訂網關**:企業私有化部署(如奇安信)需在設定後透過 `openclaw.json` 或環境變數設定 `apiGatewayUrl` — 見[可選設定](#可選設定)。
|
|
87
87
|
|
|
88
88
|
### 開發安裝(本地連結)
|
|
89
89
|
|
|
@@ -180,7 +180,7 @@ openclaw pairing approve lansenger <配對碼>
|
|
|
180
180
|
| `groups` | 羣級設定(requireMention、enabled、allowFrom) | — |
|
|
181
181
|
| `ackMessage` | 在代理處理前發送確認訊息 | `true` |
|
|
182
182
|
| `revokeAckMessage` | 代理回覆遞送後自動撤回確認訊息。設為 `false` 則保留確認訊息可見(有些使用者偏好看到確認訊息而非撤回的系統通知) | `false` |
|
|
183
|
-
| `mediaLocalRoots` |
|
|
183
|
+
| `mediaLocalRoots` | 透過媒體發送本地檔案時允許的根目錄列表;空陣列預設為工作區 + `/tmp` | `[cwd, /tmp]` |
|
|
184
184
|
| `ackMessageTextZh` | 中文確認訊息文案 | `收到,正在處理...` |
|
|
185
185
|
| `ackMessageTextEn` | 英文確認訊息文案 | `Received, processing...` |
|
|
186
186
|
|
package/dist/src/channel.js
CHANGED
|
@@ -8,59 +8,20 @@ import { coerceSecretRef } from "openclaw/plugin-sdk/secret-ref-runtime";
|
|
|
8
8
|
import { isPrivateNetworkOptInEnabled } from "openclaw/plugin-sdk/ssrf-policy";
|
|
9
9
|
import { chunkMarkdownTextWithMode, resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-chunking";
|
|
10
10
|
import * as fs from "node:fs/promises";
|
|
11
|
-
import * as fsSync from "node:fs";
|
|
12
11
|
import * as os from "node:os";
|
|
13
12
|
import * as path from "node:path";
|
|
14
13
|
import * as crypto from "node:crypto";
|
|
15
14
|
import { LansengerClient, DEFAULT_API_GATEWAY_URL } from "./client.js";
|
|
16
15
|
import { createMessageReceiptFromOutboundResults, createChannelMessageAdapterFromOutbound, } from "openclaw/plugin-sdk/channel-outbound";
|
|
17
|
-
import {
|
|
16
|
+
import { getRunningEntryByAccount, getLastInboundTimeByAccount, stripOpenClawUuidSuffix, gatewayStartAccount, gatewayStopAccount } from "./runtime.js";
|
|
18
17
|
import { lansengerSetupWizard } from "./setup-wizard.js";
|
|
18
|
+
import { PersistentStore } from "./persistent-store.js";
|
|
19
19
|
const log = createSubsystemLogger("lansenger");
|
|
20
20
|
const LANSENGER_TEXT_CHUNK_LIMIT = 4000;
|
|
21
21
|
const APPROVAL_CARD_FILE = path.join(os.homedir(), ".openclaw", "lansenger-approval-cards.json");
|
|
22
|
-
class PersistentApprovalCardStore {
|
|
23
|
-
data = new Map();
|
|
22
|
+
class PersistentApprovalCardStore extends PersistentStore {
|
|
24
23
|
constructor() {
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
load() {
|
|
28
|
-
try {
|
|
29
|
-
const raw = fsSync.readFileSync(APPROVAL_CARD_FILE, "utf-8");
|
|
30
|
-
const parsed = JSON.parse(raw);
|
|
31
|
-
if (parsed && typeof parsed === "object") {
|
|
32
|
-
for (const [k, v] of Object.entries(parsed)) {
|
|
33
|
-
this.data.set(k, v);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
catch { }
|
|
38
|
-
}
|
|
39
|
-
save() {
|
|
40
|
-
try {
|
|
41
|
-
const dir = path.dirname(APPROVAL_CARD_FILE);
|
|
42
|
-
fsSync.mkdirSync(dir, { recursive: true });
|
|
43
|
-
const obj = {};
|
|
44
|
-
for (const [k, v] of this.data)
|
|
45
|
-
obj[k] = v;
|
|
46
|
-
fsSync.writeFileSync(APPROVAL_CARD_FILE, JSON.stringify(obj), "utf-8");
|
|
47
|
-
}
|
|
48
|
-
catch (e) {
|
|
49
|
-
log.warn(`failed to persist approval cards: ${e instanceof Error ? e.message : String(e)}`);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
get(chatId) { return this.data.get(chatId); }
|
|
53
|
-
set(chatId, info) {
|
|
54
|
-
this.data.set(chatId, info);
|
|
55
|
-
this.save();
|
|
56
|
-
}
|
|
57
|
-
delete(chatId) {
|
|
58
|
-
this.data.delete(chatId);
|
|
59
|
-
this.save();
|
|
60
|
-
}
|
|
61
|
-
clear() {
|
|
62
|
-
this.data.clear();
|
|
63
|
-
this.save();
|
|
24
|
+
super(APPROVAL_CARD_FILE, "approval cards");
|
|
64
25
|
}
|
|
65
26
|
}
|
|
66
27
|
const pendingApprovalCards = new PersistentApprovalCardStore();
|
|
@@ -456,14 +417,14 @@ const chatPlugin = createChatChannelPlugin({
|
|
|
456
417
|
sendText: async (ctx) => {
|
|
457
418
|
const sessionKey = ctx.sessionKey;
|
|
458
419
|
const account = resolveAccount(ctx.cfg, ctx.accountId ?? undefined);
|
|
459
|
-
const client = makeClient(account);
|
|
420
|
+
const client = getRunningEntryByAccount(account.accountId ?? "")?.client ?? makeClient(account);
|
|
460
421
|
const result = await client.sendFormatText(ctx.to, ctx.text);
|
|
461
422
|
return toOutboundResult(result.messageId, sessionKey, ctx.to);
|
|
462
423
|
},
|
|
463
424
|
sendMedia: async (ctx) => {
|
|
464
425
|
const sessionKey = ctx.sessionKey;
|
|
465
426
|
const account = resolveAccount(ctx.cfg, ctx.accountId ?? undefined);
|
|
466
|
-
const client = makeClient(account);
|
|
427
|
+
const client = getRunningEntryByAccount(account.accountId ?? "")?.client ?? makeClient(account);
|
|
467
428
|
const caption = ctx.text ?? "";
|
|
468
429
|
if (ctx.mediaUrl && /^https?:\/\//i.test(ctx.mediaUrl)) {
|
|
469
430
|
const result = await client.sendImageUrl(ctx.to, ctx.mediaUrl, caption, account.dangerouslyAllowPrivateNetwork);
|
|
@@ -520,15 +481,17 @@ const chatPlugin = createChatChannelPlugin({
|
|
|
520
481
|
const sessionKey = params.sessionKey ?? "";
|
|
521
482
|
if (hint?.kind === "approval-resolved") {
|
|
522
483
|
const chatId = target?.to;
|
|
523
|
-
const
|
|
484
|
+
const acctId = target?.accountId ?? "";
|
|
485
|
+
const cardKey = acctId ? `${acctId}:${chatId}` : chatId;
|
|
486
|
+
const cardInfo = pendingApprovalCards.get(cardKey);
|
|
524
487
|
if (cardInfo?.messageId) {
|
|
525
488
|
const account = resolveAccount(params.cfg, target?.accountId ?? undefined);
|
|
526
|
-
const client = makeClient(account);
|
|
489
|
+
const client = getRunningEntryByAccount(account.accountId ?? "")?.client ?? makeClient(account);
|
|
527
490
|
const status = payload?.text?.includes("✅") ? "approved" : "denied";
|
|
528
491
|
try {
|
|
529
492
|
await client.updateCardStatus(cardInfo.messageId, status, cardInfo.lang);
|
|
530
493
|
log.info(`beforeDeliverPayload: approval card updated — messageId=${cardInfo.messageId} status=${status}`);
|
|
531
|
-
pendingApprovalCards.delete(
|
|
494
|
+
pendingApprovalCards.delete(cardKey);
|
|
532
495
|
}
|
|
533
496
|
catch (e) {
|
|
534
497
|
log.error(`beforeDeliverPayload: card update failed — ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -546,7 +509,7 @@ const chatPlugin = createChatChannelPlugin({
|
|
|
546
509
|
sendFormattedText: async (ctx) => {
|
|
547
510
|
const sessionKey = ctx.sessionKey;
|
|
548
511
|
const account = resolveAccount(ctx.cfg, ctx.accountId ?? undefined);
|
|
549
|
-
const client = makeClient(account);
|
|
512
|
+
const client = getRunningEntryByAccount(account.accountId ?? "")?.client ?? makeClient(account);
|
|
550
513
|
const result = await client.sendFormatText(ctx.to, ctx.text);
|
|
551
514
|
return [{ channel: "lansenger", messageId: result.messageId ?? "", sessionKey }];
|
|
552
515
|
},
|
|
@@ -572,7 +535,7 @@ const chatPlugin = createChatChannelPlugin({
|
|
|
572
535
|
const lansengerOutboundBridge = {
|
|
573
536
|
sendText: async (ctx) => {
|
|
574
537
|
const account = resolveAccount(ctx.cfg, ctx.accountId ?? undefined);
|
|
575
|
-
const client = makeClient(account);
|
|
538
|
+
const client = getRunningEntryByAccount(account.accountId ?? "")?.client ?? makeClient(account);
|
|
576
539
|
const result = await client.sendFormatText(ctx.to, ctx.text);
|
|
577
540
|
const receipt = result.messageId
|
|
578
541
|
? createMessageReceiptFromOutboundResults({ results: [{ messageId: result.messageId, chatId: ctx.to }], kind: "text", sentAt: Date.now() })
|
|
@@ -581,7 +544,7 @@ const lansengerOutboundBridge = {
|
|
|
581
544
|
},
|
|
582
545
|
sendMedia: async (ctx) => {
|
|
583
546
|
const account = resolveAccount(ctx.cfg, ctx.accountId ?? undefined);
|
|
584
|
-
const client = makeClient(account);
|
|
547
|
+
const client = getRunningEntryByAccount(account.accountId ?? "")?.client ?? makeClient(account);
|
|
585
548
|
const caption = ctx.text ?? "";
|
|
586
549
|
if (ctx.mediaUrl && /^https?:\/\//i.test(ctx.mediaUrl)) {
|
|
587
550
|
const result = await client.sendImageUrl(ctx.to, ctx.mediaUrl, caption, account.dangerouslyAllowPrivateNetwork);
|
|
@@ -630,7 +593,7 @@ const lansengerOutboundBridge = {
|
|
|
630
593
|
},
|
|
631
594
|
sendPayload: async (ctx) => {
|
|
632
595
|
const account = resolveAccount(ctx.cfg, ctx.accountId ?? undefined);
|
|
633
|
-
const client = makeClient(account);
|
|
596
|
+
const client = getRunningEntryByAccount(account.accountId ?? "")?.client ?? makeClient(account);
|
|
634
597
|
const payload = ctx.payload;
|
|
635
598
|
const hasCodeBlock = payload?.text && /```/.test(payload.text);
|
|
636
599
|
if (payload?.text && !payload?.mediaUrl && !payload?.presentation) {
|
|
@@ -901,9 +864,9 @@ export const lansengerPlugin = {
|
|
|
901
864
|
return await probeLansengerAccount(account);
|
|
902
865
|
},
|
|
903
866
|
buildAccountSnapshot: ({ account, runtime, probe }) => {
|
|
904
|
-
const
|
|
905
|
-
const connected =
|
|
906
|
-
const lastInboundAt =
|
|
867
|
+
const entry = getRunningEntryByAccount(account.accountId ?? "");
|
|
868
|
+
const connected = entry?.client.isWsAlive() ?? false;
|
|
869
|
+
const lastInboundAt = getLastInboundTimeByAccount(account.accountId ?? "");
|
|
907
870
|
return {
|
|
908
871
|
accountId: account.accountId ?? account.appId ?? DEFAULT_ACCOUNT_ID,
|
|
909
872
|
enabled: account.enabled,
|
|
@@ -1016,7 +979,7 @@ export const lansengerPlugin = {
|
|
|
1016
979
|
const appCard = (lang === "zh" ? payload.zh : payload.en) ?? payload.en;
|
|
1017
980
|
const result = await client.sendAppCard(target.to, appCard);
|
|
1018
981
|
if (result.messageId) {
|
|
1019
|
-
pendingApprovalCards.set(target.to
|
|
982
|
+
pendingApprovalCards.set(`${accountId}:${target.to}`, { messageId: result.messageId, lang: lang });
|
|
1020
983
|
}
|
|
1021
984
|
return { delivered: result.success, messageId: result.messageId ?? null };
|
|
1022
985
|
}
|