mirror of
https://github.com/farcasclaudiu/openclaw.git
synced 2026-06-29 01:02:03 +03:00
chore: Run pnpm format:fix.
This commit is contained in:
@@ -3,61 +3,73 @@
|
||||
## 2026.1.30
|
||||
|
||||
### Changes
|
||||
|
||||
- Version alignment with core OpenClaw release numbers.
|
||||
|
||||
## 2026.1.29
|
||||
|
||||
### Changes
|
||||
|
||||
- Version alignment with core OpenClaw release numbers.
|
||||
|
||||
## 2026.1.23
|
||||
|
||||
### Changes
|
||||
|
||||
- Version alignment with core OpenClaw release numbers.
|
||||
|
||||
## 2026.1.22
|
||||
|
||||
### Changes
|
||||
|
||||
- Version alignment with core OpenClaw release numbers.
|
||||
|
||||
## 2026.1.21
|
||||
|
||||
### Changes
|
||||
|
||||
- Version alignment with core OpenClaw release numbers.
|
||||
|
||||
## 2026.1.20
|
||||
|
||||
### Changes
|
||||
|
||||
- Version alignment with core OpenClaw release numbers.
|
||||
|
||||
## 2026.1.17-1
|
||||
|
||||
### Changes
|
||||
|
||||
- Version alignment with core OpenClaw release numbers.
|
||||
|
||||
## 2026.1.17
|
||||
|
||||
### Changes
|
||||
|
||||
- Version alignment with core OpenClaw release numbers.
|
||||
|
||||
## 2026.1.16
|
||||
|
||||
### Changes
|
||||
|
||||
- Version alignment with core OpenClaw release numbers.
|
||||
|
||||
## 2026.1.15
|
||||
|
||||
### Changes
|
||||
|
||||
- Version alignment with core OpenClaw release numbers.
|
||||
|
||||
## 2026.1.14
|
||||
|
||||
### Changes
|
||||
|
||||
- Version alignment with core OpenClaw release numbers.
|
||||
|
||||
## 0.1.0
|
||||
|
||||
### Features
|
||||
|
||||
- Zalo Bot API channel plugin with token-based auth (env/config/file).
|
||||
- Direct message support (DMs only) with pairing/allowlist/open/disabled policies.
|
||||
- Polling and webhook delivery modes.
|
||||
|
||||
@@ -25,9 +25,9 @@ Onboarding: select Zalo and confirm the install prompt to fetch the plugin autom
|
||||
enabled: true,
|
||||
botToken: "12345689:abc-xyz",
|
||||
dmPolicy: "pairing",
|
||||
proxy: "http://proxy.local:8080"
|
||||
}
|
||||
}
|
||||
proxy: "http://proxy.local:8080",
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
@@ -39,9 +39,9 @@ Onboarding: select Zalo and confirm the install prompt to fetch the plugin autom
|
||||
zalo: {
|
||||
webhookUrl: "https://example.com/zalo-webhook",
|
||||
webhookSecret: "your-secret-8-plus-chars",
|
||||
webhookPath: "/zalo-webhook"
|
||||
}
|
||||
}
|
||||
webhookPath: "/zalo-webhook",
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
{
|
||||
"id": "zalo",
|
||||
"channels": [
|
||||
"zalo"
|
||||
],
|
||||
"channels": ["zalo"],
|
||||
"configSchema": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
{
|
||||
"name": "@openclaw/zalo",
|
||||
"version": "2026.1.30",
|
||||
"type": "module",
|
||||
"description": "OpenClaw Zalo channel plugin",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"openclaw": "workspace:*",
|
||||
"undici": "7.19.2"
|
||||
},
|
||||
"openclaw": {
|
||||
"extensions": [
|
||||
"./index.ts"
|
||||
@@ -25,9 +29,5 @@
|
||||
"localPath": "extensions/zalo",
|
||||
"defaultChoice": "npm"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"openclaw": "workspace:*",
|
||||
"undici": "7.19.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,12 @@ describe("zalo directory", () => {
|
||||
expect(zaloPlugin.directory?.listGroups).toBeTruthy();
|
||||
|
||||
await expect(
|
||||
zaloPlugin.directory!.listPeers({ cfg, accountId: undefined, query: undefined, limit: undefined }),
|
||||
zaloPlugin.directory!.listPeers({
|
||||
cfg,
|
||||
accountId: undefined,
|
||||
query: undefined,
|
||||
limit: undefined,
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.arrayContaining([
|
||||
{ kind: "user", id: "123" },
|
||||
@@ -28,8 +33,13 @@ describe("zalo directory", () => {
|
||||
]),
|
||||
);
|
||||
|
||||
await expect(zaloPlugin.directory!.listGroups({ cfg, accountId: undefined, query: undefined, limit: undefined })).resolves.toEqual(
|
||||
[],
|
||||
);
|
||||
await expect(
|
||||
zaloPlugin.directory!.listGroups({
|
||||
cfg,
|
||||
accountId: undefined,
|
||||
query: undefined,
|
||||
limit: undefined,
|
||||
}),
|
||||
).resolves.toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,7 +16,12 @@ import {
|
||||
setAccountEnabledInConfigSection,
|
||||
} from "openclaw/plugin-sdk";
|
||||
|
||||
import { listZaloAccountIds, resolveDefaultZaloAccountId, resolveZaloAccount, type ResolvedZaloAccount } from "./accounts.js";
|
||||
import {
|
||||
listZaloAccountIds,
|
||||
resolveDefaultZaloAccountId,
|
||||
resolveZaloAccount,
|
||||
type ResolvedZaloAccount,
|
||||
} from "./accounts.js";
|
||||
import { zaloMessageActions } from "./actions.js";
|
||||
import { ZaloConfigSchema } from "./config-schema.js";
|
||||
import { zaloOnboardingAdapter } from "./onboarding.js";
|
||||
@@ -88,7 +93,8 @@ export const zaloPlugin: ChannelPlugin<ResolvedZaloAccount> = {
|
||||
configSchema: buildChannelConfigSchema(ZaloConfigSchema),
|
||||
config: {
|
||||
listAccountIds: (cfg) => listZaloAccountIds(cfg as OpenClawConfig),
|
||||
resolveAccount: (cfg, accountId) => resolveZaloAccount({ cfg: cfg as OpenClawConfig, accountId }),
|
||||
resolveAccount: (cfg, accountId) =>
|
||||
resolveZaloAccount({ cfg: cfg as OpenClawConfig, accountId }),
|
||||
defaultAccountId: (cfg) => resolveDefaultZaloAccountId(cfg as OpenClawConfig),
|
||||
setAccountEnabled: ({ cfg, accountId, enabled }) =>
|
||||
setAccountEnabledInConfigSection({
|
||||
|
||||
@@ -137,9 +137,7 @@ export function registerZaloWebhookTarget(target: WebhookTarget): () => void {
|
||||
const next = [...existing, normalizedTarget];
|
||||
webhookTargets.set(key, next);
|
||||
return () => {
|
||||
const updated = (webhookTargets.get(key) ?? []).filter(
|
||||
(entry) => entry !== normalizedTarget,
|
||||
);
|
||||
const updated = (webhookTargets.get(key) ?? []).filter((entry) => entry !== normalizedTarget);
|
||||
if (updated.length > 0) {
|
||||
webhookTargets.set(key, updated);
|
||||
} else {
|
||||
@@ -181,12 +179,11 @@ export async function handleZaloWebhookRequest(
|
||||
|
||||
// Zalo sends updates directly as { event_name, message, ... }, not wrapped in { ok, result }
|
||||
const raw = body.value;
|
||||
const record =
|
||||
raw && typeof raw === "object" ? (raw as Record<string, unknown>) : null;
|
||||
const record = raw && typeof raw === "object" ? (raw as Record<string, unknown>) : null;
|
||||
const update: ZaloUpdate | undefined =
|
||||
record && record.ok === true && record.result
|
||||
? (record.result as ZaloUpdate)
|
||||
: (record as ZaloUpdate | null) ?? undefined;
|
||||
: ((record as ZaloUpdate | null) ?? undefined);
|
||||
|
||||
if (!update?.event_name) {
|
||||
res.statusCode = 400;
|
||||
@@ -292,16 +289,7 @@ async function processUpdate(
|
||||
|
||||
switch (event_name) {
|
||||
case "message.text.received":
|
||||
await handleTextMessage(
|
||||
message,
|
||||
token,
|
||||
account,
|
||||
config,
|
||||
runtime,
|
||||
core,
|
||||
statusSink,
|
||||
fetcher,
|
||||
);
|
||||
await handleTextMessage(message, token, account, config, runtime, core, statusSink, fetcher);
|
||||
break;
|
||||
case "message.image.received":
|
||||
await handleImageMessage(
|
||||
@@ -439,10 +427,7 @@ async function processMessageWithPipeline(params: {
|
||||
const dmPolicy = account.config.dmPolicy ?? "pairing";
|
||||
const configAllowFrom = (account.config.allowFrom ?? []).map((v) => String(v));
|
||||
const rawBody = text?.trim() || (mediaPath ? "<media:image>" : "");
|
||||
const shouldComputeAuth = core.channel.commands.shouldComputeCommandAuthorized(
|
||||
rawBody,
|
||||
config,
|
||||
);
|
||||
const shouldComputeAuth = core.channel.commands.shouldComputeCommandAuthorized(rawBody, config);
|
||||
const storeAllowFrom =
|
||||
!isGroup && (dmPolicy !== "open" || shouldComputeAuth)
|
||||
? await core.channel.pairing.readAllowFromStore("zalo").catch(() => [])
|
||||
@@ -453,7 +438,9 @@ async function processMessageWithPipeline(params: {
|
||||
const commandAuthorized = shouldComputeAuth
|
||||
? core.channel.commands.resolveCommandAuthorizedFromAuthorizers({
|
||||
useAccessGroups,
|
||||
authorizers: [{ configured: effectiveAllowFrom.length > 0, allowed: senderAllowedForCommands }],
|
||||
authorizers: [
|
||||
{ configured: effectiveAllowFrom.length > 0, allowed: senderAllowedForCommands },
|
||||
],
|
||||
})
|
||||
: undefined;
|
||||
|
||||
@@ -649,11 +636,7 @@ async function deliverZaloReply(params: {
|
||||
|
||||
if (text) {
|
||||
const chunkMode = core.channel.text.resolveChunkMode(config, "zalo", accountId);
|
||||
const chunks = core.channel.text.chunkMarkdownTextWithMode(
|
||||
text,
|
||||
ZALO_TEXT_LIMIT,
|
||||
chunkMode,
|
||||
);
|
||||
const chunks = core.channel.text.chunkMarkdownTextWithMode(text, ZALO_TEXT_LIMIT, chunkMode);
|
||||
for (const chunk of chunks) {
|
||||
try {
|
||||
await sendMessage(token, { chat_id: chatId, text: chunk }, fetcher);
|
||||
@@ -665,9 +648,7 @@ async function deliverZaloReply(params: {
|
||||
}
|
||||
}
|
||||
|
||||
export async function monitorZaloProvider(
|
||||
options: ZaloMonitorOptions,
|
||||
): Promise<ZaloMonitorResult> {
|
||||
export async function monitorZaloProvider(options: ZaloMonitorOptions): Promise<ZaloMonitorResult> {
|
||||
const {
|
||||
token,
|
||||
account,
|
||||
|
||||
@@ -46,23 +46,26 @@ describe("handleZaloWebhookRequest", () => {
|
||||
});
|
||||
|
||||
try {
|
||||
await withServer(async (req, res) => {
|
||||
const handled = await handleZaloWebhookRequest(req, res);
|
||||
if (!handled) {
|
||||
res.statusCode = 404;
|
||||
res.end("not found");
|
||||
}
|
||||
}, async (baseUrl) => {
|
||||
const response = await fetch(`${baseUrl}/hook`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"x-bot-api-secret-token": "secret",
|
||||
},
|
||||
body: "null",
|
||||
});
|
||||
await withServer(
|
||||
async (req, res) => {
|
||||
const handled = await handleZaloWebhookRequest(req, res);
|
||||
if (!handled) {
|
||||
res.statusCode = 404;
|
||||
res.end("not found");
|
||||
}
|
||||
},
|
||||
async (baseUrl) => {
|
||||
const response = await fetch(`${baseUrl}/hook`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"x-bot-api-secret-token": "secret",
|
||||
},
|
||||
body: "null",
|
||||
});
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
});
|
||||
expect(response.status).toBe(400);
|
||||
},
|
||||
);
|
||||
} finally {
|
||||
unregister();
|
||||
}
|
||||
|
||||
@@ -11,11 +11,7 @@ import {
|
||||
promptAccountId,
|
||||
} from "openclaw/plugin-sdk";
|
||||
|
||||
import {
|
||||
listZaloAccountIds,
|
||||
resolveDefaultZaloAccountId,
|
||||
resolveZaloAccount,
|
||||
} from "./accounts.js";
|
||||
import { listZaloAccountIds, resolveDefaultZaloAccountId, resolveZaloAccount } from "./accounts.js";
|
||||
|
||||
const channel = "zalo" as const;
|
||||
|
||||
@@ -25,7 +21,8 @@ function setZaloDmPolicy(
|
||||
cfg: OpenClawConfig,
|
||||
dmPolicy: "pairing" | "allowlist" | "open" | "disabled",
|
||||
) {
|
||||
const allowFrom = dmPolicy === "open" ? addWildcardAllowFrom(cfg.channels?.zalo?.allowFrom) : undefined;
|
||||
const allowFrom =
|
||||
dmPolicy === "open" ? addWildcardAllowFrom(cfg.channels?.zalo?.allowFrom) : undefined;
|
||||
return {
|
||||
...cfg,
|
||||
channels: {
|
||||
@@ -69,12 +66,7 @@ function setZaloUpdateMode(
|
||||
Record<string, unknown>
|
||||
>;
|
||||
const existing = accounts[accountId] ?? {};
|
||||
const {
|
||||
webhookUrl: _url,
|
||||
webhookSecret: _secret,
|
||||
webhookPath: _path,
|
||||
...rest
|
||||
} = existing;
|
||||
const { webhookUrl: _url, webhookSecret: _secret, webhookPath: _path, ...rest } = existing;
|
||||
accounts[accountId] = rest;
|
||||
return {
|
||||
...cfg,
|
||||
@@ -210,7 +202,7 @@ const dmPolicy: ChannelOnboardingDmPolicy = {
|
||||
promptAllowFrom: async ({ cfg, prompter, accountId }) => {
|
||||
const id =
|
||||
accountId && normalizeAccountId(accountId)
|
||||
? normalizeAccountId(accountId) ?? DEFAULT_ACCOUNT_ID
|
||||
? (normalizeAccountId(accountId) ?? DEFAULT_ACCOUNT_ID)
|
||||
: resolveDefaultZaloAccountId(cfg as OpenClawConfig);
|
||||
return promptZaloAllowFrom({
|
||||
cfg: cfg as OpenClawConfig,
|
||||
@@ -235,12 +227,16 @@ export const zaloOnboardingAdapter: ChannelOnboardingAdapter = {
|
||||
quickstartScore: configured ? 1 : 10,
|
||||
};
|
||||
},
|
||||
configure: async ({ cfg, prompter, accountOverrides, shouldPromptAccountIds, forceAllowFrom }) => {
|
||||
configure: async ({
|
||||
cfg,
|
||||
prompter,
|
||||
accountOverrides,
|
||||
shouldPromptAccountIds,
|
||||
forceAllowFrom,
|
||||
}) => {
|
||||
const zaloOverride = accountOverrides.zalo?.trim();
|
||||
const defaultZaloAccountId = resolveDefaultZaloAccountId(cfg as OpenClawConfig);
|
||||
let zaloAccountId = zaloOverride
|
||||
? normalizeAccountId(zaloOverride)
|
||||
: defaultZaloAccountId;
|
||||
let zaloAccountId = zaloOverride ? normalizeAccountId(zaloOverride) : defaultZaloAccountId;
|
||||
if (shouldPromptAccountIds && !zaloOverride) {
|
||||
zaloAccountId = await promptAccountId({
|
||||
cfg: cfg as OpenClawConfig,
|
||||
@@ -354,7 +350,8 @@ export const zaloOnboardingAdapter: ChannelOnboardingAdapter = {
|
||||
const webhookUrl = String(
|
||||
await prompter.text({
|
||||
message: "Webhook URL (https://...) ",
|
||||
validate: (value) => (value?.trim()?.startsWith("https://") ? undefined : "HTTPS URL required"),
|
||||
validate: (value) =>
|
||||
value?.trim()?.startsWith("https://") ? undefined : "HTTPS URL required",
|
||||
}),
|
||||
).trim();
|
||||
const defaultPath = (() => {
|
||||
|
||||
@@ -65,10 +65,14 @@ export async function sendMessageZalo(
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await sendMessage(token, {
|
||||
chat_id: chatId.trim(),
|
||||
text: text.slice(0, 2000),
|
||||
}, fetcher);
|
||||
const response = await sendMessage(
|
||||
token,
|
||||
{
|
||||
chat_id: chatId.trim(),
|
||||
text: text.slice(0, 2000),
|
||||
},
|
||||
fetcher,
|
||||
);
|
||||
|
||||
if (response.ok && response.result) {
|
||||
return { ok: true, messageId: response.result.message_id };
|
||||
@@ -100,11 +104,15 @@ export async function sendPhotoZalo(
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await sendPhoto(token, {
|
||||
chat_id: chatId.trim(),
|
||||
photo: photoUrl.trim(),
|
||||
caption: options.caption?.slice(0, 2000),
|
||||
}, fetcher);
|
||||
const response = await sendPhoto(
|
||||
token,
|
||||
{
|
||||
chat_id: chatId.trim(),
|
||||
photo: photoUrl.trim(),
|
||||
caption: options.caption?.slice(0, 2000),
|
||||
},
|
||||
fetcher,
|
||||
);
|
||||
|
||||
if (response.ok && response.result) {
|
||||
return { ok: true, messageId: response.result.message_id };
|
||||
|
||||
@@ -23,9 +23,7 @@ function readZaloAccountStatus(value: ChannelAccountSnapshot): ZaloAccountStatus
|
||||
};
|
||||
}
|
||||
|
||||
export function collectZaloStatusIssues(
|
||||
accounts: ChannelAccountSnapshot[],
|
||||
): ChannelStatusIssue[] {
|
||||
export function collectZaloStatusIssues(accounts: ChannelAccountSnapshot[]): ChannelStatusIssue[] {
|
||||
const issues: ChannelStatusIssue[] = [];
|
||||
for (const entry of accounts) {
|
||||
const account = readZaloAccountStatus(entry);
|
||||
@@ -40,8 +38,7 @@ export function collectZaloStatusIssues(
|
||||
channel: "zalo",
|
||||
accountId,
|
||||
kind: "config",
|
||||
message:
|
||||
'Zalo dmPolicy is "open", allowing any user to message the bot without pairing.',
|
||||
message: 'Zalo dmPolicy is "open", allowing any user to message the bot without pairing.',
|
||||
fix: 'Set channels.zalo.dmPolicy to "pairing" or "allowlist" to restrict access.',
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user