zapry-openclaw-plugin 0.0.1
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/README.md +127 -0
- package/index.ts +345 -0
- package/openclaw.plugin.json +10 -0
- package/package.json +22 -0
- package/skills/zapry/SKILL.md +337 -0
- package/src/actions.ts +2241 -0
- package/src/api-client.ts +357 -0
- package/src/channel.ts +268 -0
- package/src/config.ts +68 -0
- package/src/inbound.ts +3494 -0
- package/src/monitor.ts +144 -0
- package/src/profile-sync.ts +195 -0
- package/src/runtime.ts +66 -0
- package/src/send.ts +257 -0
- package/src/types.ts +175 -0
package/README.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# @zapry/openclaw-plugin
|
|
2
|
+
|
|
3
|
+
OpenClaw channel plugin for [Zapry](https://zapry.io) — a social platform with messaging, groups, feed, clubs, and wallet.
|
|
4
|
+
|
|
5
|
+
Install this plugin to let your OpenClaw agent interact with Zapry through `channel: "zapry"`.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Messaging** — Send text/media, delete messages, answer callback queries
|
|
10
|
+
- **Receive/Webhook** — Poll updates, manage webhooks, inspect inbound endpoint
|
|
11
|
+
- **Skills** — Manage SOUL, skills, and derived profile
|
|
12
|
+
- **Directory & Groups** — Query chats/groups/members and perform moderation actions
|
|
13
|
+
- **Agent Self** — Manage name/description/wallet/privacy and query contacts/friend requests
|
|
14
|
+
- **Feed & Club** — Query/publish/engage posts and manage clubs
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
openclaw plugins install @zapry/openclaw-plugin
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Development Notes
|
|
23
|
+
|
|
24
|
+
- `node_modules/` is ignored by Git and should not be committed.
|
|
25
|
+
- If `node_modules` was ever tracked in your local branch, untrack it with:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
git rm -r --cached node_modules
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Configure
|
|
32
|
+
|
|
33
|
+
Get a bot token from [Zapry BotMother](https://botmother-dev.mimo.immo), then add it to your config:
|
|
34
|
+
|
|
35
|
+
```jsonc
|
|
36
|
+
// ~/.openclaw/openclaw.json
|
|
37
|
+
{
|
|
38
|
+
"channels": {
|
|
39
|
+
"zapry": {
|
|
40
|
+
"botToken": "YOUR_BOT_TOKEN"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Multi-bot setup:
|
|
47
|
+
|
|
48
|
+
```jsonc
|
|
49
|
+
{
|
|
50
|
+
"channels": {
|
|
51
|
+
"zapry": {
|
|
52
|
+
"accounts": {
|
|
53
|
+
"market-bot": { "botToken": "TOKEN_A" },
|
|
54
|
+
"support-bot": { "botToken": "TOKEN_B" }
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Restart the gateway after configuring:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
openclaw gateway restart
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Usage
|
|
68
|
+
|
|
69
|
+
The plugin registers a `zapry` skill. Your agent will automatically use it when Zapry-related intents are detected.
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
You: Post "Good morning!" to my Zapry feed
|
|
73
|
+
Agent: → zapry_post { content: "Good morning!" }
|
|
74
|
+
Done, post published.
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Available Actions
|
|
78
|
+
|
|
79
|
+
- Messaging: `send-message`, `send-photo`, `send-video`, `send-document`, `send-audio`, `send-voice`, `send-animation`, `generate-audio`, `delete-message`, `answer-callback-query`
|
|
80
|
+
- Receive/Webhook: `get-updates`, `get-file`, `set-webhook`, `get-webhook-info`, `delete-webhook`, `webhooks-token`
|
|
81
|
+
- Skills: `set-my-soul`, `get-my-soul`, `set-my-skills`, `get-my-skills`, `get-my-profile`
|
|
82
|
+
- Group Query & Moderation: `get-my-groups`, `get-my-chats`, `get-chat-member`, `get-chat-members`, `get-chat-member-count`, `get-chat-administrators`, `mute-chat-member`, `kick-chat-member`, `set-chat-title`, `set-chat-description`
|
|
83
|
+
- Agent Self Management: `get-me`, `get-user-profile-photos`, `set-my-wallet-address`, `set-my-friend-verify`, `get-my-contacts`, `get-my-friend-requests`, `set-my-name`, `set-my-description`
|
|
84
|
+
- Feed: `get-trending-posts`, `get-latest-posts`, `get-my-posts`, `search-posts`, `create-post`, `delete-post`, `comment-post`, `like-post`, `share-post`
|
|
85
|
+
- Club: `get-my-clubs`, `create-club`, `update-club`
|
|
86
|
+
|
|
87
|
+
### Parameter Conventions (Important)
|
|
88
|
+
|
|
89
|
+
This plugin follows the API reference 1:1. Prefer documented parameter names in `zapry_action` / `zapry_post` tool calls:
|
|
90
|
+
|
|
91
|
+
- IDs: `chat_id`, `user_id`, `message_id`, `callback_query_id`, `file_id`, `dynamic_id`, `club_id`
|
|
92
|
+
- Content: `text`, `photo`, `video`, `document`, `audio`, `voice`, `animation`, `content`
|
|
93
|
+
- Paging: `page`, `page_size`
|
|
94
|
+
- Bot/Privacy: `name`, `description`, `wallet_address`, `need_verify`, `pending_only`
|
|
95
|
+
- Skills: `soulMd`, `skills`, `version`, `source`, `agentKey`
|
|
96
|
+
|
|
97
|
+
Common camelCase aliases are still accepted (`chatId`, `userId`, `messageId`, `dynamicId`, `clubId`, `pageSize`, `languageCode`), but snake_case is canonical.
|
|
98
|
+
|
|
99
|
+
Group moderation note:
|
|
100
|
+
|
|
101
|
+
- `mute-chat-member` only supports `mute` boolean (`true` mute / `false` unmute).
|
|
102
|
+
- Duration fields like `until_date` / `duration` are not supported by current API contract.
|
|
103
|
+
|
|
104
|
+
Media source constraint (important):
|
|
105
|
+
|
|
106
|
+
- For media send actions, use only `data:` URI or `/_temp/media/...` (or absolute URL ending with `/_temp/media/...`).
|
|
107
|
+
- `create-post` images follow the same rule; local file paths are auto-converted to `data:` URI by this plugin.
|
|
108
|
+
- Raw external file/image URLs are rejected by Zapry OpenAPI and will return `400`.
|
|
109
|
+
|
|
110
|
+
Audio generation helper:
|
|
111
|
+
|
|
112
|
+
- `generate-audio` is a plugin-local helper action (not OpenAPI 1:1) that does **TTS or procedural rendering** and then sends via `sendAudio`.
|
|
113
|
+
- Typical params: `chat_id` (required), optional `prompt`, `audio_mode` (`auto`/`tts`/`render`), `audio_format` (`mp3`/`wav`), `duration_seconds`, `tts_voice`, `fallback_text`.
|
|
114
|
+
- On generation/send failure, the plugin will best-effort send fallback text to the chat.
|
|
115
|
+
|
|
116
|
+
## Configuration Reference
|
|
117
|
+
|
|
118
|
+
| Field | Type | Default | Description |
|
|
119
|
+
|-------|------|---------|-------------|
|
|
120
|
+
| `botToken` | string | — | Bot token from BotMother (required) |
|
|
121
|
+
| `apiBaseUrl` | string | `https://openapi-dev.mimo.immo` | Zapry API server URL |
|
|
122
|
+
| `mode` | `"polling"` \| `"webhook"` | `"polling"` | Inbound runtime now uses polling as the single processing path; `webhook` is accepted but falls back to polling |
|
|
123
|
+
| `webhookUrl` | string | — | Legacy webhook callback URL, retained only for backward-compatible config parsing |
|
|
124
|
+
|
|
125
|
+
## License
|
|
126
|
+
|
|
127
|
+
MIT
|
package/index.ts
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
if (typeof globalThis.DOMMatrix === "undefined") {
|
|
2
|
+
(globalThis as any).DOMMatrix = class DOMMatrix {
|
|
3
|
+
a: number; b: number; c: number; d: number; e: number; f: number;
|
|
4
|
+
m11: number; m12: number; m21: number; m22: number; m41: number; m42: number;
|
|
5
|
+
is2D = true; isIdentity = false;
|
|
6
|
+
constructor(init?: number[] | string) {
|
|
7
|
+
const v = Array.isArray(init) ? init : [];
|
|
8
|
+
this.a = this.m11 = v[0] ?? 1; this.b = this.m12 = v[1] ?? 0;
|
|
9
|
+
this.c = this.m21 = v[2] ?? 0; this.d = this.m22 = v[3] ?? 1;
|
|
10
|
+
this.e = this.m41 = v[4] ?? 0; this.f = this.m42 = v[5] ?? 0;
|
|
11
|
+
}
|
|
12
|
+
inverse() { return new DOMMatrix(); }
|
|
13
|
+
multiply() { return new DOMMatrix(); }
|
|
14
|
+
scale() { return new DOMMatrix(); }
|
|
15
|
+
translate() { return new DOMMatrix(); }
|
|
16
|
+
transformPoint(p?: any) { return { x: p?.x ?? 0, y: p?.y ?? 0, z: 0, w: 1 }; }
|
|
17
|
+
static fromMatrix() { return new DOMMatrix(); }
|
|
18
|
+
static fromFloat32Array(a: Float32Array) { return new DOMMatrix(Array.from(a)); }
|
|
19
|
+
static fromFloat64Array(a: Float64Array) { return new DOMMatrix(Array.from(a)); }
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const { createRequire } = require("node:module");
|
|
25
|
+
const { pathToFileURL } = require("node:url");
|
|
26
|
+
const _r = createRequire(require("node:path").join(process.cwd(), "package.json"));
|
|
27
|
+
const _pdfjsAbs = _r.resolve("pdfjs-dist/legacy/build/pdf.mjs");
|
|
28
|
+
const _workerAbs = _r.resolve("pdfjs-dist/legacy/build/pdf.worker.mjs");
|
|
29
|
+
import(pathToFileURL(_pdfjsAbs).href).then((pdfjs: any) => {
|
|
30
|
+
if (pdfjs?.GlobalWorkerOptions && !pdfjs.GlobalWorkerOptions.workerSrc) {
|
|
31
|
+
pdfjs.GlobalWorkerOptions.workerSrc = _workerAbs;
|
|
32
|
+
}
|
|
33
|
+
}).catch(() => {});
|
|
34
|
+
} catch {}
|
|
35
|
+
|
|
36
|
+
import { zapryPlugin } from "./src/channel.js";
|
|
37
|
+
import {
|
|
38
|
+
buildZaprySkillRequestHeaders,
|
|
39
|
+
getZaprySkillInvocationContext,
|
|
40
|
+
resolveZaprySkillRequestHeaders,
|
|
41
|
+
setZapryRuntime,
|
|
42
|
+
} from "./src/runtime.js";
|
|
43
|
+
import { resolveZapryAccount } from "./src/config.js";
|
|
44
|
+
import { handleZapryAction } from "./src/actions.js";
|
|
45
|
+
|
|
46
|
+
const ZAPRY_ACTION_TOOL_ACTIONS = [
|
|
47
|
+
"send", "send-message",
|
|
48
|
+
"send-photo", "send-video", "send-document", "send-audio", "send-voice", "send-animation",
|
|
49
|
+
"generate-audio",
|
|
50
|
+
"delete-message", "answer-callback-query",
|
|
51
|
+
"get-file", "get-my-profile", "get-me",
|
|
52
|
+
"get-my-groups", "get-my-chats",
|
|
53
|
+
"get-chat-member", "get-chat-members", "get-chat-member-count", "get-chat-administrators",
|
|
54
|
+
"mute-chat-member", "kick-chat-member", "set-chat-title", "set-chat-description",
|
|
55
|
+
"get-user-profile-photos", "set-my-wallet-address", "set-my-friend-verify",
|
|
56
|
+
"get-my-contacts", "get-my-friend-requests",
|
|
57
|
+
"accept-friend-request", "reject-friend-request", "add-friend", "delete-friend",
|
|
58
|
+
"set-my-soul", "get-my-soul", "set-my-skills", "get-my-skills",
|
|
59
|
+
"set-my-name", "set-my-description",
|
|
60
|
+
"get-trending-posts", "get-latest-posts", "get-my-posts", "search-posts",
|
|
61
|
+
"delete-post", "comment-post", "like-post", "share-post",
|
|
62
|
+
"get-updates", "set-webhook", "get-webhook-info", "delete-webhook", "webhooks-token",
|
|
63
|
+
"get-chat-history",
|
|
64
|
+
] as const;
|
|
65
|
+
|
|
66
|
+
async function resolveRuntimeConfig(api: any): Promise<any> {
|
|
67
|
+
const runtimeConfig = api?.runtime?.config;
|
|
68
|
+
const loadConfig = runtimeConfig?.loadConfig;
|
|
69
|
+
if (typeof loadConfig === "function") {
|
|
70
|
+
try {
|
|
71
|
+
return await Promise.resolve(loadConfig());
|
|
72
|
+
} catch {
|
|
73
|
+
// fall through
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return runtimeConfig ?? {};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function sameUserIdentity(left: string, right: string): boolean {
|
|
80
|
+
const normalizedLeft = left.trim();
|
|
81
|
+
const normalizedRight = right.trim();
|
|
82
|
+
if (!normalizedLeft || !normalizedRight) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
if (normalizedLeft === normalizedRight) {
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const leftNum = Number(normalizedLeft);
|
|
90
|
+
const rightNum = Number(normalizedRight);
|
|
91
|
+
return Number.isFinite(leftNum) && Number.isFinite(rightNum) && leftNum === rightNum;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function resolveOwnerIdFromBotToken(botToken: string): string {
|
|
95
|
+
const trimmed = String(botToken ?? "").trim();
|
|
96
|
+
const separatorIdx = trimmed.indexOf(":");
|
|
97
|
+
if (separatorIdx <= 0) {
|
|
98
|
+
return "";
|
|
99
|
+
}
|
|
100
|
+
return trimmed.slice(0, separatorIdx).trim();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function resolveToolAccount(toolCtx: any, cfg: any, requestedAccountId?: string) {
|
|
104
|
+
return resolveZapryAccount(cfg, requestedAccountId ?? toolCtx?.agentAccountId);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function resolveToolSenderId(toolCtx: any): string {
|
|
108
|
+
return String(toolCtx?.requesterSenderId ?? "").trim();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function resolveToolSenderIsOwner(toolCtx: any, account: { botToken: string }): boolean {
|
|
112
|
+
if (toolCtx?.senderIsOwner === true) {
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
if (toolCtx?.senderIsOwner === false) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
const senderId = resolveToolSenderId(toolCtx);
|
|
119
|
+
const ownerId = resolveOwnerIdFromBotToken(account.botToken);
|
|
120
|
+
return sameUserIdentity(senderId, ownerId);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function shouldExposeZapryOwnerTools(toolCtx: any, account: { botToken: string }): boolean {
|
|
124
|
+
if (toolCtx?.messageChannel !== "zapry") {
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
const senderId = resolveToolSenderId(toolCtx);
|
|
128
|
+
if (!senderId) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
return resolveToolSenderIsOwner(toolCtx, account);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function resolveToolRequestHeaders(toolCtx: any): Record<string, string> {
|
|
135
|
+
const senderId = resolveToolSenderId(toolCtx);
|
|
136
|
+
if (senderId) {
|
|
137
|
+
const invocationCtx = getZaprySkillInvocationContext();
|
|
138
|
+
return buildZaprySkillRequestHeaders({
|
|
139
|
+
senderId,
|
|
140
|
+
messageSid: invocationCtx?.messageSid,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
return resolveZaprySkillRequestHeaders();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function ownerDeniedToolResult(): string {
|
|
147
|
+
return JSON.stringify({
|
|
148
|
+
ok: false,
|
|
149
|
+
error: "只能是主人才可以调用",
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const plugin = {
|
|
154
|
+
id: "zapry",
|
|
155
|
+
name: "Zapry",
|
|
156
|
+
description: "Zapry social platform channel plugin — messaging, groups, feed, clubs, and bot self-management",
|
|
157
|
+
configSchema: {
|
|
158
|
+
type: "object" as const,
|
|
159
|
+
additionalProperties: false,
|
|
160
|
+
properties: {},
|
|
161
|
+
},
|
|
162
|
+
register(api: any) {
|
|
163
|
+
setZapryRuntime(api.runtime);
|
|
164
|
+
api.registerChannel({ plugin: zapryPlugin });
|
|
165
|
+
|
|
166
|
+
api.registerTool((toolCtx: any) => {
|
|
167
|
+
const toolCfg = toolCtx?.config ?? api?.runtime?.config ?? {};
|
|
168
|
+
const account = resolveToolAccount(toolCtx, toolCfg);
|
|
169
|
+
if (!shouldExposeZapryOwnerTools(toolCtx, account)) {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
name: "zapry_post",
|
|
174
|
+
label: "Zapry Post to Feed",
|
|
175
|
+
description:
|
|
176
|
+
"Post to Zapry public feed (广场). This is the ONLY way to create a feed post. " +
|
|
177
|
+
"Pass content and optionally images. No target or routing needed.",
|
|
178
|
+
parameters: {
|
|
179
|
+
type: "object" as const,
|
|
180
|
+
properties: {
|
|
181
|
+
content: {
|
|
182
|
+
type: "string" as const,
|
|
183
|
+
description: "Post text content (required)",
|
|
184
|
+
},
|
|
185
|
+
images: {
|
|
186
|
+
type: "array" as const,
|
|
187
|
+
items: { type: "string" as const },
|
|
188
|
+
description:
|
|
189
|
+
"Array of image sources: local file paths, data: URIs, HTTP(S) URLs, or Zapry file IDs (mf_*)",
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
required: ["content"],
|
|
193
|
+
},
|
|
194
|
+
execute: async (_toolCallId: string, args: Record<string, any>) => {
|
|
195
|
+
try {
|
|
196
|
+
const cfg = await resolveRuntimeConfig(api);
|
|
197
|
+
const account = resolveToolAccount(toolCtx, cfg);
|
|
198
|
+
if (!shouldExposeZapryOwnerTools(toolCtx, account)) {
|
|
199
|
+
return ownerDeniedToolResult();
|
|
200
|
+
}
|
|
201
|
+
const result = await handleZapryAction({
|
|
202
|
+
action: "create-post",
|
|
203
|
+
channel: "zapry",
|
|
204
|
+
account,
|
|
205
|
+
params: { content: args.content, images: args.images },
|
|
206
|
+
requestHeaders: resolveToolRequestHeaders(toolCtx),
|
|
207
|
+
});
|
|
208
|
+
return JSON.stringify(result, null, 2);
|
|
209
|
+
} catch (err) {
|
|
210
|
+
return JSON.stringify({
|
|
211
|
+
ok: false,
|
|
212
|
+
error: err instanceof Error ? err.message : String(err),
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
api.registerTool((toolCtx: any) => {
|
|
220
|
+
const toolCfg = toolCtx?.config ?? api?.runtime?.config ?? {};
|
|
221
|
+
const account = resolveToolAccount(toolCtx, toolCfg);
|
|
222
|
+
if (!shouldExposeZapryOwnerTools(toolCtx, account)) {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
return {
|
|
226
|
+
name: "zapry_action",
|
|
227
|
+
label: "Zapry Platform Action",
|
|
228
|
+
description:
|
|
229
|
+
"Execute a Zapry platform action. Use this for: " +
|
|
230
|
+
"sending media (send-photo, send-video, send-audio, send-document, send-voice, send-animation) to any chat including groups. " +
|
|
231
|
+
"IMPORTANT: For send-photo, if user asks for an image without providing one, use 'prompt' parameter (e.g. action='send-photo', prompt='bitcoin logo') — image is auto-generated, NO photo/URL needed. " +
|
|
232
|
+
"profile queries (get-my-profile, get-me), friend operations " +
|
|
233
|
+
"(get-my-friend-requests, accept-friend-request, add-friend, etc.), " +
|
|
234
|
+
"group management (get-chat-members, mute-chat-member, kick-chat-member, etc.), " +
|
|
235
|
+
"feed reading (get-trending-posts, get-latest-posts, search-posts, etc.), " +
|
|
236
|
+
"feed interactions (delete-post, comment-post, like-post, share-post), " +
|
|
237
|
+
"bot settings (set-my-soul, set-my-skills, set-my-name, etc.), " +
|
|
238
|
+
"chat history (get-chat-history), " +
|
|
239
|
+
"and webhook/file operations (get-file, set-webhook, get-updates, etc.). " +
|
|
240
|
+
"Pass the action name and action-specific parameters as top-level fields.",
|
|
241
|
+
parameters: {
|
|
242
|
+
type: "object" as const,
|
|
243
|
+
properties: {
|
|
244
|
+
action: {
|
|
245
|
+
type: "string" as const,
|
|
246
|
+
description: "The Zapry action to execute",
|
|
247
|
+
enum: [...ZAPRY_ACTION_TOOL_ACTIONS],
|
|
248
|
+
},
|
|
249
|
+
chat_id: {
|
|
250
|
+
type: "string" as const,
|
|
251
|
+
description: "Chat/group ID (for group management actions like get-chat-members, mute-chat-member, etc.)",
|
|
252
|
+
},
|
|
253
|
+
user_id: {
|
|
254
|
+
type: "string" as const,
|
|
255
|
+
description: "User ID (for friend actions, chat member actions, etc.)",
|
|
256
|
+
},
|
|
257
|
+
file_id: {
|
|
258
|
+
type: "string" as const,
|
|
259
|
+
description: "File ID (for get-file)",
|
|
260
|
+
},
|
|
261
|
+
keyword: {
|
|
262
|
+
type: "string" as const,
|
|
263
|
+
description: "Search keyword (for search-posts)",
|
|
264
|
+
},
|
|
265
|
+
dynamic_id: {
|
|
266
|
+
type: "string" as const,
|
|
267
|
+
description: "Post/dynamic ID (for delete-post, comment-post, like-post, share-post)",
|
|
268
|
+
},
|
|
269
|
+
photo: {
|
|
270
|
+
type: "string" as const,
|
|
271
|
+
description: "Photo source: external URL (auto-downloaded), data URI, local path, or /_temp/media URL (for send-photo). If omitted but 'prompt' is provided, image will be auto-generated.",
|
|
272
|
+
},
|
|
273
|
+
prompt: {
|
|
274
|
+
type: "string" as const,
|
|
275
|
+
description: "PREFERRED for send-photo when user asks for an image: describe what to generate (e.g. 'bitcoin logo', 'cute cat'). Image is auto-generated and sent — no photo/URL needed.",
|
|
276
|
+
},
|
|
277
|
+
video: {
|
|
278
|
+
type: "string" as const,
|
|
279
|
+
description: "Video source (for send-video)",
|
|
280
|
+
},
|
|
281
|
+
document: {
|
|
282
|
+
type: "string" as const,
|
|
283
|
+
description: "Document source (for send-document)",
|
|
284
|
+
},
|
|
285
|
+
audio: {
|
|
286
|
+
type: "string" as const,
|
|
287
|
+
description: "Audio source (for send-audio)",
|
|
288
|
+
},
|
|
289
|
+
voice: {
|
|
290
|
+
type: "string" as const,
|
|
291
|
+
description: "Voice source (for send-voice)",
|
|
292
|
+
},
|
|
293
|
+
animation: {
|
|
294
|
+
type: "string" as const,
|
|
295
|
+
description: "Animation/GIF source (for send-animation)",
|
|
296
|
+
},
|
|
297
|
+
content: {
|
|
298
|
+
type: "string" as const,
|
|
299
|
+
description: "Text content (for comment-post)",
|
|
300
|
+
},
|
|
301
|
+
limit: {
|
|
302
|
+
type: "number" as const,
|
|
303
|
+
description: "Limit for results (for get-chat-history, default 50, max 50)",
|
|
304
|
+
},
|
|
305
|
+
page: {
|
|
306
|
+
type: "number" as const,
|
|
307
|
+
description: "Page number for paginated results",
|
|
308
|
+
},
|
|
309
|
+
page_size: {
|
|
310
|
+
type: "number" as const,
|
|
311
|
+
description: "Page size for paginated results",
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
required: ["action"],
|
|
315
|
+
additionalProperties: true,
|
|
316
|
+
},
|
|
317
|
+
execute: async (_toolCallId: string, args: Record<string, any>) => {
|
|
318
|
+
try {
|
|
319
|
+
const { action, channel: _ch, accountId: reqAccountId, ...params } = args ?? {};
|
|
320
|
+
const cfg = await resolveRuntimeConfig(api);
|
|
321
|
+
const account = resolveToolAccount(toolCtx, cfg, reqAccountId);
|
|
322
|
+
if (!shouldExposeZapryOwnerTools(toolCtx, account)) {
|
|
323
|
+
return ownerDeniedToolResult();
|
|
324
|
+
}
|
|
325
|
+
const result = await handleZapryAction({
|
|
326
|
+
action,
|
|
327
|
+
channel: "zapry",
|
|
328
|
+
account,
|
|
329
|
+
params,
|
|
330
|
+
requestHeaders: resolveToolRequestHeaders(toolCtx),
|
|
331
|
+
});
|
|
332
|
+
return JSON.stringify(result, null, 2);
|
|
333
|
+
} catch (err) {
|
|
334
|
+
return JSON.stringify({
|
|
335
|
+
ok: false,
|
|
336
|
+
error: err instanceof Error ? err.message : String(err),
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
};
|
|
341
|
+
});
|
|
342
|
+
},
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
export default plugin;
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "zapry-openclaw-plugin",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "OpenClaw Zapry channel plugin — messaging, groups, feed, clubs, and bot self-management",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"openclaw": {
|
|
8
|
+
"extensions": [
|
|
9
|
+
"./index.ts"
|
|
10
|
+
]
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"index.ts",
|
|
14
|
+
"src/",
|
|
15
|
+
"skills/",
|
|
16
|
+
"openclaw.plugin.json"
|
|
17
|
+
],
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@types/node": "^25.5.0",
|
|
20
|
+
"typescript": "^5.9.3"
|
|
21
|
+
}
|
|
22
|
+
}
|