zalo_bot 1.0.0
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/.github/workflows/publish.yml +22 -0
- package/README.md +57 -0
- package/index.ts +19 -0
- package/openclaw.plugin.json +9 -0
- package/package.json +38 -0
- package/src/accounts.ts +82 -0
- package/src/actions.ts +56 -0
- package/src/api.ts +208 -0
- package/src/channel.ts +404 -0
- package/src/config-schema.ts +27 -0
- package/src/group-access.ts +48 -0
- package/src/monitor.ts +671 -0
- package/src/monitor.webhook.ts +221 -0
- package/src/onboarding.ts +398 -0
- package/src/probe.ts +45 -0
- package/src/proxy.ts +24 -0
- package/src/runtime.ts +14 -0
- package/src/send.ts +124 -0
- package/src/status-issues.ts +53 -0
- package/src/token.ts +62 -0
- package/src/types.ts +48 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
name: Publish to npm
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
publish:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- uses: actions/checkout@v4
|
|
12
|
+
|
|
13
|
+
- uses: actions/setup-node@v4
|
|
14
|
+
with:
|
|
15
|
+
node-version: 22
|
|
16
|
+
registry-url: https://registry.npmjs.org
|
|
17
|
+
|
|
18
|
+
- run: npm install
|
|
19
|
+
|
|
20
|
+
- run: npm publish --access public
|
|
21
|
+
env:
|
|
22
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
package/README.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# @openclaw/zalo-bot
|
|
2
|
+
|
|
3
|
+
OpenClaw Zalo channel plugin (Bot API) — patched fork.
|
|
4
|
+
|
|
5
|
+
## AI Install Metadata
|
|
6
|
+
- Plugin id: zalo_bot
|
|
7
|
+
- Channel id: zalo_bot
|
|
8
|
+
- Package name: @openclaw/zalo-bot
|
|
9
|
+
|
|
10
|
+
## Fixes
|
|
11
|
+
|
|
12
|
+
- **`photo_url` field mapping**: Correctly parses the Zalo Bot API's `photo_url` field for image messages
|
|
13
|
+
- **Image + Caption handling**: When a user sends both an image and text caption, both are now correctly forwarded to the AI agent
|
|
14
|
+
|
|
15
|
+
## Install (local checkout)
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Clone the repo
|
|
19
|
+
git clone https://github.com/vuminhtuanhvtc/openclaw-zalo-bot.git
|
|
20
|
+
cd openclaw-zalo-bot
|
|
21
|
+
|
|
22
|
+
# Install dependencies
|
|
23
|
+
npm install
|
|
24
|
+
|
|
25
|
+
# Register with OpenClaw
|
|
26
|
+
openclaw plugins install .
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Restart Gateway after installation.
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
|
|
33
|
+
Add channel config to `openclaw.json`:
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"channels": {
|
|
38
|
+
"zalo_bot": {
|
|
39
|
+
"enabled": true,
|
|
40
|
+
"token": "YOUR_ZALO_BOT_TOKEN",
|
|
41
|
+
"webhookUrl": "https://your-domain.com/zalo_bot-webhook",
|
|
42
|
+
"webhookSecret": "your-webhook-secret",
|
|
43
|
+
"dmPolicy": "open"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Then restart:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
openclaw gateway restart
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## License
|
|
56
|
+
|
|
57
|
+
MIT
|
package/index.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
2
|
+
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
|
|
3
|
+
import { zaloDock, zaloPlugin } from "./src/channel.js";
|
|
4
|
+
import { handleZaloWebhookRequest } from "./src/monitor.js";
|
|
5
|
+
import { setZaloRuntime } from "./src/runtime.js";
|
|
6
|
+
|
|
7
|
+
const plugin = {
|
|
8
|
+
id: "zalo_bot",
|
|
9
|
+
name: "Zalo Bot",
|
|
10
|
+
description: "Zalo channel plugin (Bot API) — patched fork",
|
|
11
|
+
configSchema: emptyPluginConfigSchema(),
|
|
12
|
+
register(api: OpenClawPluginApi) {
|
|
13
|
+
setZaloRuntime(api.runtime);
|
|
14
|
+
api.registerChannel({ plugin: zaloPlugin, dock: zaloDock });
|
|
15
|
+
api.registerHttpHandler(handleZaloWebhookRequest);
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default plugin;
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "zalo_bot",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "OpenClaw Zalo channel plugin (Bot API) — patched fork with photo_url and image+caption fixes",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"dependencies": {
|
|
7
|
+
"undici": "7.22.0",
|
|
8
|
+
"zod": "^4.3.6"
|
|
9
|
+
},
|
|
10
|
+
"openclaw": {
|
|
11
|
+
"extensions": [
|
|
12
|
+
"./index.ts"
|
|
13
|
+
],
|
|
14
|
+
"channel": {
|
|
15
|
+
"id": "zalo_bot",
|
|
16
|
+
"label": "Zalo Bot",
|
|
17
|
+
"selectionLabel": "Zalo Bot (Bot API)",
|
|
18
|
+
"docsPath": "/channels/zalo",
|
|
19
|
+
"docsLabel": "zalo_bot",
|
|
20
|
+
"blurb": "Vietnam-focused messaging platform with Bot API (patched).",
|
|
21
|
+
"aliases": [
|
|
22
|
+
"zb"
|
|
23
|
+
],
|
|
24
|
+
"order": 81,
|
|
25
|
+
"quickstartAllowFrom": true
|
|
26
|
+
},
|
|
27
|
+
"install": {
|
|
28
|
+
"npmSpec": "zalo_bot",
|
|
29
|
+
"localPath": "extensions/zalo_bot",
|
|
30
|
+
"defaultChoice": "npm"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/vuminhtuanhvtc/openclaw-zalo-bot.git"
|
|
36
|
+
},
|
|
37
|
+
"license": "MIT"
|
|
38
|
+
}
|
package/src/accounts.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { OpenClawConfig } from "openclaw/plugin-sdk";
|
|
2
|
+
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
|
|
3
|
+
import { resolveZaloToken } from "./token.js";
|
|
4
|
+
import type { ResolvedZaloAccount, ZaloAccountConfig, ZaloConfig } from "./types.js";
|
|
5
|
+
|
|
6
|
+
export type { ResolvedZaloAccount };
|
|
7
|
+
|
|
8
|
+
function listConfiguredAccountIds(cfg: OpenClawConfig): string[] {
|
|
9
|
+
const accounts = (cfg.channels?.zalo_bot as ZaloConfig | undefined)?.accounts;
|
|
10
|
+
if (!accounts || typeof accounts !== "object") {
|
|
11
|
+
return [];
|
|
12
|
+
}
|
|
13
|
+
return Object.keys(accounts).filter(Boolean);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function listZaloAccountIds(cfg: OpenClawConfig): string[] {
|
|
17
|
+
const ids = listConfiguredAccountIds(cfg);
|
|
18
|
+
if (ids.length === 0) {
|
|
19
|
+
return [DEFAULT_ACCOUNT_ID];
|
|
20
|
+
}
|
|
21
|
+
return ids.toSorted((a, b) => a.localeCompare(b));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function resolveDefaultZaloAccountId(cfg: OpenClawConfig): string {
|
|
25
|
+
const zaloConfig = cfg.channels?.zalo_bot as ZaloConfig | undefined;
|
|
26
|
+
if (zaloConfig?.defaultAccount?.trim()) {
|
|
27
|
+
return zaloConfig.defaultAccount.trim();
|
|
28
|
+
}
|
|
29
|
+
const ids = listZaloAccountIds(cfg);
|
|
30
|
+
if (ids.includes(DEFAULT_ACCOUNT_ID)) {
|
|
31
|
+
return DEFAULT_ACCOUNT_ID;
|
|
32
|
+
}
|
|
33
|
+
return ids[0] ?? DEFAULT_ACCOUNT_ID;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function resolveAccountConfig(
|
|
37
|
+
cfg: OpenClawConfig,
|
|
38
|
+
accountId: string,
|
|
39
|
+
): ZaloAccountConfig | undefined {
|
|
40
|
+
const accounts = (cfg.channels?.zalo_bot as ZaloConfig | undefined)?.accounts;
|
|
41
|
+
if (!accounts || typeof accounts !== "object") {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
return accounts[accountId] as ZaloAccountConfig | undefined;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function mergeZaloAccountConfig(cfg: OpenClawConfig, accountId: string): ZaloAccountConfig {
|
|
48
|
+
const raw = (cfg.channels?.zalo_bot ?? {}) as ZaloConfig;
|
|
49
|
+
const { accounts: _ignored, defaultAccount: _ignored2, ...base } = raw;
|
|
50
|
+
const account = resolveAccountConfig(cfg, accountId) ?? {};
|
|
51
|
+
return { ...base, ...account };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function resolveZaloAccount(params: {
|
|
55
|
+
cfg: OpenClawConfig;
|
|
56
|
+
accountId?: string | null;
|
|
57
|
+
}): ResolvedZaloAccount {
|
|
58
|
+
const accountId = normalizeAccountId(params.accountId);
|
|
59
|
+
const baseEnabled = (params.cfg.channels?.zalo_bot as ZaloConfig | undefined)?.enabled !== false;
|
|
60
|
+
const merged = mergeZaloAccountConfig(params.cfg, accountId);
|
|
61
|
+
const accountEnabled = merged.enabled !== false;
|
|
62
|
+
const enabled = baseEnabled && accountEnabled;
|
|
63
|
+
const tokenResolution = resolveZaloToken(
|
|
64
|
+
params.cfg.channels?.zalo_bot as ZaloConfig | undefined,
|
|
65
|
+
accountId,
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
accountId,
|
|
70
|
+
name: merged.name?.trim() || undefined,
|
|
71
|
+
enabled,
|
|
72
|
+
token: tokenResolution.token,
|
|
73
|
+
tokenSource: tokenResolution.source,
|
|
74
|
+
config: merged,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function listEnabledZaloAccounts(cfg: OpenClawConfig): ResolvedZaloAccount[] {
|
|
79
|
+
return listZaloAccountIds(cfg)
|
|
80
|
+
.map((accountId) => resolveZaloAccount({ cfg, accountId }))
|
|
81
|
+
.filter((account) => account.enabled);
|
|
82
|
+
}
|
package/src/actions.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ChannelMessageActionAdapter,
|
|
3
|
+
ChannelMessageActionName,
|
|
4
|
+
OpenClawConfig,
|
|
5
|
+
} from "openclaw/plugin-sdk";
|
|
6
|
+
import { extractToolSend, jsonResult, readStringParam } from "openclaw/plugin-sdk";
|
|
7
|
+
import { listEnabledZaloAccounts } from "./accounts.js";
|
|
8
|
+
import { sendMessageZalo } from "./send.js";
|
|
9
|
+
|
|
10
|
+
const providerId = "zalo";
|
|
11
|
+
|
|
12
|
+
function listEnabledAccounts(cfg: OpenClawConfig) {
|
|
13
|
+
return listEnabledZaloAccounts(cfg).filter(
|
|
14
|
+
(account) => account.enabled && account.tokenSource !== "none",
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const zaloMessageActions: ChannelMessageActionAdapter = {
|
|
19
|
+
listActions: ({ cfg }) => {
|
|
20
|
+
const accounts = listEnabledAccounts(cfg);
|
|
21
|
+
if (accounts.length === 0) {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
const actions = new Set<ChannelMessageActionName>(["send"]);
|
|
25
|
+
return Array.from(actions);
|
|
26
|
+
},
|
|
27
|
+
supportsButtons: () => false,
|
|
28
|
+
extractToolSend: ({ args }) => extractToolSend(args, "sendMessage"),
|
|
29
|
+
handleAction: async ({ action, params, cfg, accountId }) => {
|
|
30
|
+
if (action === "send") {
|
|
31
|
+
const to = readStringParam(params, "to", { required: true });
|
|
32
|
+
const content = readStringParam(params, "message", {
|
|
33
|
+
required: true,
|
|
34
|
+
allowEmpty: true,
|
|
35
|
+
});
|
|
36
|
+
const mediaUrl = readStringParam(params, "media", { trim: false });
|
|
37
|
+
|
|
38
|
+
const result = await sendMessageZalo(to ?? "", content ?? "", {
|
|
39
|
+
accountId: accountId ?? undefined,
|
|
40
|
+
mediaUrl: mediaUrl ?? undefined,
|
|
41
|
+
cfg: cfg,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
if (!result.ok) {
|
|
45
|
+
return jsonResult({
|
|
46
|
+
ok: false,
|
|
47
|
+
error: result.error ?? "Failed to send Zalo message",
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return jsonResult({ ok: true, to, messageId: result.messageId });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
throw new Error(`Action ${action} is not supported for provider ${providerId}.`);
|
|
55
|
+
},
|
|
56
|
+
};
|
package/src/api.ts
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zalo Bot API client
|
|
3
|
+
* @see https://bot.zaloplatforms.com/docs
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const ZALO_API_BASE = "https://bot-api.zaloplatforms.com";
|
|
7
|
+
|
|
8
|
+
export type ZaloFetch = (input: string, init?: RequestInit) => Promise<Response>;
|
|
9
|
+
|
|
10
|
+
export type ZaloApiResponse<T = unknown> = {
|
|
11
|
+
ok: boolean;
|
|
12
|
+
result?: T;
|
|
13
|
+
error_code?: number;
|
|
14
|
+
description?: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type ZaloBotInfo = {
|
|
18
|
+
id: string;
|
|
19
|
+
name: string;
|
|
20
|
+
avatar?: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type ZaloMessage = {
|
|
24
|
+
message_id: string;
|
|
25
|
+
from: {
|
|
26
|
+
id: string;
|
|
27
|
+
name?: string;
|
|
28
|
+
avatar?: string;
|
|
29
|
+
};
|
|
30
|
+
chat: {
|
|
31
|
+
id: string;
|
|
32
|
+
chat_type: "PRIVATE" | "GROUP";
|
|
33
|
+
};
|
|
34
|
+
date: number;
|
|
35
|
+
text?: string;
|
|
36
|
+
photo_url?: string;
|
|
37
|
+
caption?: string;
|
|
38
|
+
sticker?: string;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export type ZaloUpdate = {
|
|
42
|
+
event_name:
|
|
43
|
+
| "message.text.received"
|
|
44
|
+
| "message.image.received"
|
|
45
|
+
| "message.sticker.received"
|
|
46
|
+
| "message.unsupported.received";
|
|
47
|
+
message?: ZaloMessage;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export type ZaloSendMessageParams = {
|
|
51
|
+
chat_id: string;
|
|
52
|
+
text: string;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export type ZaloSendPhotoParams = {
|
|
56
|
+
chat_id: string;
|
|
57
|
+
photo: string;
|
|
58
|
+
caption?: string;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export type ZaloSetWebhookParams = {
|
|
62
|
+
url: string;
|
|
63
|
+
secret_token: string;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export type ZaloGetUpdatesParams = {
|
|
67
|
+
/** Timeout in seconds (passed as string to API) */
|
|
68
|
+
timeout?: number;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export class ZaloApiError extends Error {
|
|
72
|
+
constructor(
|
|
73
|
+
message: string,
|
|
74
|
+
public readonly errorCode?: number,
|
|
75
|
+
public readonly description?: string,
|
|
76
|
+
) {
|
|
77
|
+
super(message);
|
|
78
|
+
this.name = "ZaloApiError";
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** True if this is a long-polling timeout (no updates available) */
|
|
82
|
+
get isPollingTimeout(): boolean {
|
|
83
|
+
return this.errorCode === 408;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Call the Zalo Bot API
|
|
89
|
+
*/
|
|
90
|
+
export async function callZaloApi<T = unknown>(
|
|
91
|
+
method: string,
|
|
92
|
+
token: string,
|
|
93
|
+
body?: Record<string, unknown>,
|
|
94
|
+
options?: { timeoutMs?: number; fetch?: ZaloFetch },
|
|
95
|
+
): Promise<ZaloApiResponse<T>> {
|
|
96
|
+
const url = `${ZALO_API_BASE}/bot${token}/${method}`;
|
|
97
|
+
const controller = new AbortController();
|
|
98
|
+
const timeoutId = options?.timeoutMs
|
|
99
|
+
? setTimeout(() => controller.abort(), options.timeoutMs)
|
|
100
|
+
: undefined;
|
|
101
|
+
const fetcher = options?.fetch ?? fetch;
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
const response = await fetcher(url, {
|
|
105
|
+
method: "POST",
|
|
106
|
+
headers: {
|
|
107
|
+
"Content-Type": "application/json",
|
|
108
|
+
},
|
|
109
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
110
|
+
signal: controller.signal,
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const data = (await response.json()) as ZaloApiResponse<T>;
|
|
114
|
+
|
|
115
|
+
if (!data.ok) {
|
|
116
|
+
throw new ZaloApiError(
|
|
117
|
+
data.description ?? `Zalo API error: ${method}`,
|
|
118
|
+
data.error_code,
|
|
119
|
+
data.description,
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return data;
|
|
124
|
+
} finally {
|
|
125
|
+
if (timeoutId) {
|
|
126
|
+
clearTimeout(timeoutId);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Validate bot token and get bot info
|
|
133
|
+
*/
|
|
134
|
+
export async function getMe(
|
|
135
|
+
token: string,
|
|
136
|
+
timeoutMs?: number,
|
|
137
|
+
fetcher?: ZaloFetch,
|
|
138
|
+
): Promise<ZaloApiResponse<ZaloBotInfo>> {
|
|
139
|
+
return callZaloApi<ZaloBotInfo>("getMe", token, undefined, { timeoutMs, fetch: fetcher });
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Send a text message
|
|
144
|
+
*/
|
|
145
|
+
export async function sendMessage(
|
|
146
|
+
token: string,
|
|
147
|
+
params: ZaloSendMessageParams,
|
|
148
|
+
fetcher?: ZaloFetch,
|
|
149
|
+
): Promise<ZaloApiResponse<ZaloMessage>> {
|
|
150
|
+
return callZaloApi<ZaloMessage>("sendMessage", token, params, { fetch: fetcher });
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Send a photo message
|
|
155
|
+
*/
|
|
156
|
+
export async function sendPhoto(
|
|
157
|
+
token: string,
|
|
158
|
+
params: ZaloSendPhotoParams,
|
|
159
|
+
fetcher?: ZaloFetch,
|
|
160
|
+
): Promise<ZaloApiResponse<ZaloMessage>> {
|
|
161
|
+
return callZaloApi<ZaloMessage>("sendPhoto", token, params, { fetch: fetcher });
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Get updates using long polling (dev/testing only)
|
|
166
|
+
* Note: Zalo returns a single update per call, not an array like Telegram
|
|
167
|
+
*/
|
|
168
|
+
export async function getUpdates(
|
|
169
|
+
token: string,
|
|
170
|
+
params?: ZaloGetUpdatesParams,
|
|
171
|
+
fetcher?: ZaloFetch,
|
|
172
|
+
): Promise<ZaloApiResponse<ZaloUpdate>> {
|
|
173
|
+
const pollTimeoutSec = params?.timeout ?? 30;
|
|
174
|
+
const timeoutMs = (pollTimeoutSec + 5) * 1000;
|
|
175
|
+
const body = { timeout: String(pollTimeoutSec) };
|
|
176
|
+
return callZaloApi<ZaloUpdate>("getUpdates", token, body, { timeoutMs, fetch: fetcher });
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Set webhook URL for receiving updates
|
|
181
|
+
*/
|
|
182
|
+
export async function setWebhook(
|
|
183
|
+
token: string,
|
|
184
|
+
params: ZaloSetWebhookParams,
|
|
185
|
+
fetcher?: ZaloFetch,
|
|
186
|
+
): Promise<ZaloApiResponse<boolean>> {
|
|
187
|
+
return callZaloApi<boolean>("setWebhook", token, params, { fetch: fetcher });
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Delete webhook configuration
|
|
192
|
+
*/
|
|
193
|
+
export async function deleteWebhook(
|
|
194
|
+
token: string,
|
|
195
|
+
fetcher?: ZaloFetch,
|
|
196
|
+
): Promise<ZaloApiResponse<boolean>> {
|
|
197
|
+
return callZaloApi<boolean>("deleteWebhook", token, undefined, { fetch: fetcher });
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Get current webhook info
|
|
202
|
+
*/
|
|
203
|
+
export async function getWebhookInfo(
|
|
204
|
+
token: string,
|
|
205
|
+
fetcher?: ZaloFetch,
|
|
206
|
+
): Promise<ZaloApiResponse<{ url?: string; has_custom_certificate?: boolean }>> {
|
|
207
|
+
return callZaloApi("getWebhookInfo", token, undefined, { fetch: fetcher });
|
|
208
|
+
}
|