@openclaw/zalouser 2026.3.1 → 2026.3.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 +10 -0
- package/README.md +41 -147
- package/index.ts +1 -3
- package/package.json +4 -3
- package/src/accounts.test.ts +214 -0
- package/src/accounts.ts +17 -14
- package/src/channel.sendpayload.test.ts +117 -0
- package/src/channel.test.ts +123 -1
- package/src/channel.ts +244 -191
- package/src/config-schema.ts +1 -0
- package/src/group-policy.test.ts +49 -0
- package/src/group-policy.ts +78 -0
- package/src/message-sid.test.ts +66 -0
- package/src/message-sid.ts +80 -0
- package/src/monitor.account-scope.test.ts +123 -0
- package/src/monitor.group-gating.test.ts +216 -0
- package/src/monitor.ts +291 -225
- package/src/onboarding.ts +110 -142
- package/src/probe.test.ts +60 -0
- package/src/probe.ts +19 -12
- package/src/reaction.test.ts +19 -0
- package/src/reaction.ts +29 -0
- package/src/send.test.ts +116 -115
- package/src/send.ts +63 -117
- package/src/status-issues.test.ts +1 -15
- package/src/status-issues.ts +7 -26
- package/src/tool.test.ts +149 -0
- package/src/tool.ts +36 -54
- package/src/types.ts +52 -42
- package/src/zalo-js.ts +1401 -0
- package/src/zca-client.ts +249 -0
- package/src/zca-js-exports.d.ts +22 -0
- package/src/zca.ts +0 -198
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2026.3.2
|
|
4
|
+
|
|
5
|
+
### Changes
|
|
6
|
+
|
|
7
|
+
- Rebuilt the plugin to use native `zca-js` integration inside OpenClaw (no external `zca` CLI runtime dependency).
|
|
8
|
+
|
|
9
|
+
### Breaking
|
|
10
|
+
|
|
11
|
+
- **BREAKING:** Removed the old external CLI-based backend (`zca`/`openzca`/`zca-cli`) from runtime flow. Existing setups that depended on external CLI binaries should re-login with `openclaw channels login --channel zalouser` after upgrading.
|
|
12
|
+
|
|
3
13
|
## 2026.3.1
|
|
4
14
|
|
|
5
15
|
### Changes
|
package/README.md
CHANGED
|
@@ -1,112 +1,78 @@
|
|
|
1
1
|
# @openclaw/zalouser
|
|
2
2
|
|
|
3
|
-
OpenClaw extension for Zalo Personal Account messaging via
|
|
3
|
+
OpenClaw extension for Zalo Personal Account messaging via native `zca-js` integration.
|
|
4
4
|
|
|
5
5
|
> **Warning:** Using Zalo automation may result in account suspension or ban. Use at your own risk. This is an unofficial integration.
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
9
|
+
- Channel plugin integration with onboarding + QR login
|
|
10
|
+
- In-process listener/sender via `zca-js` (no external CLI)
|
|
11
|
+
- Multi-account support
|
|
12
|
+
- Agent tool integration (`zalouser`)
|
|
13
|
+
- DM/group policy support
|
|
14
14
|
|
|
15
15
|
## Prerequisites
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
- OpenClaw Gateway
|
|
18
|
+
- Zalo mobile app (for QR login)
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
No external `zca`, `openzca`, or `zca-cli` binary is required.
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
curl -fsSL https://get.zca-cli.dev/install.sh | bash
|
|
23
|
-
|
|
24
|
-
# Or with custom install directory
|
|
25
|
-
ZCA_INSTALL_DIR=~/.local/bin curl -fsSL https://get.zca-cli.dev/install.sh | bash
|
|
26
|
-
|
|
27
|
-
# Install specific version
|
|
28
|
-
curl -fsSL https://get.zca-cli.dev/install.sh | bash -s v1.0.0
|
|
29
|
-
|
|
30
|
-
# Uninstall
|
|
31
|
-
curl -fsSL https://get.zca-cli.dev/install.sh | bash -s uninstall
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
**Windows (PowerShell):**
|
|
35
|
-
|
|
36
|
-
```powershell
|
|
37
|
-
irm https://get.zca-cli.dev/install.ps1 | iex
|
|
22
|
+
## Install
|
|
38
23
|
|
|
39
|
-
|
|
40
|
-
$env:ZCA_INSTALL_DIR = "C:\Tools\zca"; irm https://get.zca-cli.dev/install.ps1 | iex
|
|
41
|
-
|
|
42
|
-
# Install specific version
|
|
43
|
-
iex "& { $(irm https://get.zca-cli.dev/install.ps1) } -Version v1.0.0"
|
|
44
|
-
|
|
45
|
-
# Uninstall
|
|
46
|
-
iex "& { $(irm https://get.zca-cli.dev/install.ps1) } -Uninstall"
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
### Manual Download
|
|
50
|
-
|
|
51
|
-
Download binary directly:
|
|
52
|
-
|
|
53
|
-
**macOS / Linux:**
|
|
24
|
+
### Option A: npm
|
|
54
25
|
|
|
55
26
|
```bash
|
|
56
|
-
|
|
27
|
+
openclaw plugins install @openclaw/zalouser
|
|
57
28
|
```
|
|
58
29
|
|
|
59
|
-
|
|
30
|
+
### Option B: local source checkout
|
|
60
31
|
|
|
61
|
-
```
|
|
62
|
-
|
|
32
|
+
```bash
|
|
33
|
+
openclaw plugins install ./extensions/zalouser
|
|
34
|
+
cd ./extensions/zalouser && pnpm install
|
|
63
35
|
```
|
|
64
36
|
|
|
65
|
-
|
|
37
|
+
Restart the Gateway after install.
|
|
66
38
|
|
|
67
|
-
|
|
68
|
-
- `zca-darwin-x64` - macOS Intel
|
|
69
|
-
- `zca-linux-arm64` - Linux ARM64
|
|
70
|
-
- `zca-linux-x64` - Linux x86_64
|
|
71
|
-
- `zca-windows-x64.exe` - Windows
|
|
39
|
+
## Quick start
|
|
72
40
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
## Quick Start
|
|
76
|
-
|
|
77
|
-
### Option 1: Onboarding Wizard (Recommended)
|
|
41
|
+
### Login (QR)
|
|
78
42
|
|
|
79
43
|
```bash
|
|
80
|
-
openclaw
|
|
81
|
-
# Select "Zalo Personal" from channel list
|
|
82
|
-
# Follow QR code login flow
|
|
44
|
+
openclaw channels login --channel zalouser
|
|
83
45
|
```
|
|
84
46
|
|
|
85
|
-
|
|
47
|
+
Scan the QR code with the Zalo app on your phone.
|
|
86
48
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
49
|
+
### Enable channel
|
|
50
|
+
|
|
51
|
+
```yaml
|
|
52
|
+
channels:
|
|
53
|
+
zalouser:
|
|
54
|
+
enabled: true
|
|
55
|
+
dmPolicy: pairing # pairing | allowlist | open | disabled
|
|
90
56
|
```
|
|
91
57
|
|
|
92
|
-
### Send a
|
|
58
|
+
### Send a message
|
|
93
59
|
|
|
94
60
|
```bash
|
|
95
|
-
openclaw message send --channel zalouser --target <threadId> --message "Hello from OpenClaw
|
|
61
|
+
openclaw message send --channel zalouser --target <threadId> --message "Hello from OpenClaw"
|
|
96
62
|
```
|
|
97
63
|
|
|
98
64
|
## Configuration
|
|
99
65
|
|
|
100
|
-
|
|
66
|
+
Basic:
|
|
101
67
|
|
|
102
68
|
```yaml
|
|
103
69
|
channels:
|
|
104
70
|
zalouser:
|
|
105
71
|
enabled: true
|
|
106
|
-
dmPolicy: pairing
|
|
72
|
+
dmPolicy: pairing
|
|
107
73
|
```
|
|
108
74
|
|
|
109
|
-
|
|
75
|
+
Multi-account:
|
|
110
76
|
|
|
111
77
|
```yaml
|
|
112
78
|
channels:
|
|
@@ -122,104 +88,32 @@ channels:
|
|
|
122
88
|
profile: work
|
|
123
89
|
```
|
|
124
90
|
|
|
125
|
-
##
|
|
126
|
-
|
|
127
|
-
### Authentication
|
|
91
|
+
## Useful commands
|
|
128
92
|
|
|
129
93
|
```bash
|
|
130
|
-
openclaw channels login --channel zalouser
|
|
94
|
+
openclaw channels login --channel zalouser
|
|
131
95
|
openclaw channels login --channel zalouser --account work
|
|
132
96
|
openclaw channels status --probe
|
|
133
97
|
openclaw channels logout --channel zalouser
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
### Directory (IDs, contacts, groups)
|
|
137
98
|
|
|
138
|
-
```bash
|
|
139
99
|
openclaw directory self --channel zalouser
|
|
140
100
|
openclaw directory peers list --channel zalouser --query "name"
|
|
141
101
|
openclaw directory groups list --channel zalouser --query "work"
|
|
142
102
|
openclaw directory groups members --channel zalouser --group-id <id>
|
|
143
103
|
```
|
|
144
104
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
```bash
|
|
148
|
-
zca account list # List all profiles
|
|
149
|
-
zca account current # Show active profile
|
|
150
|
-
zca account switch <profile>
|
|
151
|
-
zca account remove <profile>
|
|
152
|
-
zca account label <profile> "Work Account"
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
### Messaging
|
|
156
|
-
|
|
157
|
-
```bash
|
|
158
|
-
# Text
|
|
159
|
-
openclaw message send --channel zalouser --target <threadId> --message "message"
|
|
160
|
-
|
|
161
|
-
# Media (URL)
|
|
162
|
-
openclaw message send --channel zalouser --target <threadId> --message "caption" --media-url "https://example.com/img.jpg"
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
### Listener
|
|
166
|
-
|
|
167
|
-
The listener runs inside the Gateway when the channel is enabled. For debugging,
|
|
168
|
-
use `openclaw channels logs --channel zalouser` or run `zca listen` directly.
|
|
169
|
-
|
|
170
|
-
### Data Access
|
|
105
|
+
## Agent tool
|
|
171
106
|
|
|
172
|
-
|
|
173
|
-
# Friends
|
|
174
|
-
zca friend list
|
|
175
|
-
zca friend list -j # JSON output
|
|
176
|
-
zca friend find "name"
|
|
177
|
-
zca friend online
|
|
178
|
-
|
|
179
|
-
# Groups
|
|
180
|
-
zca group list
|
|
181
|
-
zca group info <groupId>
|
|
182
|
-
zca group members <groupId>
|
|
183
|
-
|
|
184
|
-
# Profile
|
|
185
|
-
zca me info
|
|
186
|
-
zca me id
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
## Multi-Account Support
|
|
190
|
-
|
|
191
|
-
Use `--profile` or `-p` to work with multiple accounts:
|
|
192
|
-
|
|
193
|
-
```bash
|
|
194
|
-
openclaw channels login --channel zalouser --account work
|
|
195
|
-
openclaw message send --channel zalouser --account work --target <id> --message "Hello"
|
|
196
|
-
ZCA_PROFILE=work zca listen
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
Profile resolution order: `--profile` flag > `ZCA_PROFILE` env > default
|
|
200
|
-
|
|
201
|
-
## Agent Tool
|
|
202
|
-
|
|
203
|
-
The extension registers a `zalouser` tool for AI agents:
|
|
204
|
-
|
|
205
|
-
```json
|
|
206
|
-
{
|
|
207
|
-
"action": "send",
|
|
208
|
-
"threadId": "123456",
|
|
209
|
-
"message": "Hello from AI!",
|
|
210
|
-
"isGroup": false,
|
|
211
|
-
"profile": "default"
|
|
212
|
-
}
|
|
213
|
-
```
|
|
107
|
+
The extension registers a `zalouser` tool for AI agents.
|
|
214
108
|
|
|
215
109
|
Available actions: `send`, `image`, `link`, `friends`, `groups`, `me`, `status`
|
|
216
110
|
|
|
217
111
|
## Troubleshooting
|
|
218
112
|
|
|
219
|
-
-
|
|
220
|
-
-
|
|
221
|
-
-
|
|
113
|
+
- Login not persisted: `openclaw channels logout --channel zalouser && openclaw channels login --channel zalouser`
|
|
114
|
+
- Probe status: `openclaw channels status --probe`
|
|
115
|
+
- Name resolution issues (allowlist/groups): use numeric IDs or exact Zalo names
|
|
222
116
|
|
|
223
117
|
## Credits
|
|
224
118
|
|
|
225
|
-
Built on [zca-
|
|
119
|
+
Built on [zca-js](https://github.com/RFS-ADRENO/zca-js).
|
package/index.ts
CHANGED
|
@@ -7,14 +7,12 @@ import { ZalouserToolSchema, executeZalouserTool } from "./src/tool.js";
|
|
|
7
7
|
const plugin = {
|
|
8
8
|
id: "zalouser",
|
|
9
9
|
name: "Zalo Personal",
|
|
10
|
-
description: "Zalo personal account messaging via zca-
|
|
10
|
+
description: "Zalo personal account messaging via native zca-js integration",
|
|
11
11
|
configSchema: emptyPluginConfigSchema(),
|
|
12
12
|
register(api: OpenClawPluginApi) {
|
|
13
13
|
setZalouserRuntime(api.runtime);
|
|
14
|
-
// Register channel plugin (for onboarding & gateway)
|
|
15
14
|
api.registerChannel({ plugin: zalouserPlugin, dock: zalouserDock });
|
|
16
15
|
|
|
17
|
-
// Register agent tool
|
|
18
16
|
api.registerTool({
|
|
19
17
|
name: "zalouser",
|
|
20
18
|
label: "Zalo Personal",
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/zalouser",
|
|
3
|
-
"version": "2026.3.
|
|
4
|
-
"description": "OpenClaw Zalo Personal Account plugin via zca-
|
|
3
|
+
"version": "2026.3.2",
|
|
4
|
+
"description": "OpenClaw Zalo Personal Account plugin via native zca-js integration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@sinclair/typebox": "0.34.48"
|
|
7
|
+
"@sinclair/typebox": "0.34.48",
|
|
8
|
+
"zca-js": "2.1.1"
|
|
8
9
|
},
|
|
9
10
|
"openclaw": {
|
|
10
11
|
"extensions": [
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import type { OpenClawConfig } from "openclaw/plugin-sdk";
|
|
2
|
+
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
|
|
3
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
4
|
+
import {
|
|
5
|
+
getZcaUserInfo,
|
|
6
|
+
listEnabledZalouserAccounts,
|
|
7
|
+
listZalouserAccountIds,
|
|
8
|
+
resolveDefaultZalouserAccountId,
|
|
9
|
+
resolveZalouserAccount,
|
|
10
|
+
resolveZalouserAccountSync,
|
|
11
|
+
} from "./accounts.js";
|
|
12
|
+
import { checkZaloAuthenticated, getZaloUserInfo } from "./zalo-js.js";
|
|
13
|
+
|
|
14
|
+
vi.mock("./zalo-js.js", () => ({
|
|
15
|
+
checkZaloAuthenticated: vi.fn(),
|
|
16
|
+
getZaloUserInfo: vi.fn(),
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
const mockCheckAuthenticated = vi.mocked(checkZaloAuthenticated);
|
|
20
|
+
const mockGetUserInfo = vi.mocked(getZaloUserInfo);
|
|
21
|
+
|
|
22
|
+
function asConfig(value: unknown): OpenClawConfig {
|
|
23
|
+
return value as OpenClawConfig;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
describe("zalouser account resolution", () => {
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
mockCheckAuthenticated.mockReset();
|
|
29
|
+
mockGetUserInfo.mockReset();
|
|
30
|
+
delete process.env.ZALOUSER_PROFILE;
|
|
31
|
+
delete process.env.ZCA_PROFILE;
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("returns default account id when no accounts are configured", () => {
|
|
35
|
+
expect(listZalouserAccountIds(asConfig({}))).toEqual([DEFAULT_ACCOUNT_ID]);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("returns sorted configured account ids", () => {
|
|
39
|
+
const cfg = asConfig({
|
|
40
|
+
channels: {
|
|
41
|
+
zalouser: {
|
|
42
|
+
accounts: {
|
|
43
|
+
work: {},
|
|
44
|
+
personal: {},
|
|
45
|
+
default: {},
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
expect(listZalouserAccountIds(cfg)).toEqual(["default", "personal", "work"]);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("uses configured defaultAccount when present", () => {
|
|
55
|
+
const cfg = asConfig({
|
|
56
|
+
channels: {
|
|
57
|
+
zalouser: {
|
|
58
|
+
defaultAccount: "work",
|
|
59
|
+
accounts: {
|
|
60
|
+
default: {},
|
|
61
|
+
work: {},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
expect(resolveDefaultZalouserAccountId(cfg)).toBe("work");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("falls back to default account when configured defaultAccount is missing", () => {
|
|
71
|
+
const cfg = asConfig({
|
|
72
|
+
channels: {
|
|
73
|
+
zalouser: {
|
|
74
|
+
defaultAccount: "missing",
|
|
75
|
+
accounts: {
|
|
76
|
+
default: {},
|
|
77
|
+
work: {},
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
expect(resolveDefaultZalouserAccountId(cfg)).toBe("default");
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("falls back to first sorted configured account when default is absent", () => {
|
|
87
|
+
const cfg = asConfig({
|
|
88
|
+
channels: {
|
|
89
|
+
zalouser: {
|
|
90
|
+
accounts: {
|
|
91
|
+
zzz: {},
|
|
92
|
+
aaa: {},
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
expect(resolveDefaultZalouserAccountId(cfg)).toBe("aaa");
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("resolves sync account by merging base + account config", () => {
|
|
102
|
+
const cfg = asConfig({
|
|
103
|
+
channels: {
|
|
104
|
+
zalouser: {
|
|
105
|
+
enabled: true,
|
|
106
|
+
dmPolicy: "pairing",
|
|
107
|
+
accounts: {
|
|
108
|
+
work: {
|
|
109
|
+
enabled: false,
|
|
110
|
+
name: "Work",
|
|
111
|
+
dmPolicy: "allowlist",
|
|
112
|
+
allowFrom: ["123"],
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const resolved = resolveZalouserAccountSync({ cfg, accountId: "work" });
|
|
120
|
+
expect(resolved.accountId).toBe("work");
|
|
121
|
+
expect(resolved.enabled).toBe(false);
|
|
122
|
+
expect(resolved.name).toBe("Work");
|
|
123
|
+
expect(resolved.config.dmPolicy).toBe("allowlist");
|
|
124
|
+
expect(resolved.config.allowFrom).toEqual(["123"]);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("resolves profile precedence correctly", () => {
|
|
128
|
+
const cfg = asConfig({
|
|
129
|
+
channels: {
|
|
130
|
+
zalouser: {
|
|
131
|
+
accounts: {
|
|
132
|
+
work: {},
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
process.env.ZALOUSER_PROFILE = "zalo-env";
|
|
139
|
+
expect(resolveZalouserAccountSync({ cfg, accountId: "work" }).profile).toBe("zalo-env");
|
|
140
|
+
|
|
141
|
+
delete process.env.ZALOUSER_PROFILE;
|
|
142
|
+
process.env.ZCA_PROFILE = "zca-env";
|
|
143
|
+
expect(resolveZalouserAccountSync({ cfg, accountId: "work" }).profile).toBe("zca-env");
|
|
144
|
+
|
|
145
|
+
delete process.env.ZCA_PROFILE;
|
|
146
|
+
expect(resolveZalouserAccountSync({ cfg, accountId: "work" }).profile).toBe("work");
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("uses explicit profile from config over env fallback", () => {
|
|
150
|
+
process.env.ZALOUSER_PROFILE = "env-profile";
|
|
151
|
+
const cfg = asConfig({
|
|
152
|
+
channels: {
|
|
153
|
+
zalouser: {
|
|
154
|
+
accounts: {
|
|
155
|
+
work: {
|
|
156
|
+
profile: "explicit-profile",
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
expect(resolveZalouserAccountSync({ cfg, accountId: "work" }).profile).toBe("explicit-profile");
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it("checks authentication during async account resolution", async () => {
|
|
167
|
+
mockCheckAuthenticated.mockResolvedValueOnce(true);
|
|
168
|
+
const cfg = asConfig({
|
|
169
|
+
channels: {
|
|
170
|
+
zalouser: {
|
|
171
|
+
accounts: {
|
|
172
|
+
default: {},
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
const resolved = await resolveZalouserAccount({ cfg, accountId: "default" });
|
|
179
|
+
expect(mockCheckAuthenticated).toHaveBeenCalledWith("default");
|
|
180
|
+
expect(resolved.authenticated).toBe(true);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it("filters disabled accounts when listing enabled accounts", async () => {
|
|
184
|
+
mockCheckAuthenticated.mockResolvedValue(true);
|
|
185
|
+
const cfg = asConfig({
|
|
186
|
+
channels: {
|
|
187
|
+
zalouser: {
|
|
188
|
+
accounts: {
|
|
189
|
+
default: { enabled: true },
|
|
190
|
+
work: { enabled: false },
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
const accounts = await listEnabledZalouserAccounts(cfg);
|
|
197
|
+
expect(accounts.map((account) => account.accountId)).toEqual(["default"]);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it("maps account info helper from zalo-js", async () => {
|
|
201
|
+
mockGetUserInfo.mockResolvedValueOnce({
|
|
202
|
+
userId: "123",
|
|
203
|
+
displayName: "Alice",
|
|
204
|
+
avatar: "https://example.com/avatar.png",
|
|
205
|
+
});
|
|
206
|
+
expect(await getZcaUserInfo("default")).toEqual({
|
|
207
|
+
userId: "123",
|
|
208
|
+
displayName: "Alice",
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
mockGetUserInfo.mockResolvedValueOnce(null);
|
|
212
|
+
expect(await getZcaUserInfo("default")).toBeNull();
|
|
213
|
+
});
|
|
214
|
+
});
|
package/src/accounts.ts
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
normalizeOptionalAccountId,
|
|
6
6
|
} from "openclaw/plugin-sdk/account-id";
|
|
7
7
|
import type { ResolvedZalouserAccount, ZalouserAccountConfig, ZalouserConfig } from "./types.js";
|
|
8
|
-
import {
|
|
8
|
+
import { checkZaloAuthenticated, getZaloUserInfo } from "./zalo-js.js";
|
|
9
9
|
|
|
10
10
|
function listConfiguredAccountIds(cfg: OpenClawConfig): string[] {
|
|
11
11
|
const accounts = (cfg.channels?.zalouser as ZalouserConfig | undefined)?.accounts;
|
|
@@ -57,10 +57,13 @@ function mergeZalouserAccountConfig(cfg: OpenClawConfig, accountId: string): Zal
|
|
|
57
57
|
return { ...base, ...account };
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
function
|
|
60
|
+
function resolveProfile(config: ZalouserAccountConfig, accountId: string): string {
|
|
61
61
|
if (config.profile?.trim()) {
|
|
62
62
|
return config.profile.trim();
|
|
63
63
|
}
|
|
64
|
+
if (process.env.ZALOUSER_PROFILE?.trim()) {
|
|
65
|
+
return process.env.ZALOUSER_PROFILE.trim();
|
|
66
|
+
}
|
|
64
67
|
if (process.env.ZCA_PROFILE?.trim()) {
|
|
65
68
|
return process.env.ZCA_PROFILE.trim();
|
|
66
69
|
}
|
|
@@ -70,11 +73,6 @@ function resolveZcaProfile(config: ZalouserAccountConfig, accountId: string): st
|
|
|
70
73
|
return "default";
|
|
71
74
|
}
|
|
72
75
|
|
|
73
|
-
export async function checkZcaAuthenticated(profile: string): Promise<boolean> {
|
|
74
|
-
const result = await runZca(["auth", "status"], { profile, timeout: 5000 });
|
|
75
|
-
return result.ok;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
76
|
export async function resolveZalouserAccount(params: {
|
|
79
77
|
cfg: OpenClawConfig;
|
|
80
78
|
accountId?: string | null;
|
|
@@ -85,8 +83,8 @@ export async function resolveZalouserAccount(params: {
|
|
|
85
83
|
const merged = mergeZalouserAccountConfig(params.cfg, accountId);
|
|
86
84
|
const accountEnabled = merged.enabled !== false;
|
|
87
85
|
const enabled = baseEnabled && accountEnabled;
|
|
88
|
-
const profile =
|
|
89
|
-
const authenticated = await
|
|
86
|
+
const profile = resolveProfile(merged, accountId);
|
|
87
|
+
const authenticated = await checkZaloAuthenticated(profile);
|
|
90
88
|
|
|
91
89
|
return {
|
|
92
90
|
accountId,
|
|
@@ -108,14 +106,14 @@ export function resolveZalouserAccountSync(params: {
|
|
|
108
106
|
const merged = mergeZalouserAccountConfig(params.cfg, accountId);
|
|
109
107
|
const accountEnabled = merged.enabled !== false;
|
|
110
108
|
const enabled = baseEnabled && accountEnabled;
|
|
111
|
-
const profile =
|
|
109
|
+
const profile = resolveProfile(merged, accountId);
|
|
112
110
|
|
|
113
111
|
return {
|
|
114
112
|
accountId,
|
|
115
113
|
name: merged.name?.trim() || undefined,
|
|
116
114
|
enabled,
|
|
117
115
|
profile,
|
|
118
|
-
authenticated: false,
|
|
116
|
+
authenticated: false,
|
|
119
117
|
config: merged,
|
|
120
118
|
};
|
|
121
119
|
}
|
|
@@ -133,11 +131,16 @@ export async function listEnabledZalouserAccounts(
|
|
|
133
131
|
export async function getZcaUserInfo(
|
|
134
132
|
profile: string,
|
|
135
133
|
): Promise<{ userId?: string; displayName?: string } | null> {
|
|
136
|
-
const
|
|
137
|
-
if (!
|
|
134
|
+
const info = await getZaloUserInfo(profile);
|
|
135
|
+
if (!info) {
|
|
138
136
|
return null;
|
|
139
137
|
}
|
|
140
|
-
return
|
|
138
|
+
return {
|
|
139
|
+
userId: info.userId,
|
|
140
|
+
displayName: info.displayName,
|
|
141
|
+
};
|
|
141
142
|
}
|
|
142
143
|
|
|
144
|
+
export { checkZaloAuthenticated as checkZcaAuthenticated };
|
|
145
|
+
|
|
143
146
|
export type { ResolvedZalouserAccount } from "./types.js";
|