@openclaw/zalouser 2026.2.25 → 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 +22 -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 +28 -17
- 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 +299 -228
- 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
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import {
|
|
2
|
+
LoginQRCallbackEventType as LoginQRCallbackEventTypeRuntime,
|
|
3
|
+
Reactions as ReactionsRuntime,
|
|
4
|
+
ThreadType as ThreadTypeRuntime,
|
|
5
|
+
Zalo as ZaloRuntime,
|
|
6
|
+
} from "zca-js";
|
|
7
|
+
|
|
8
|
+
export const ThreadType = ThreadTypeRuntime as {
|
|
9
|
+
User: 0;
|
|
10
|
+
Group: 1;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const LoginQRCallbackEventType = LoginQRCallbackEventTypeRuntime as {
|
|
14
|
+
QRCodeGenerated: 0;
|
|
15
|
+
QRCodeExpired: 1;
|
|
16
|
+
QRCodeScanned: 2;
|
|
17
|
+
QRCodeDeclined: 3;
|
|
18
|
+
GotLoginInfo: 4;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const Reactions = ReactionsRuntime as Record<string, string> & {
|
|
22
|
+
HEART: string;
|
|
23
|
+
LIKE: string;
|
|
24
|
+
HAHA: string;
|
|
25
|
+
WOW: string;
|
|
26
|
+
CRY: string;
|
|
27
|
+
ANGRY: string;
|
|
28
|
+
NONE: string;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type Credentials = {
|
|
32
|
+
imei: string;
|
|
33
|
+
cookie: unknown;
|
|
34
|
+
userAgent: string;
|
|
35
|
+
language?: string;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export type User = {
|
|
39
|
+
userId: string;
|
|
40
|
+
username: string;
|
|
41
|
+
displayName: string;
|
|
42
|
+
zaloName: string;
|
|
43
|
+
avatar: string;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export type GroupInfo = {
|
|
47
|
+
groupId: string;
|
|
48
|
+
name: string;
|
|
49
|
+
totalMember?: number;
|
|
50
|
+
memberIds?: unknown[];
|
|
51
|
+
currentMems?: Array<{
|
|
52
|
+
id?: unknown;
|
|
53
|
+
dName?: string;
|
|
54
|
+
zaloName?: string;
|
|
55
|
+
avatar?: string;
|
|
56
|
+
}>;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export type Message = {
|
|
60
|
+
type: number;
|
|
61
|
+
threadId: string;
|
|
62
|
+
isSelf: boolean;
|
|
63
|
+
data: Record<string, unknown>;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export type LoginQRCallbackEvent =
|
|
67
|
+
| {
|
|
68
|
+
type: 0;
|
|
69
|
+
data: {
|
|
70
|
+
code: string;
|
|
71
|
+
image: string;
|
|
72
|
+
};
|
|
73
|
+
actions: {
|
|
74
|
+
saveToFile: (qrPath?: string) => Promise<unknown>;
|
|
75
|
+
retry: () => unknown;
|
|
76
|
+
abort: () => unknown;
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
| {
|
|
80
|
+
type: 1;
|
|
81
|
+
data: null;
|
|
82
|
+
actions: {
|
|
83
|
+
retry: () => unknown;
|
|
84
|
+
abort: () => unknown;
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
| {
|
|
88
|
+
type: 2;
|
|
89
|
+
data: {
|
|
90
|
+
avatar: string;
|
|
91
|
+
display_name: string;
|
|
92
|
+
};
|
|
93
|
+
actions: {
|
|
94
|
+
retry: () => unknown;
|
|
95
|
+
abort: () => unknown;
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
| {
|
|
99
|
+
type: 3;
|
|
100
|
+
data: {
|
|
101
|
+
code: string;
|
|
102
|
+
};
|
|
103
|
+
actions: {
|
|
104
|
+
retry: () => unknown;
|
|
105
|
+
abort: () => unknown;
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
| {
|
|
109
|
+
type: 4;
|
|
110
|
+
data: {
|
|
111
|
+
cookie: unknown;
|
|
112
|
+
imei: string;
|
|
113
|
+
userAgent: string;
|
|
114
|
+
};
|
|
115
|
+
actions: null;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export type Listener = {
|
|
119
|
+
on(event: "message", callback: (message: Message) => void): void;
|
|
120
|
+
on(event: "error", callback: (error: unknown) => void): void;
|
|
121
|
+
on(event: "closed", callback: (code: number, reason: string) => void): void;
|
|
122
|
+
off(event: "message", callback: (message: Message) => void): void;
|
|
123
|
+
off(event: "error", callback: (error: unknown) => void): void;
|
|
124
|
+
off(event: "closed", callback: (code: number, reason: string) => void): void;
|
|
125
|
+
start(opts?: { retryOnClose?: boolean }): void;
|
|
126
|
+
stop(): void;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
export type API = {
|
|
130
|
+
listener: Listener;
|
|
131
|
+
getContext(): {
|
|
132
|
+
imei: string;
|
|
133
|
+
userAgent: string;
|
|
134
|
+
language?: string;
|
|
135
|
+
};
|
|
136
|
+
getCookie(): {
|
|
137
|
+
toJSON(): {
|
|
138
|
+
cookies: unknown[];
|
|
139
|
+
};
|
|
140
|
+
};
|
|
141
|
+
fetchAccountInfo(): Promise<{ profile: User } | User>;
|
|
142
|
+
getAllFriends(): Promise<User[]>;
|
|
143
|
+
getOwnId(): string;
|
|
144
|
+
getAllGroups(): Promise<{
|
|
145
|
+
gridVerMap: Record<string, string>;
|
|
146
|
+
}>;
|
|
147
|
+
getGroupInfo(groupId: string | string[]): Promise<{
|
|
148
|
+
gridInfoMap: Record<string, GroupInfo & { memVerList?: unknown }>;
|
|
149
|
+
}>;
|
|
150
|
+
getGroupMembersInfo(memberId: string | string[]): Promise<{
|
|
151
|
+
profiles: Record<
|
|
152
|
+
string,
|
|
153
|
+
{
|
|
154
|
+
id?: string;
|
|
155
|
+
displayName?: string;
|
|
156
|
+
zaloName?: string;
|
|
157
|
+
avatar?: string;
|
|
158
|
+
}
|
|
159
|
+
>;
|
|
160
|
+
}>;
|
|
161
|
+
sendMessage(
|
|
162
|
+
message: string | Record<string, unknown>,
|
|
163
|
+
threadId: string,
|
|
164
|
+
type?: number,
|
|
165
|
+
): Promise<{
|
|
166
|
+
message?: { msgId?: string | number } | null;
|
|
167
|
+
attachment?: Array<{ msgId?: string | number }>;
|
|
168
|
+
}>;
|
|
169
|
+
sendLink(
|
|
170
|
+
payload: { link: string; msg?: string },
|
|
171
|
+
threadId: string,
|
|
172
|
+
type?: number,
|
|
173
|
+
): Promise<{ msgId?: string | number }>;
|
|
174
|
+
sendTypingEvent(threadId: string, type?: number, destType?: number): Promise<{ status: number }>;
|
|
175
|
+
addReaction(
|
|
176
|
+
icon: string | { rType: number; source: number; icon: string },
|
|
177
|
+
dest: {
|
|
178
|
+
data: {
|
|
179
|
+
msgId: string;
|
|
180
|
+
cliMsgId: string;
|
|
181
|
+
};
|
|
182
|
+
threadId: string;
|
|
183
|
+
type: number;
|
|
184
|
+
},
|
|
185
|
+
): Promise<unknown>;
|
|
186
|
+
sendDeliveredEvent(
|
|
187
|
+
isSeen: boolean,
|
|
188
|
+
messages:
|
|
189
|
+
| {
|
|
190
|
+
msgId: string;
|
|
191
|
+
cliMsgId: string;
|
|
192
|
+
uidFrom: string;
|
|
193
|
+
idTo: string;
|
|
194
|
+
msgType: string;
|
|
195
|
+
st: number;
|
|
196
|
+
at: number;
|
|
197
|
+
cmd: number;
|
|
198
|
+
ts: string | number;
|
|
199
|
+
}
|
|
200
|
+
| Array<{
|
|
201
|
+
msgId: string;
|
|
202
|
+
cliMsgId: string;
|
|
203
|
+
uidFrom: string;
|
|
204
|
+
idTo: string;
|
|
205
|
+
msgType: string;
|
|
206
|
+
st: number;
|
|
207
|
+
at: number;
|
|
208
|
+
cmd: number;
|
|
209
|
+
ts: string | number;
|
|
210
|
+
}>,
|
|
211
|
+
type?: number,
|
|
212
|
+
): Promise<unknown>;
|
|
213
|
+
sendSeenEvent(
|
|
214
|
+
messages:
|
|
215
|
+
| {
|
|
216
|
+
msgId: string;
|
|
217
|
+
cliMsgId: string;
|
|
218
|
+
uidFrom: string;
|
|
219
|
+
idTo: string;
|
|
220
|
+
msgType: string;
|
|
221
|
+
st: number;
|
|
222
|
+
at: number;
|
|
223
|
+
cmd: number;
|
|
224
|
+
ts: string | number;
|
|
225
|
+
}
|
|
226
|
+
| Array<{
|
|
227
|
+
msgId: string;
|
|
228
|
+
cliMsgId: string;
|
|
229
|
+
uidFrom: string;
|
|
230
|
+
idTo: string;
|
|
231
|
+
msgType: string;
|
|
232
|
+
st: number;
|
|
233
|
+
at: number;
|
|
234
|
+
cmd: number;
|
|
235
|
+
ts: string | number;
|
|
236
|
+
}>,
|
|
237
|
+
type?: number,
|
|
238
|
+
): Promise<unknown>;
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
type ZaloCtor = new (options?: { logging?: boolean; selfListen?: boolean }) => {
|
|
242
|
+
login(credentials: Credentials): Promise<API>;
|
|
243
|
+
loginQR(
|
|
244
|
+
options?: { userAgent?: string; language?: string; qrPath?: string },
|
|
245
|
+
callback?: (event: LoginQRCallbackEvent) => unknown,
|
|
246
|
+
): Promise<API>;
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
export const Zalo = ZaloRuntime as unknown as ZaloCtor;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
declare module "zca-js" {
|
|
2
|
+
export const ThreadType: {
|
|
3
|
+
User: number;
|
|
4
|
+
Group: number;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export const LoginQRCallbackEventType: {
|
|
8
|
+
QRCodeGenerated: number;
|
|
9
|
+
QRCodeExpired: number;
|
|
10
|
+
QRCodeScanned: number;
|
|
11
|
+
QRCodeDeclined: number;
|
|
12
|
+
GotLoginInfo: number;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const Reactions: Record<string, string>;
|
|
16
|
+
|
|
17
|
+
export class Zalo {
|
|
18
|
+
constructor(options?: { logging?: boolean; selfListen?: boolean });
|
|
19
|
+
login(credentials: unknown): Promise<unknown>;
|
|
20
|
+
loginQR(options?: unknown, callback?: (event: unknown) => unknown): Promise<unknown>;
|
|
21
|
+
}
|
|
22
|
+
}
|
package/src/zca.ts
DELETED
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
import { spawn, type SpawnOptions } from "node:child_process";
|
|
2
|
-
import { stripAnsi } from "openclaw/plugin-sdk";
|
|
3
|
-
import type { ZcaResult, ZcaRunOptions } from "./types.js";
|
|
4
|
-
|
|
5
|
-
const ZCA_BINARY = "zca";
|
|
6
|
-
const DEFAULT_TIMEOUT = 30000;
|
|
7
|
-
|
|
8
|
-
function buildArgs(args: string[], options?: ZcaRunOptions): string[] {
|
|
9
|
-
const result: string[] = [];
|
|
10
|
-
// Profile flag comes first (before subcommand)
|
|
11
|
-
const profile = options?.profile || process.env.ZCA_PROFILE;
|
|
12
|
-
if (profile) {
|
|
13
|
-
result.push("--profile", profile);
|
|
14
|
-
}
|
|
15
|
-
result.push(...args);
|
|
16
|
-
return result;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export async function runZca(args: string[], options?: ZcaRunOptions): Promise<ZcaResult> {
|
|
20
|
-
const fullArgs = buildArgs(args, options);
|
|
21
|
-
const timeout = options?.timeout ?? DEFAULT_TIMEOUT;
|
|
22
|
-
|
|
23
|
-
return new Promise((resolve) => {
|
|
24
|
-
const spawnOpts: SpawnOptions = {
|
|
25
|
-
cwd: options?.cwd,
|
|
26
|
-
env: { ...process.env },
|
|
27
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
const proc = spawn(ZCA_BINARY, fullArgs, spawnOpts);
|
|
31
|
-
let stdout = "";
|
|
32
|
-
let stderr = "";
|
|
33
|
-
let timedOut = false;
|
|
34
|
-
|
|
35
|
-
const timer = setTimeout(() => {
|
|
36
|
-
timedOut = true;
|
|
37
|
-
proc.kill("SIGTERM");
|
|
38
|
-
}, timeout);
|
|
39
|
-
|
|
40
|
-
proc.stdout?.on("data", (data: Buffer) => {
|
|
41
|
-
stdout += data.toString();
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
proc.stderr?.on("data", (data: Buffer) => {
|
|
45
|
-
stderr += data.toString();
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
proc.on("close", (code) => {
|
|
49
|
-
clearTimeout(timer);
|
|
50
|
-
if (timedOut) {
|
|
51
|
-
resolve({
|
|
52
|
-
ok: false,
|
|
53
|
-
stdout,
|
|
54
|
-
stderr: stderr || "Command timed out",
|
|
55
|
-
exitCode: code ?? 124,
|
|
56
|
-
});
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
resolve({
|
|
60
|
-
ok: code === 0,
|
|
61
|
-
stdout: stdout.trim(),
|
|
62
|
-
stderr: stderr.trim(),
|
|
63
|
-
exitCode: code ?? 1,
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
proc.on("error", (err) => {
|
|
68
|
-
clearTimeout(timer);
|
|
69
|
-
resolve({
|
|
70
|
-
ok: false,
|
|
71
|
-
stdout: "",
|
|
72
|
-
stderr: err.message,
|
|
73
|
-
exitCode: 1,
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export function runZcaInteractive(args: string[], options?: ZcaRunOptions): Promise<ZcaResult> {
|
|
80
|
-
const fullArgs = buildArgs(args, options);
|
|
81
|
-
|
|
82
|
-
return new Promise((resolve) => {
|
|
83
|
-
const spawnOpts: SpawnOptions = {
|
|
84
|
-
cwd: options?.cwd,
|
|
85
|
-
env: { ...process.env },
|
|
86
|
-
stdio: "inherit",
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
const proc = spawn(ZCA_BINARY, fullArgs, spawnOpts);
|
|
90
|
-
|
|
91
|
-
proc.on("close", (code) => {
|
|
92
|
-
resolve({
|
|
93
|
-
ok: code === 0,
|
|
94
|
-
stdout: "",
|
|
95
|
-
stderr: "",
|
|
96
|
-
exitCode: code ?? 1,
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
proc.on("error", (err) => {
|
|
101
|
-
resolve({
|
|
102
|
-
ok: false,
|
|
103
|
-
stdout: "",
|
|
104
|
-
stderr: err.message,
|
|
105
|
-
exitCode: 1,
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export function parseJsonOutput<T>(stdout: string): T | null {
|
|
112
|
-
try {
|
|
113
|
-
return JSON.parse(stdout) as T;
|
|
114
|
-
} catch {
|
|
115
|
-
const cleaned = stripAnsi(stdout);
|
|
116
|
-
|
|
117
|
-
try {
|
|
118
|
-
return JSON.parse(cleaned) as T;
|
|
119
|
-
} catch {
|
|
120
|
-
// zca may prefix output with INFO/log lines, try to find JSON
|
|
121
|
-
const lines = cleaned.split("\n");
|
|
122
|
-
|
|
123
|
-
for (let i = 0; i < lines.length; i++) {
|
|
124
|
-
const line = lines[i].trim();
|
|
125
|
-
if (line.startsWith("{") || line.startsWith("[")) {
|
|
126
|
-
// Try parsing from this line to the end
|
|
127
|
-
const jsonCandidate = lines.slice(i).join("\n").trim();
|
|
128
|
-
try {
|
|
129
|
-
return JSON.parse(jsonCandidate) as T;
|
|
130
|
-
} catch {
|
|
131
|
-
continue;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
return null;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
export async function checkZcaInstalled(): Promise<boolean> {
|
|
141
|
-
const result = await runZca(["--version"], { timeout: 5000 });
|
|
142
|
-
return result.ok;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export type ZcaStreamingOptions = ZcaRunOptions & {
|
|
146
|
-
onData?: (data: string) => void;
|
|
147
|
-
onError?: (err: Error) => void;
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
export function runZcaStreaming(
|
|
151
|
-
args: string[],
|
|
152
|
-
options?: ZcaStreamingOptions,
|
|
153
|
-
): { proc: ReturnType<typeof spawn>; promise: Promise<ZcaResult> } {
|
|
154
|
-
const fullArgs = buildArgs(args, options);
|
|
155
|
-
|
|
156
|
-
const spawnOpts: SpawnOptions = {
|
|
157
|
-
cwd: options?.cwd,
|
|
158
|
-
env: { ...process.env },
|
|
159
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
const proc = spawn(ZCA_BINARY, fullArgs, spawnOpts);
|
|
163
|
-
let stdout = "";
|
|
164
|
-
let stderr = "";
|
|
165
|
-
|
|
166
|
-
proc.stdout?.on("data", (data: Buffer) => {
|
|
167
|
-
const text = data.toString();
|
|
168
|
-
stdout += text;
|
|
169
|
-
options?.onData?.(text);
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
proc.stderr?.on("data", (data: Buffer) => {
|
|
173
|
-
stderr += data.toString();
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
const promise = new Promise<ZcaResult>((resolve) => {
|
|
177
|
-
proc.on("close", (code) => {
|
|
178
|
-
resolve({
|
|
179
|
-
ok: code === 0,
|
|
180
|
-
stdout: stdout.trim(),
|
|
181
|
-
stderr: stderr.trim(),
|
|
182
|
-
exitCode: code ?? 1,
|
|
183
|
-
});
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
proc.on("error", (err) => {
|
|
187
|
-
options?.onError?.(err);
|
|
188
|
-
resolve({
|
|
189
|
-
ok: false,
|
|
190
|
-
stdout: "",
|
|
191
|
-
stderr: err.message,
|
|
192
|
-
exitCode: 1,
|
|
193
|
-
});
|
|
194
|
-
});
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
return { proc, promise };
|
|
198
|
-
}
|