@openclaw/zalo 2026.5.2 → 2026.5.3-beta.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/dist/accounts-9NLDDlZ8.js +118 -0
- package/dist/actions.runtime-kJ65ZxW7.js +5 -0
- package/dist/api.js +5 -0
- package/dist/channel-VPbtV3Oq.js +343 -0
- package/dist/channel-plugin-api.js +2 -0
- package/dist/channel.runtime-BnTAWQx5.js +106 -0
- package/dist/contract-api.js +3 -0
- package/dist/group-access-DZR43lOR.js +30 -0
- package/dist/index.js +22 -0
- package/dist/monitor-DMysJBWa.js +823 -0
- package/dist/monitor.webhook-DqnuvgjV.js +175 -0
- package/dist/proxy-CY8VuC6H.js +135 -0
- package/dist/runtime-BRFxnYQx.js +8 -0
- package/dist/runtime-api-MOTmRW4F.js +19 -0
- package/dist/runtime-api.js +3 -0
- package/dist/secret-contract-Dw93tGo2.js +87 -0
- package/dist/secret-contract-api.js +2 -0
- package/dist/send-Gv3l5EGI.js +101 -0
- package/dist/setup-api.js +30 -0
- package/dist/setup-core-DigRD3j1.js +166 -0
- package/dist/setup-entry.js +15 -0
- package/dist/setup-surface-2Up3yWov.js +216 -0
- package/dist/test-api.js +2 -0
- package/package.json +15 -6
- package/api.ts +0 -9
- package/channel-plugin-api.ts +0 -1
- package/contract-api.ts +0 -5
- package/index.test.ts +0 -15
- package/index.ts +0 -20
- package/runtime-api.test.ts +0 -17
- package/runtime-api.ts +0 -75
- package/secret-contract-api.ts +0 -5
- package/setup-api.ts +0 -34
- package/setup-entry.ts +0 -13
- package/src/accounts.test.ts +0 -70
- package/src/accounts.ts +0 -60
- package/src/actions.runtime.ts +0 -5
- package/src/actions.test.ts +0 -32
- package/src/actions.ts +0 -62
- package/src/api.test.ts +0 -149
- package/src/api.ts +0 -265
- package/src/approval-auth.test.ts +0 -17
- package/src/approval-auth.ts +0 -25
- package/src/channel.directory.test.ts +0 -59
- package/src/channel.runtime.ts +0 -93
- package/src/channel.startup.test.ts +0 -101
- package/src/channel.ts +0 -275
- package/src/config-schema.test.ts +0 -30
- package/src/config-schema.ts +0 -29
- package/src/group-access.ts +0 -49
- package/src/monitor.group-policy.test.ts +0 -94
- package/src/monitor.image.polling.test.ts +0 -110
- package/src/monitor.lifecycle.test.ts +0 -198
- package/src/monitor.pairing.lifecycle.test.ts +0 -141
- package/src/monitor.polling.media-reply.test.ts +0 -425
- package/src/monitor.reply-once.lifecycle.test.ts +0 -171
- package/src/monitor.ts +0 -1028
- package/src/monitor.types.ts +0 -4
- package/src/monitor.webhook.test.ts +0 -806
- package/src/monitor.webhook.ts +0 -278
- package/src/outbound-media.test.ts +0 -182
- package/src/outbound-media.ts +0 -241
- package/src/outbound-payload.contract.test.ts +0 -45
- package/src/probe.ts +0 -45
- package/src/proxy.ts +0 -24
- package/src/runtime-api.ts +0 -75
- package/src/runtime-support.ts +0 -91
- package/src/runtime.ts +0 -9
- package/src/secret-contract.ts +0 -109
- package/src/secret-input.ts +0 -5
- package/src/send.test.ts +0 -120
- package/src/send.ts +0 -153
- package/src/session-route.ts +0 -32
- package/src/setup-allow-from.ts +0 -94
- package/src/setup-core.ts +0 -149
- package/src/setup-status.test.ts +0 -33
- package/src/setup-surface.test.ts +0 -175
- package/src/setup-surface.ts +0 -291
- package/src/status-issues.test.ts +0 -17
- package/src/status-issues.ts +0 -37
- package/src/test-support/lifecycle-test-support.ts +0 -413
- package/src/test-support/monitor-mocks-test-support.ts +0 -209
- package/src/token.test.ts +0 -92
- package/src/token.ts +0 -79
- package/src/types.ts +0 -50
- package/test-api.ts +0 -1
- package/tsconfig.json +0 -16
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { a as resolveZaloAccount, i as resolveDefaultZaloAccountId } from "./accounts-9NLDDlZ8.js";
|
|
2
|
+
import { DEFAULT_ACCOUNT_ID, addWildcardAllowFrom, createDelegatedSetupWizardProxy, createPatchedAccountSetupAdapter, createSetupInputPresenceValidator, formatDocsLink, mergeAllowFromEntries, normalizeAccountId } from "openclaw/plugin-sdk/setup";
|
|
3
|
+
//#region extensions/zalo/src/setup-allow-from.ts
|
|
4
|
+
async function noteZaloTokenHelp(prompter) {
|
|
5
|
+
await prompter.note([
|
|
6
|
+
"1) Open Zalo Bot Platform: https://bot.zaloplatforms.com",
|
|
7
|
+
"2) Create a bot and get the token",
|
|
8
|
+
"3) Token looks like 12345689:abc-xyz",
|
|
9
|
+
"Tip: you can also set ZALO_BOT_TOKEN in your env.",
|
|
10
|
+
`Docs: ${formatDocsLink("/channels/zalo", "zalo")}`
|
|
11
|
+
].join("\n"), "Zalo bot token");
|
|
12
|
+
}
|
|
13
|
+
async function promptZaloAllowFrom(params) {
|
|
14
|
+
const { cfg, prompter } = params;
|
|
15
|
+
const accountId = params.accountId ?? resolveDefaultZaloAccountId(cfg);
|
|
16
|
+
const existingAllowFrom = resolveZaloAccount({
|
|
17
|
+
cfg,
|
|
18
|
+
accountId
|
|
19
|
+
}).config.allowFrom ?? [];
|
|
20
|
+
const unique = mergeAllowFromEntries(existingAllowFrom, [(await prompter.text({
|
|
21
|
+
message: "Zalo allowFrom (user id)",
|
|
22
|
+
placeholder: "123456789",
|
|
23
|
+
initialValue: existingAllowFrom[0] ? String(existingAllowFrom[0]) : void 0,
|
|
24
|
+
validate: (value) => {
|
|
25
|
+
const raw = (value ?? "").trim();
|
|
26
|
+
if (!raw) return "Required";
|
|
27
|
+
if (!/^\d+$/.test(raw)) return "Use a numeric Zalo user id";
|
|
28
|
+
}
|
|
29
|
+
})).trim()]);
|
|
30
|
+
if (accountId === DEFAULT_ACCOUNT_ID) return {
|
|
31
|
+
...cfg,
|
|
32
|
+
channels: {
|
|
33
|
+
...cfg.channels,
|
|
34
|
+
zalo: {
|
|
35
|
+
...cfg.channels?.zalo,
|
|
36
|
+
enabled: true,
|
|
37
|
+
dmPolicy: "allowlist",
|
|
38
|
+
allowFrom: unique
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
const currentAccount = cfg.channels?.zalo?.accounts?.[accountId];
|
|
43
|
+
return {
|
|
44
|
+
...cfg,
|
|
45
|
+
channels: {
|
|
46
|
+
...cfg.channels,
|
|
47
|
+
zalo: {
|
|
48
|
+
...cfg.channels?.zalo,
|
|
49
|
+
enabled: true,
|
|
50
|
+
accounts: {
|
|
51
|
+
...cfg.channels?.zalo?.accounts,
|
|
52
|
+
[accountId]: {
|
|
53
|
+
...currentAccount,
|
|
54
|
+
enabled: currentAccount?.enabled ?? true,
|
|
55
|
+
dmPolicy: "allowlist",
|
|
56
|
+
allowFrom: unique
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
//#endregion
|
|
64
|
+
//#region extensions/zalo/src/setup-core.ts
|
|
65
|
+
const channel = "zalo";
|
|
66
|
+
const zaloSetupAdapter = createPatchedAccountSetupAdapter({
|
|
67
|
+
channelKey: channel,
|
|
68
|
+
validateInput: createSetupInputPresenceValidator({
|
|
69
|
+
defaultAccountOnlyEnvError: "ZALO_BOT_TOKEN can only be used for the default account.",
|
|
70
|
+
whenNotUseEnv: [{
|
|
71
|
+
someOf: ["token", "tokenFile"],
|
|
72
|
+
message: "Zalo requires token or --token-file (or --use-env)."
|
|
73
|
+
}]
|
|
74
|
+
}),
|
|
75
|
+
buildPatch: (input) => input.useEnv ? {} : input.tokenFile ? { tokenFile: input.tokenFile } : input.token ? { botToken: input.token } : {}
|
|
76
|
+
});
|
|
77
|
+
const zaloDmPolicy = {
|
|
78
|
+
label: "Zalo",
|
|
79
|
+
channel,
|
|
80
|
+
policyKey: "channels.zalo.dmPolicy",
|
|
81
|
+
allowFromKey: "channels.zalo.allowFrom",
|
|
82
|
+
resolveConfigKeys: (cfg, accountId) => (accountId ?? resolveDefaultZaloAccountId(cfg)) !== DEFAULT_ACCOUNT_ID ? {
|
|
83
|
+
policyKey: `channels.zalo.accounts.${accountId ?? resolveDefaultZaloAccountId(cfg)}.dmPolicy`,
|
|
84
|
+
allowFromKey: `channels.zalo.accounts.${accountId ?? resolveDefaultZaloAccountId(cfg)}.allowFrom`
|
|
85
|
+
} : {
|
|
86
|
+
policyKey: "channels.zalo.dmPolicy",
|
|
87
|
+
allowFromKey: "channels.zalo.allowFrom"
|
|
88
|
+
},
|
|
89
|
+
getCurrent: (cfg, accountId) => resolveZaloAccount({
|
|
90
|
+
cfg,
|
|
91
|
+
accountId: accountId ?? resolveDefaultZaloAccountId(cfg)
|
|
92
|
+
}).config.dmPolicy ?? "pairing",
|
|
93
|
+
setPolicy: (cfg, policy, accountId) => {
|
|
94
|
+
const resolvedAccountId = accountId && normalizeAccountId(accountId) ? normalizeAccountId(accountId) ?? DEFAULT_ACCOUNT_ID : resolveDefaultZaloAccountId(cfg);
|
|
95
|
+
const resolved = resolveZaloAccount({
|
|
96
|
+
cfg,
|
|
97
|
+
accountId: resolvedAccountId
|
|
98
|
+
});
|
|
99
|
+
if (resolvedAccountId === DEFAULT_ACCOUNT_ID) return {
|
|
100
|
+
...cfg,
|
|
101
|
+
channels: {
|
|
102
|
+
...cfg.channels,
|
|
103
|
+
zalo: {
|
|
104
|
+
...cfg.channels?.zalo,
|
|
105
|
+
enabled: true,
|
|
106
|
+
dmPolicy: policy,
|
|
107
|
+
...policy === "open" ? { allowFrom: addWildcardAllowFrom(resolved.config.allowFrom) } : {}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
const currentAccount = cfg.channels?.zalo?.accounts?.[resolvedAccountId];
|
|
112
|
+
return {
|
|
113
|
+
...cfg,
|
|
114
|
+
channels: {
|
|
115
|
+
...cfg.channels,
|
|
116
|
+
zalo: {
|
|
117
|
+
...cfg.channels?.zalo,
|
|
118
|
+
enabled: true,
|
|
119
|
+
accounts: {
|
|
120
|
+
...cfg.channels?.zalo?.accounts,
|
|
121
|
+
[resolvedAccountId]: {
|
|
122
|
+
...currentAccount,
|
|
123
|
+
enabled: currentAccount?.enabled ?? true,
|
|
124
|
+
dmPolicy: policy,
|
|
125
|
+
...policy === "open" ? { allowFrom: addWildcardAllowFrom(resolved.config.allowFrom) } : {}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
},
|
|
132
|
+
promptAllowFrom: async ({ cfg, prompter, accountId }) => promptZaloAllowFrom({
|
|
133
|
+
cfg,
|
|
134
|
+
prompter,
|
|
135
|
+
accountId: accountId ?? resolveDefaultZaloAccountId(cfg)
|
|
136
|
+
})
|
|
137
|
+
};
|
|
138
|
+
function createZaloSetupWizardProxy(loadWizard) {
|
|
139
|
+
return createDelegatedSetupWizardProxy({
|
|
140
|
+
channel,
|
|
141
|
+
loadWizard,
|
|
142
|
+
status: {
|
|
143
|
+
configuredLabel: "configured",
|
|
144
|
+
unconfiguredLabel: "needs token",
|
|
145
|
+
configuredHint: "recommended · configured",
|
|
146
|
+
unconfiguredHint: "recommended · newcomer-friendly",
|
|
147
|
+
configuredScore: 1,
|
|
148
|
+
unconfiguredScore: 10
|
|
149
|
+
},
|
|
150
|
+
credentials: [],
|
|
151
|
+
delegateFinalize: true,
|
|
152
|
+
dmPolicy: zaloDmPolicy,
|
|
153
|
+
disable: (cfg) => ({
|
|
154
|
+
...cfg,
|
|
155
|
+
channels: {
|
|
156
|
+
...cfg.channels,
|
|
157
|
+
zalo: {
|
|
158
|
+
...cfg.channels?.zalo,
|
|
159
|
+
enabled: false
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
})
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
//#endregion
|
|
166
|
+
export { promptZaloAllowFrom as a, noteZaloTokenHelp as i, zaloDmPolicy as n, zaloSetupAdapter as r, createZaloSetupWizardProxy as t };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entry-contract";
|
|
2
|
+
//#region extensions/zalo/setup-entry.ts
|
|
3
|
+
var setup_entry_default = defineBundledChannelSetupEntry({
|
|
4
|
+
importMetaUrl: import.meta.url,
|
|
5
|
+
plugin: {
|
|
6
|
+
specifier: "./api.js",
|
|
7
|
+
exportName: "zaloPlugin"
|
|
8
|
+
},
|
|
9
|
+
secrets: {
|
|
10
|
+
specifier: "./secret-contract-api.js",
|
|
11
|
+
exportName: "channelSecrets"
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
//#endregion
|
|
15
|
+
export { setup_entry_default as default };
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { a as resolveZaloAccount } from "./accounts-9NLDDlZ8.js";
|
|
2
|
+
import { a as promptZaloAllowFrom, i as noteZaloTokenHelp, n as zaloDmPolicy } from "./setup-core-DigRD3j1.js";
|
|
3
|
+
import { DEFAULT_ACCOUNT_ID, buildSingleChannelSecretPromptState, createStandardChannelSetupStatus, hasConfiguredSecretInput, promptSingleChannelSecretInput, runSingleChannelSecretStep } from "openclaw/plugin-sdk/setup";
|
|
4
|
+
//#region extensions/zalo/src/setup-surface.ts
|
|
5
|
+
const channel = "zalo";
|
|
6
|
+
function setZaloUpdateMode(cfg, accountId, mode, webhookUrl, webhookSecret, webhookPath) {
|
|
7
|
+
const isDefault = accountId === DEFAULT_ACCOUNT_ID;
|
|
8
|
+
if (mode === "polling") {
|
|
9
|
+
if (isDefault) {
|
|
10
|
+
const { webhookUrl: _url, webhookSecret: _secret, webhookPath: _path, ...rest } = cfg.channels?.zalo ?? {};
|
|
11
|
+
return {
|
|
12
|
+
...cfg,
|
|
13
|
+
channels: {
|
|
14
|
+
...cfg.channels,
|
|
15
|
+
zalo: rest
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
const accounts = { ...cfg.channels?.zalo?.accounts };
|
|
20
|
+
const { webhookUrl: _url, webhookSecret: _secret, webhookPath: _path, ...rest } = accounts[accountId] ?? {};
|
|
21
|
+
accounts[accountId] = rest;
|
|
22
|
+
return {
|
|
23
|
+
...cfg,
|
|
24
|
+
channels: {
|
|
25
|
+
...cfg.channels,
|
|
26
|
+
zalo: {
|
|
27
|
+
...cfg.channels?.zalo,
|
|
28
|
+
accounts
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
if (isDefault) return {
|
|
34
|
+
...cfg,
|
|
35
|
+
channels: {
|
|
36
|
+
...cfg.channels,
|
|
37
|
+
zalo: {
|
|
38
|
+
...cfg.channels?.zalo,
|
|
39
|
+
webhookUrl,
|
|
40
|
+
webhookSecret,
|
|
41
|
+
webhookPath
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
const accounts = { ...cfg.channels?.zalo?.accounts };
|
|
46
|
+
accounts[accountId] = {
|
|
47
|
+
...accounts[accountId],
|
|
48
|
+
webhookUrl,
|
|
49
|
+
webhookSecret,
|
|
50
|
+
webhookPath
|
|
51
|
+
};
|
|
52
|
+
return {
|
|
53
|
+
...cfg,
|
|
54
|
+
channels: {
|
|
55
|
+
...cfg.channels,
|
|
56
|
+
zalo: {
|
|
57
|
+
...cfg.channels?.zalo,
|
|
58
|
+
accounts
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const zaloSetupWizard = {
|
|
64
|
+
channel,
|
|
65
|
+
status: createStandardChannelSetupStatus({
|
|
66
|
+
channelLabel: "Zalo",
|
|
67
|
+
configuredLabel: "configured",
|
|
68
|
+
unconfiguredLabel: "needs token",
|
|
69
|
+
configuredHint: "recommended · configured",
|
|
70
|
+
unconfiguredHint: "recommended · newcomer-friendly",
|
|
71
|
+
configuredScore: 1,
|
|
72
|
+
unconfiguredScore: 10,
|
|
73
|
+
includeStatusLine: true,
|
|
74
|
+
resolveConfigured: ({ cfg, accountId }) => {
|
|
75
|
+
const account = resolveZaloAccount({
|
|
76
|
+
cfg,
|
|
77
|
+
accountId,
|
|
78
|
+
allowUnresolvedSecretRef: true
|
|
79
|
+
});
|
|
80
|
+
return Boolean(account.token) || hasConfiguredSecretInput(account.config.botToken) || Boolean(account.config.tokenFile?.trim());
|
|
81
|
+
}
|
|
82
|
+
}),
|
|
83
|
+
credentials: [],
|
|
84
|
+
finalize: async ({ cfg, accountId, forceAllowFrom, options, prompter }) => {
|
|
85
|
+
let next = cfg;
|
|
86
|
+
const resolvedAccount = resolveZaloAccount({
|
|
87
|
+
cfg: next,
|
|
88
|
+
accountId,
|
|
89
|
+
allowUnresolvedSecretRef: true
|
|
90
|
+
});
|
|
91
|
+
const accountConfigured = Boolean(resolvedAccount.token);
|
|
92
|
+
const allowEnv = accountId === DEFAULT_ACCOUNT_ID;
|
|
93
|
+
const hasConfigToken = Boolean(hasConfiguredSecretInput(resolvedAccount.config.botToken) || resolvedAccount.config.tokenFile);
|
|
94
|
+
next = (await runSingleChannelSecretStep({
|
|
95
|
+
cfg: next,
|
|
96
|
+
prompter,
|
|
97
|
+
providerHint: "zalo",
|
|
98
|
+
credentialLabel: "bot token",
|
|
99
|
+
secretInputMode: options?.secretInputMode,
|
|
100
|
+
accountConfigured,
|
|
101
|
+
hasConfigToken,
|
|
102
|
+
allowEnv,
|
|
103
|
+
envValue: process.env.ZALO_BOT_TOKEN,
|
|
104
|
+
envPrompt: "ZALO_BOT_TOKEN detected. Use env var?",
|
|
105
|
+
keepPrompt: "Zalo token already configured. Keep it?",
|
|
106
|
+
inputPrompt: "Enter Zalo bot token",
|
|
107
|
+
preferredEnvVar: "ZALO_BOT_TOKEN",
|
|
108
|
+
onMissingConfigured: async () => await noteZaloTokenHelp(prompter),
|
|
109
|
+
applyUseEnv: async (currentCfg) => accountId === DEFAULT_ACCOUNT_ID ? {
|
|
110
|
+
...currentCfg,
|
|
111
|
+
channels: {
|
|
112
|
+
...currentCfg.channels,
|
|
113
|
+
zalo: {
|
|
114
|
+
...currentCfg.channels?.zalo,
|
|
115
|
+
enabled: true
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
} : currentCfg,
|
|
119
|
+
applySet: async (currentCfg, value) => accountId === DEFAULT_ACCOUNT_ID ? {
|
|
120
|
+
...currentCfg,
|
|
121
|
+
channels: {
|
|
122
|
+
...currentCfg.channels,
|
|
123
|
+
zalo: {
|
|
124
|
+
...currentCfg.channels?.zalo,
|
|
125
|
+
enabled: true,
|
|
126
|
+
botToken: value
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
} : {
|
|
130
|
+
...currentCfg,
|
|
131
|
+
channels: {
|
|
132
|
+
...currentCfg.channels,
|
|
133
|
+
zalo: {
|
|
134
|
+
...currentCfg.channels?.zalo,
|
|
135
|
+
enabled: true,
|
|
136
|
+
accounts: {
|
|
137
|
+
...currentCfg.channels?.zalo?.accounts,
|
|
138
|
+
[accountId]: {
|
|
139
|
+
...currentCfg.channels?.zalo?.accounts?.[accountId],
|
|
140
|
+
enabled: true,
|
|
141
|
+
botToken: value
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
})).cfg;
|
|
148
|
+
if (await prompter.confirm({
|
|
149
|
+
message: "Use webhook mode for Zalo?",
|
|
150
|
+
initialValue: Boolean(resolvedAccount.config.webhookUrl)
|
|
151
|
+
})) {
|
|
152
|
+
const webhookUrl = (await prompter.text({
|
|
153
|
+
message: "Webhook URL (https://...) ",
|
|
154
|
+
initialValue: resolvedAccount.config.webhookUrl,
|
|
155
|
+
validate: (value) => value?.trim()?.startsWith("https://") ? void 0 : "HTTPS URL required"
|
|
156
|
+
})).trim();
|
|
157
|
+
const defaultPath = (() => {
|
|
158
|
+
try {
|
|
159
|
+
return new URL(webhookUrl).pathname || "/zalo-webhook";
|
|
160
|
+
} catch {
|
|
161
|
+
return "/zalo-webhook";
|
|
162
|
+
}
|
|
163
|
+
})();
|
|
164
|
+
let webhookSecretResult = await promptSingleChannelSecretInput({
|
|
165
|
+
cfg: next,
|
|
166
|
+
prompter,
|
|
167
|
+
providerHint: "zalo-webhook",
|
|
168
|
+
credentialLabel: "webhook secret",
|
|
169
|
+
secretInputMode: options?.secretInputMode,
|
|
170
|
+
...buildSingleChannelSecretPromptState({
|
|
171
|
+
accountConfigured: hasConfiguredSecretInput(resolvedAccount.config.webhookSecret),
|
|
172
|
+
hasConfigToken: hasConfiguredSecretInput(resolvedAccount.config.webhookSecret),
|
|
173
|
+
allowEnv: false
|
|
174
|
+
}),
|
|
175
|
+
envPrompt: "",
|
|
176
|
+
keepPrompt: "Zalo webhook secret already configured. Keep it?",
|
|
177
|
+
inputPrompt: "Webhook secret (8-256 chars)",
|
|
178
|
+
preferredEnvVar: "ZALO_WEBHOOK_SECRET"
|
|
179
|
+
});
|
|
180
|
+
while (webhookSecretResult.action === "set" && typeof webhookSecretResult.value === "string" && (webhookSecretResult.value.length < 8 || webhookSecretResult.value.length > 256)) {
|
|
181
|
+
await prompter.note("Webhook secret must be between 8 and 256 characters.", "Zalo webhook");
|
|
182
|
+
webhookSecretResult = await promptSingleChannelSecretInput({
|
|
183
|
+
cfg: next,
|
|
184
|
+
prompter,
|
|
185
|
+
providerHint: "zalo-webhook",
|
|
186
|
+
credentialLabel: "webhook secret",
|
|
187
|
+
secretInputMode: options?.secretInputMode,
|
|
188
|
+
...buildSingleChannelSecretPromptState({
|
|
189
|
+
accountConfigured: false,
|
|
190
|
+
hasConfigToken: false,
|
|
191
|
+
allowEnv: false
|
|
192
|
+
}),
|
|
193
|
+
envPrompt: "",
|
|
194
|
+
keepPrompt: "Zalo webhook secret already configured. Keep it?",
|
|
195
|
+
inputPrompt: "Webhook secret (8-256 chars)",
|
|
196
|
+
preferredEnvVar: "ZALO_WEBHOOK_SECRET"
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
const webhookSecret = webhookSecretResult.action === "set" ? webhookSecretResult.value : resolvedAccount.config.webhookSecret;
|
|
200
|
+
const webhookPath = (await prompter.text({
|
|
201
|
+
message: "Webhook path (optional)",
|
|
202
|
+
initialValue: resolvedAccount.config.webhookPath ?? defaultPath
|
|
203
|
+
})).trim();
|
|
204
|
+
next = setZaloUpdateMode(next, accountId, "webhook", webhookUrl, webhookSecret, webhookPath || void 0);
|
|
205
|
+
} else next = setZaloUpdateMode(next, accountId, "polling");
|
|
206
|
+
if (forceAllowFrom) next = await promptZaloAllowFrom({
|
|
207
|
+
cfg: next,
|
|
208
|
+
prompter,
|
|
209
|
+
accountId
|
|
210
|
+
});
|
|
211
|
+
return { cfg: next };
|
|
212
|
+
},
|
|
213
|
+
dmPolicy: zaloDmPolicy
|
|
214
|
+
};
|
|
215
|
+
//#endregion
|
|
216
|
+
export { zaloSetupWizard };
|
package/dist/test-api.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/zalo",
|
|
3
|
-
"version": "2026.5.2",
|
|
3
|
+
"version": "2026.5.3-beta.2",
|
|
4
4
|
"description": "OpenClaw Zalo channel plugin",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"openclaw": "workspace:*"
|
|
16
16
|
},
|
|
17
17
|
"peerDependencies": {
|
|
18
|
-
"openclaw": ">=2026.5.2"
|
|
18
|
+
"openclaw": ">=2026.5.3-beta.2"
|
|
19
19
|
},
|
|
20
20
|
"peerDependenciesMeta": {
|
|
21
21
|
"openclaw": {
|
|
@@ -46,14 +46,23 @@
|
|
|
46
46
|
"minHostVersion": ">=2026.4.10"
|
|
47
47
|
},
|
|
48
48
|
"compat": {
|
|
49
|
-
"pluginApi": ">=2026.5.2"
|
|
49
|
+
"pluginApi": ">=2026.5.3-beta.2"
|
|
50
50
|
},
|
|
51
51
|
"build": {
|
|
52
|
-
"openclawVersion": "2026.5.2"
|
|
52
|
+
"openclawVersion": "2026.5.3-beta.2"
|
|
53
53
|
},
|
|
54
54
|
"release": {
|
|
55
55
|
"publishToClawHub": true,
|
|
56
56
|
"publishToNpm": true
|
|
57
|
-
}
|
|
58
|
-
|
|
57
|
+
},
|
|
58
|
+
"runtimeExtensions": [
|
|
59
|
+
"./dist/index.js"
|
|
60
|
+
],
|
|
61
|
+
"runtimeSetupEntry": "./dist/setup-entry.js"
|
|
62
|
+
},
|
|
63
|
+
"files": [
|
|
64
|
+
"dist/**",
|
|
65
|
+
"openclaw.plugin.json",
|
|
66
|
+
"README.md"
|
|
67
|
+
]
|
|
59
68
|
}
|
package/api.ts
DELETED
package/channel-plugin-api.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { zaloPlugin } from "./src/channel.js";
|
package/contract-api.ts
DELETED
package/index.test.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { assertBundledChannelEntries } from "openclaw/plugin-sdk/channel-test-helpers";
|
|
2
|
-
import { describe } from "vitest";
|
|
3
|
-
import entry from "./index.js";
|
|
4
|
-
import setupEntry from "./setup-entry.js";
|
|
5
|
-
|
|
6
|
-
describe("zalo bundled entries", () => {
|
|
7
|
-
assertBundledChannelEntries({
|
|
8
|
-
entry,
|
|
9
|
-
expectedId: "zalo",
|
|
10
|
-
expectedName: "Zalo",
|
|
11
|
-
setupEntry,
|
|
12
|
-
channelMessage: "declares the channel plugin without a runtime-barrel cycle",
|
|
13
|
-
setupMessage: "declares the setup plugin without a runtime-barrel cycle",
|
|
14
|
-
});
|
|
15
|
-
});
|
package/index.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { defineBundledChannelEntry } from "openclaw/plugin-sdk/channel-entry-contract";
|
|
2
|
-
|
|
3
|
-
export default defineBundledChannelEntry({
|
|
4
|
-
id: "zalo",
|
|
5
|
-
name: "Zalo",
|
|
6
|
-
description: "Zalo channel plugin",
|
|
7
|
-
importMetaUrl: import.meta.url,
|
|
8
|
-
plugin: {
|
|
9
|
-
specifier: "./channel-plugin-api.js",
|
|
10
|
-
exportName: "zaloPlugin",
|
|
11
|
-
},
|
|
12
|
-
secrets: {
|
|
13
|
-
specifier: "./secret-contract-api.js",
|
|
14
|
-
exportName: "channelSecrets",
|
|
15
|
-
},
|
|
16
|
-
runtime: {
|
|
17
|
-
specifier: "./runtime-api.js",
|
|
18
|
-
exportName: "setZaloRuntime",
|
|
19
|
-
},
|
|
20
|
-
});
|
package/runtime-api.test.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { runDirectImportSmoke } from "openclaw/plugin-sdk/plugin-test-contracts";
|
|
2
|
-
import { describe, expect, it } from "vitest";
|
|
3
|
-
|
|
4
|
-
describe("zalo runtime api", () => {
|
|
5
|
-
it("loads the narrow runtime api without reentering setup surfaces", async () => {
|
|
6
|
-
const stdout = await runDirectImportSmoke(
|
|
7
|
-
`const runtime = await import("./extensions/zalo/runtime-api.ts");
|
|
8
|
-
process.stdout.write(JSON.stringify({
|
|
9
|
-
hasZaloPlugin: Object.hasOwn(runtime, "zaloPlugin"),
|
|
10
|
-
hasZaloSetupWizard: Object.hasOwn(runtime, "zaloSetupWizard"),
|
|
11
|
-
type: typeof runtime.setZaloRuntime,
|
|
12
|
-
}));`,
|
|
13
|
-
);
|
|
14
|
-
|
|
15
|
-
expect(stdout).toBe('{"hasZaloPlugin":false,"hasZaloSetupWizard":false,"type":"function"}');
|
|
16
|
-
}, 45_000);
|
|
17
|
-
});
|
package/runtime-api.ts
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
export {
|
|
2
|
-
addWildcardAllowFrom,
|
|
3
|
-
applyAccountNameToChannelSection,
|
|
4
|
-
applyBasicWebhookRequestGuards,
|
|
5
|
-
applySetupAccountConfigPatch,
|
|
6
|
-
type BaseProbeResult,
|
|
7
|
-
type BaseTokenResolution,
|
|
8
|
-
buildBaseAccountStatusSnapshot,
|
|
9
|
-
buildChannelConfigSchema,
|
|
10
|
-
buildSecretInputSchema,
|
|
11
|
-
buildSingleChannelSecretPromptState,
|
|
12
|
-
buildTokenChannelStatusSummary,
|
|
13
|
-
type ChannelAccountSnapshot,
|
|
14
|
-
type ChannelMessageActionAdapter,
|
|
15
|
-
type ChannelMessageActionName,
|
|
16
|
-
type ChannelPlugin,
|
|
17
|
-
type ChannelStatusIssue,
|
|
18
|
-
chunkTextForOutbound,
|
|
19
|
-
createChannelPairingController,
|
|
20
|
-
createChannelReplyPipeline,
|
|
21
|
-
createDedupeCache,
|
|
22
|
-
createFixedWindowRateLimiter,
|
|
23
|
-
createWebhookAnomalyTracker,
|
|
24
|
-
DEFAULT_ACCOUNT_ID,
|
|
25
|
-
deliverTextOrMediaReply,
|
|
26
|
-
evaluateSenderGroupAccess,
|
|
27
|
-
formatAllowFromLowercase,
|
|
28
|
-
formatPairingApproveHint,
|
|
29
|
-
type GroupPolicy,
|
|
30
|
-
hasConfiguredSecretInput,
|
|
31
|
-
isNormalizedSenderAllowed,
|
|
32
|
-
isNumericTargetId,
|
|
33
|
-
jsonResult,
|
|
34
|
-
logTypingFailure,
|
|
35
|
-
type MarkdownTableMode,
|
|
36
|
-
mergeAllowFromEntries,
|
|
37
|
-
migrateBaseNameToDefaultAccount,
|
|
38
|
-
normalizeAccountId,
|
|
39
|
-
normalizeResolvedSecretInputString,
|
|
40
|
-
normalizeSecretInputString,
|
|
41
|
-
type OpenClawConfig,
|
|
42
|
-
type OutboundReplyPayload,
|
|
43
|
-
PAIRING_APPROVED_MESSAGE,
|
|
44
|
-
type PluginRuntime,
|
|
45
|
-
promptSingleChannelSecretInput,
|
|
46
|
-
readJsonWebhookBodyOrReject,
|
|
47
|
-
readStringParam,
|
|
48
|
-
registerPluginHttpRoute,
|
|
49
|
-
type RegisterWebhookPluginRouteOptions,
|
|
50
|
-
registerWebhookTarget,
|
|
51
|
-
type RegisterWebhookTargetOptions,
|
|
52
|
-
registerWebhookTargetWithPluginRoute,
|
|
53
|
-
type ReplyPayload,
|
|
54
|
-
resolveClientIp,
|
|
55
|
-
resolveDefaultGroupPolicy,
|
|
56
|
-
resolveDirectDmAuthorizationOutcome,
|
|
57
|
-
resolveInboundRouteEnvelopeBuilderWithRuntime,
|
|
58
|
-
resolveOpenProviderRuntimeGroupPolicy,
|
|
59
|
-
resolveSenderCommandAuthorizationWithRuntime,
|
|
60
|
-
resolveWebhookPath,
|
|
61
|
-
resolveWebhookTargetWithAuthOrRejectSync,
|
|
62
|
-
runSingleChannelSecretStep,
|
|
63
|
-
type RuntimeEnv,
|
|
64
|
-
type SecretInput,
|
|
65
|
-
type SenderGroupAccessDecision,
|
|
66
|
-
sendPayloadWithChunkedTextAndMedia,
|
|
67
|
-
setTopLevelChannelDmPolicyWithAllowFrom,
|
|
68
|
-
setZaloRuntime,
|
|
69
|
-
waitForAbortSignal,
|
|
70
|
-
warnMissingProviderGroupPolicyFallbackOnce,
|
|
71
|
-
WEBHOOK_ANOMALY_COUNTER_DEFAULTS,
|
|
72
|
-
WEBHOOK_RATE_LIMIT_DEFAULTS,
|
|
73
|
-
withResolvedWebhookRequestPipeline,
|
|
74
|
-
type WizardPrompter,
|
|
75
|
-
} from "./src/runtime-api.js";
|
package/secret-contract-api.ts
DELETED
package/setup-api.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { loadBundledEntryExportSync } from "openclaw/plugin-sdk/channel-entry-contract";
|
|
2
|
-
|
|
3
|
-
type SetupSurfaceModule = typeof import("./src/setup-surface.js");
|
|
4
|
-
|
|
5
|
-
function createLazyObjectValue<T extends object>(load: () => T): T {
|
|
6
|
-
return new Proxy({} as T, {
|
|
7
|
-
get(_target, property, receiver) {
|
|
8
|
-
return Reflect.get(load(), property, receiver);
|
|
9
|
-
},
|
|
10
|
-
has(_target, property) {
|
|
11
|
-
return property in load();
|
|
12
|
-
},
|
|
13
|
-
ownKeys() {
|
|
14
|
-
return Reflect.ownKeys(load());
|
|
15
|
-
},
|
|
16
|
-
getOwnPropertyDescriptor(_target, property) {
|
|
17
|
-
const descriptor = Object.getOwnPropertyDescriptor(load(), property);
|
|
18
|
-
return descriptor ? { ...descriptor, configurable: true } : undefined;
|
|
19
|
-
},
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function loadSetupSurfaceModule(): SetupSurfaceModule {
|
|
24
|
-
return loadBundledEntryExportSync<SetupSurfaceModule>(import.meta.url, {
|
|
25
|
-
specifier: "./src/setup-surface.js",
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export { zaloDmPolicy, zaloSetupAdapter, createZaloSetupWizardProxy } from "./src/setup-core.js";
|
|
30
|
-
export { evaluateZaloGroupAccess, resolveZaloRuntimeGroupPolicy } from "./src/group-access.js";
|
|
31
|
-
|
|
32
|
-
export const zaloSetupWizard: SetupSurfaceModule["zaloSetupWizard"] = createLazyObjectValue(
|
|
33
|
-
() => loadSetupSurfaceModule().zaloSetupWizard as object,
|
|
34
|
-
) as SetupSurfaceModule["zaloSetupWizard"];
|
package/setup-entry.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entry-contract";
|
|
2
|
-
|
|
3
|
-
export default defineBundledChannelSetupEntry({
|
|
4
|
-
importMetaUrl: import.meta.url,
|
|
5
|
-
plugin: {
|
|
6
|
-
specifier: "./api.js",
|
|
7
|
-
exportName: "zaloPlugin",
|
|
8
|
-
},
|
|
9
|
-
secrets: {
|
|
10
|
-
specifier: "./secret-contract-api.js",
|
|
11
|
-
exportName: "channelSecrets",
|
|
12
|
-
},
|
|
13
|
-
});
|