openclaw-quiubo 2.6.35 → 2.6.37
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/POSTS.md +122 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +218 -4
- package/dist/index.js.map +4 -4
- package/dist/src/api.d.ts +43 -0
- package/dist/src/api.d.ts.map +1 -1
- package/dist/src/channel.d.ts.map +1 -1
- package/dist/src/create-post-tool.d.ts +49 -0
- package/dist/src/create-post-tool.d.ts.map +1 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +3 -2
package/POSTS.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Agent Posts
|
|
2
|
+
|
|
3
|
+
Let agents create posts in Quiubo groups — image + optional caption, visible in the group's post feed, auto-expires after 24 hours.
|
|
4
|
+
|
|
5
|
+
## Why This Exists
|
|
6
|
+
|
|
7
|
+
Without this tool, an agent asked to "create a post" will generate an image and send it as a chat message via `sendMedia`. The agent has no way to distinguish "send a message with an image" from "create a post" — both route through the same `deliver-callback` pipeline.
|
|
8
|
+
|
|
9
|
+
The `quiubo_create_post` tool gives agents an explicit action for posts. The agent calls it by name, the image goes through the presigned upload flow, and the result appears in the post feed — not the chat.
|
|
10
|
+
|
|
11
|
+
## Requirements
|
|
12
|
+
|
|
13
|
+
- A configured Quiubo account with `apiKey` and `botIdentityId` in `channels.quiubo.accounts`
|
|
14
|
+
- The agent must have an image file on disk (`.jpg`, `.jpeg`, `.png`, or `.webp`, max 5MB)
|
|
15
|
+
|
|
16
|
+
If the Quiubo channel isn't configured, the tool silently doesn't register — agents won't see it.
|
|
17
|
+
|
|
18
|
+
## How It Works
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
Agent decides to create a post
|
|
22
|
+
│
|
|
23
|
+
▼
|
|
24
|
+
quiubo_create_post(image_path, caption?, group_id?)
|
|
25
|
+
│
|
|
26
|
+
├─ Reads image from disk
|
|
27
|
+
├─ Validates format + size
|
|
28
|
+
├─ POST /v1/sdk/posts/presign → gets upload URL + post record
|
|
29
|
+
├─ PUT to presigned S3 URL → uploads image bytes
|
|
30
|
+
│
|
|
31
|
+
▼
|
|
32
|
+
Post appears in group feed (not chat)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The tool creates its own API client from the account config — no shared state with the channel gateway.
|
|
36
|
+
|
|
37
|
+
## Tool Parameters
|
|
38
|
+
|
|
39
|
+
| Parameter | Type | Required | Description |
|
|
40
|
+
|-----------|------|----------|-------------|
|
|
41
|
+
| `image_path` | string | Yes | Absolute path to image file on disk |
|
|
42
|
+
| `caption` | string | No | Text caption (max 500 chars) |
|
|
43
|
+
| `group_id` | string | No | Target group UUID. Defaults to the current conversation group. |
|
|
44
|
+
|
|
45
|
+
## Tool Response
|
|
46
|
+
|
|
47
|
+
**Success:**
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"ok": true,
|
|
51
|
+
"postId": "post-uuid",
|
|
52
|
+
"imageUrl": "https://...",
|
|
53
|
+
"groupId": "group-uuid"
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Error:**
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"ok": false,
|
|
61
|
+
"error": "Unsupported image format: .gif. Use .jpg, .png, or .webp"
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Teaching Your Agent
|
|
66
|
+
|
|
67
|
+
Add something like this to the agent's identity/instructions file (`SOUL.md`, `IDENTITY.md`, or system prompt):
|
|
68
|
+
|
|
69
|
+
```markdown
|
|
70
|
+
## Posts
|
|
71
|
+
|
|
72
|
+
You have a `quiubo_create_post` tool. Use it when asked to create a post, share a photo to the feed, or post an update.
|
|
73
|
+
|
|
74
|
+
Posts are different from messages:
|
|
75
|
+
- Posts appear in the group's **post feed**, not the chat
|
|
76
|
+
- Posts require an **image** (messages don't)
|
|
77
|
+
- Posts auto-expire after **24 hours**
|
|
78
|
+
|
|
79
|
+
Workflow:
|
|
80
|
+
1. Generate or locate the image file
|
|
81
|
+
2. Call `quiubo_create_post` with `image_path` and optional `caption`
|
|
82
|
+
3. The post appears in the group feed automatically
|
|
83
|
+
|
|
84
|
+
Do NOT use sendMessage/sendMedia for posts — that sends a chat message, not a post.
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Group Targeting
|
|
88
|
+
|
|
89
|
+
The tool resolves the target group in this order:
|
|
90
|
+
|
|
91
|
+
1. **`group_id` parameter** — if the agent passes it explicitly
|
|
92
|
+
2. **Session key** — extracted from the OpenClaw session key (`agent:<name>:quiubo:<uuid>`)
|
|
93
|
+
|
|
94
|
+
For most conversational use ("create a post in this group"), the session key auto-resolves. The `group_id` parameter is for cross-posting to a different group.
|
|
95
|
+
|
|
96
|
+
## Example Agent Interaction
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
User: Create a post with today's weather forecast
|
|
100
|
+
|
|
101
|
+
Agent: (generates weather image → saves to /tmp/weather-2026-02-28.png)
|
|
102
|
+
Agent: (calls quiubo_create_post)
|
|
103
|
+
image_path: /tmp/weather-2026-02-28.png
|
|
104
|
+
caption: "☀️ Today's forecast: Clear skies, 22°C"
|
|
105
|
+
|
|
106
|
+
Tool result: { ok: true, postId: "abc-123", groupId: "def-456" }
|
|
107
|
+
|
|
108
|
+
Agent: Done — the weather forecast is now in the group's post feed.
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Troubleshooting
|
|
112
|
+
|
|
113
|
+
| Symptom | Fix |
|
|
114
|
+
|---------|-----|
|
|
115
|
+
| Agent sends image as chat message instead of post | Agent doesn't know about the tool. Add instructions to its identity file (see [Teaching Your Agent](#teaching-your-agent)). |
|
|
116
|
+
| Tool doesn't appear for the agent | Check that `channels.quiubo.accounts.default` has both `apiKey` and `botIdentityId`. The factory returns null without them. |
|
|
117
|
+
| "No group_id provided and could not resolve from session context" | The session key doesn't contain a group UUID. Pass `group_id` explicitly. |
|
|
118
|
+
| "No botIdentityId configured" | Add `botIdentityId` to the account config in `channels.quiubo.accounts`. |
|
|
119
|
+
| "Unsupported image format" | Only `.jpg`, `.jpeg`, `.png`, `.webp` are supported. Convert the image first. |
|
|
120
|
+
| "Image exceeds 5MB limit" | Compress or resize the image before calling the tool. |
|
|
121
|
+
| "Cannot read image" | The `image_path` doesn't exist or isn't readable. Check the path is absolute and the file was saved. |
|
|
122
|
+
| Post created but not visible | Check the group UUID is correct. Posts are scoped to a group and expire after 24 hours. |
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,KAAK,aAAa,EAAE,KAAK,sBAAsB,EAAE,KAAK,cAAc,EAAE,KAAK,cAAc,EAAE,KAAK,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACvK,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACtF,OAAO,EAAE,kBAAkB,EAAE,KAAK,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAC1F,YAAY,EACV,mBAAmB,EACnB,WAAW,EACX,aAAa,EACb,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,4BAA4B,GAC7B,MAAM,gBAAgB,CAAC;AAGxB,UAAU,iBAAiB;IACzB,OAAO,EAAE,OAAO,CAAC;IAEjB,eAAe,EAAE,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,GAAG,CAAA;KAAE,KAAK,IAAI,CAAC;IAEjD,YAAY,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAC7D;AAED,QAAA,MAAM,MAAM;;;;kBAII,iBAAiB;CAKhC,CAAC;AAEF,eAAe,MAAM,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -9234,6 +9234,28 @@ var QuiuboApiClient = class {
|
|
|
9234
9234
|
});
|
|
9235
9235
|
}
|
|
9236
9236
|
// ========================================================================
|
|
9237
|
+
// Posts
|
|
9238
|
+
// ========================================================================
|
|
9239
|
+
/**
|
|
9240
|
+
* Create a post as an SDK-owned identity. Returns presigned upload URL and post metadata.
|
|
9241
|
+
*/
|
|
9242
|
+
async createPost(opts) {
|
|
9243
|
+
return this.request("POST", "/posts/presign", opts);
|
|
9244
|
+
}
|
|
9245
|
+
/**
|
|
9246
|
+
* List posts by SDK identities (optionally filter by identity)
|
|
9247
|
+
*/
|
|
9248
|
+
async listPosts(identityId) {
|
|
9249
|
+
const query = identityId ? `?identity_id=${identityId}` : "";
|
|
9250
|
+
return this.request("GET", `/posts${query}`);
|
|
9251
|
+
}
|
|
9252
|
+
/**
|
|
9253
|
+
* Delete a post authored by an SDK identity
|
|
9254
|
+
*/
|
|
9255
|
+
async deletePost(postId) {
|
|
9256
|
+
return this.request("DELETE", `/posts/${postId}`);
|
|
9257
|
+
}
|
|
9258
|
+
// ========================================================================
|
|
9237
9259
|
// Attachment Upload
|
|
9238
9260
|
// ========================================================================
|
|
9239
9261
|
/**
|
|
@@ -13440,6 +13462,7 @@ var quiuboPlugin = {
|
|
|
13440
13462
|
capabilities: {
|
|
13441
13463
|
chatTypes: ["group"],
|
|
13442
13464
|
supportsMedia: true,
|
|
13465
|
+
supportsPosts: true,
|
|
13443
13466
|
supportsReactions: false,
|
|
13444
13467
|
supportsThreads: false
|
|
13445
13468
|
},
|
|
@@ -13821,6 +13844,86 @@ var quiuboPlugin = {
|
|
|
13821
13844
|
log?.error?.(`[${accountId}] [outbound:sendMedia] SEND FAILED [network] group=${groupId} \u2014 ${msg}`);
|
|
13822
13845
|
return { ok: false, error: msg };
|
|
13823
13846
|
}
|
|
13847
|
+
},
|
|
13848
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
13849
|
+
async createPost(ctx) {
|
|
13850
|
+
const text = ctx.text || ctx.caption || "";
|
|
13851
|
+
const urls = [];
|
|
13852
|
+
if (ctx.mediaUrl) urls.push(ctx.mediaUrl);
|
|
13853
|
+
if (Array.isArray(ctx.mediaUrls)) urls.push(...ctx.mediaUrls);
|
|
13854
|
+
const accountId = ctx.accountId ?? DEFAULT_ACCOUNT_ID;
|
|
13855
|
+
const log = loggers.get(accountId);
|
|
13856
|
+
let client = clients.get(accountId);
|
|
13857
|
+
let account = accounts.get(accountId);
|
|
13858
|
+
if (!client || !account) {
|
|
13859
|
+
const acct = getChannelConfig(ctx.cfg)?.accounts?.[accountId];
|
|
13860
|
+
if (!acct?.apiKey) {
|
|
13861
|
+
log?.warn?.(`[${accountId}] [outbound:createPost] no account config found`);
|
|
13862
|
+
return { ok: false, error: "No account config found" };
|
|
13863
|
+
}
|
|
13864
|
+
const apiUrl = acct.apiUrl ?? DEFAULT_API_URL;
|
|
13865
|
+
client = new QuiuboApiClient(apiUrl, acct.apiKey);
|
|
13866
|
+
account = { ...acct, accountId };
|
|
13867
|
+
clients.set(accountId, client);
|
|
13868
|
+
accounts.set(accountId, account);
|
|
13869
|
+
}
|
|
13870
|
+
let groupId = resolveOutboundGroupId(ctx);
|
|
13871
|
+
if (!groupId) {
|
|
13872
|
+
groupId = await resolveAnnounceGroupId(accountId, log);
|
|
13873
|
+
}
|
|
13874
|
+
if (!groupId) {
|
|
13875
|
+
log?.error?.(`[${accountId}] [outbound:createPost] no groupId \u2014 ctx keys=${Object.keys(ctx).join(",")}`);
|
|
13876
|
+
return { ok: false, error: "No groupId in outbound context" };
|
|
13877
|
+
}
|
|
13878
|
+
const senderId = account.botIdentityId;
|
|
13879
|
+
if (!senderId) {
|
|
13880
|
+
return { ok: false, error: "No botIdentityId configured" };
|
|
13881
|
+
}
|
|
13882
|
+
let imageBuffer;
|
|
13883
|
+
let imageContentType;
|
|
13884
|
+
for (const url of urls) {
|
|
13885
|
+
const filename = basename(url);
|
|
13886
|
+
const ext = filename.substring(filename.lastIndexOf(".")).toLowerCase();
|
|
13887
|
+
if (ext in IMAGE_MIME_TYPES) {
|
|
13888
|
+
try {
|
|
13889
|
+
imageBuffer = await readFile2(url);
|
|
13890
|
+
imageContentType = IMAGE_MIME_TYPES[ext];
|
|
13891
|
+
if (imageBuffer.length > MAX_IMAGE_BYTES) {
|
|
13892
|
+
log?.warn?.(`[${accountId}] [outbound:createPost] skipping ${filename} \u2014 exceeds 5MB (${imageBuffer.length} bytes)`);
|
|
13893
|
+
imageBuffer = void 0;
|
|
13894
|
+
imageContentType = void 0;
|
|
13895
|
+
continue;
|
|
13896
|
+
}
|
|
13897
|
+
break;
|
|
13898
|
+
} catch (err) {
|
|
13899
|
+
log?.warn?.(`[${accountId}] [outbound:createPost] failed to read ${url}: ${err}`);
|
|
13900
|
+
}
|
|
13901
|
+
}
|
|
13902
|
+
}
|
|
13903
|
+
if (!imageBuffer || !imageContentType) {
|
|
13904
|
+
log?.error?.(`[${accountId}] [outbound:createPost] no valid image found in mediaUrls (posts require an image)`);
|
|
13905
|
+
return { ok: false, error: "Posts require an image \u2014 no valid image found in media URLs" };
|
|
13906
|
+
}
|
|
13907
|
+
log?.info?.(`[${accountId}] [outbound:createPost] groupId=${groupId}, text=${text?.length ?? 0} chars, image=${imageContentType} (${imageBuffer.length} bytes)`);
|
|
13908
|
+
try {
|
|
13909
|
+
const presign = await client.createPost({
|
|
13910
|
+
identityId: senderId,
|
|
13911
|
+
contentType: imageContentType,
|
|
13912
|
+
content: text || void 0,
|
|
13913
|
+
groupId
|
|
13914
|
+
});
|
|
13915
|
+
await client.uploadToPresignedUrl(presign.uploadUrl, imageBuffer, imageContentType);
|
|
13916
|
+
log?.info?.(`[${accountId}] [outbound:createPost] post ${presign.post.id} created in group ${groupId}, image uploaded to ${presign.imageUrl}`);
|
|
13917
|
+
return { ok: true };
|
|
13918
|
+
} catch (error) {
|
|
13919
|
+
if (error instanceof QuiuboApiError) {
|
|
13920
|
+
log?.error?.(`[${accountId}] [outbound:createPost] FAILED [${error.status}] group=${groupId} \u2014 ${error.body}`);
|
|
13921
|
+
return { ok: false, error: `${error.status}: ${error.body}` };
|
|
13922
|
+
}
|
|
13923
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
13924
|
+
log?.error?.(`[${accountId}] [outbound:createPost] FAILED [network] group=${groupId} \u2014 ${msg}`);
|
|
13925
|
+
return { ok: false, error: msg };
|
|
13926
|
+
}
|
|
13824
13927
|
}
|
|
13825
13928
|
},
|
|
13826
13929
|
// ── gateway adapter ─────────────────────────────────────────────
|
|
@@ -14240,11 +14343,11 @@ function resolveOutboundGroupId(ctx) {
|
|
|
14240
14343
|
}
|
|
14241
14344
|
async function resolveAnnounceGroupId(accountId, log) {
|
|
14242
14345
|
try {
|
|
14243
|
-
const { readFile:
|
|
14346
|
+
const { readFile: readFile4 } = await import("node:fs/promises");
|
|
14244
14347
|
const { join: join3 } = await import("node:path");
|
|
14245
14348
|
const homeDir = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
14246
14349
|
const cronPath = join3(homeDir, ".openclaw", "cron", "jobs.json");
|
|
14247
|
-
const raw = await
|
|
14350
|
+
const raw = await readFile4(cronPath, "utf-8");
|
|
14248
14351
|
const parsed = JSON.parse(raw);
|
|
14249
14352
|
const jobs = parsed?.jobs ?? [];
|
|
14250
14353
|
for (const job of jobs) {
|
|
@@ -14264,12 +14367,12 @@ async function resolveAnnounceGroupId(accountId, log) {
|
|
|
14264
14367
|
}
|
|
14265
14368
|
async function getActivityData(runtime2, log, agentId) {
|
|
14266
14369
|
try {
|
|
14267
|
-
const { readFile:
|
|
14370
|
+
const { readFile: readFile4 } = await import("node:fs/promises");
|
|
14268
14371
|
const { join: join3 } = await import("node:path");
|
|
14269
14372
|
const homeDir = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
14270
14373
|
const cronPath = join3(homeDir, ".openclaw", "cron", "jobs.json");
|
|
14271
14374
|
log?.info?.(`getActivityData: reading ${cronPath} (agentId=${agentId})`);
|
|
14272
|
-
const raw = await
|
|
14375
|
+
const raw = await readFile4(cronPath, "utf-8");
|
|
14273
14376
|
const parsed = JSON.parse(raw);
|
|
14274
14377
|
const jobs = parsed?.jobs ?? [];
|
|
14275
14378
|
const items = jobs.filter((j) => j.enabled !== false && (!agentId || j.agentId === agentId)).map((job) => ({
|
|
@@ -14520,6 +14623,116 @@ async function routeInboundMessage(opts) {
|
|
|
14520
14623
|
log?.info?.(`[${accountId}] Quiubo: message processed from ${senderId}`);
|
|
14521
14624
|
}
|
|
14522
14625
|
|
|
14626
|
+
// src/create-post-tool.ts
|
|
14627
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
14628
|
+
var IMAGE_MIME_TYPES2 = {
|
|
14629
|
+
".jpg": "image/jpeg",
|
|
14630
|
+
".jpeg": "image/jpeg",
|
|
14631
|
+
".png": "image/png",
|
|
14632
|
+
".webp": "image/webp"
|
|
14633
|
+
};
|
|
14634
|
+
var MAX_IMAGE_BYTES2 = 5 * 1024 * 1024;
|
|
14635
|
+
var DEFAULT_API_URL2 = "https://api.quiubo.io";
|
|
14636
|
+
function extractGroupIdFromSessionKey(key) {
|
|
14637
|
+
if (!key) return void 0;
|
|
14638
|
+
const match = key.match(/quiubo:([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/i);
|
|
14639
|
+
return match?.[1];
|
|
14640
|
+
}
|
|
14641
|
+
function jsonResult(payload) {
|
|
14642
|
+
return {
|
|
14643
|
+
content: [{ type: "text", text: JSON.stringify(payload) }],
|
|
14644
|
+
details: payload
|
|
14645
|
+
};
|
|
14646
|
+
}
|
|
14647
|
+
function errorResult(msg) {
|
|
14648
|
+
return {
|
|
14649
|
+
content: [{ type: "text", text: msg }],
|
|
14650
|
+
details: { ok: false, error: msg }
|
|
14651
|
+
};
|
|
14652
|
+
}
|
|
14653
|
+
function createQuiuboPostToolFactory(ctx) {
|
|
14654
|
+
const quiuboConfig = ctx.config?.channels?.quiubo;
|
|
14655
|
+
const accountCfg = quiuboConfig?.accounts?.default;
|
|
14656
|
+
if (!accountCfg?.apiKey) return null;
|
|
14657
|
+
const client = new QuiuboApiClient(
|
|
14658
|
+
accountCfg.apiUrl ?? DEFAULT_API_URL2,
|
|
14659
|
+
accountCfg.apiKey
|
|
14660
|
+
);
|
|
14661
|
+
const botIdentityId = accountCfg.botIdentityId;
|
|
14662
|
+
const defaultGroupId = extractGroupIdFromSessionKey(ctx.sessionKey);
|
|
14663
|
+
return {
|
|
14664
|
+
name: "quiubo_create_post",
|
|
14665
|
+
label: "Create Quiubo Post",
|
|
14666
|
+
description: "Create a post with an image in a Quiubo group. Posts appear in the group post feed and auto-expire after 24 hours. REQUIRES an image file path on disk (.jpg/.jpeg/.png/.webp, max 5MB). Optionally include a caption (max 500 chars).",
|
|
14667
|
+
parameters: {
|
|
14668
|
+
type: "object",
|
|
14669
|
+
properties: {
|
|
14670
|
+
image_path: {
|
|
14671
|
+
type: "string",
|
|
14672
|
+
description: "Absolute path to image file on disk"
|
|
14673
|
+
},
|
|
14674
|
+
caption: {
|
|
14675
|
+
type: "string",
|
|
14676
|
+
description: "Optional text caption (max 500 chars)"
|
|
14677
|
+
},
|
|
14678
|
+
group_id: {
|
|
14679
|
+
type: "string",
|
|
14680
|
+
description: "Target group UUID. Omit to use current conversation group."
|
|
14681
|
+
}
|
|
14682
|
+
},
|
|
14683
|
+
required: ["image_path"]
|
|
14684
|
+
},
|
|
14685
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14686
|
+
execute: async (_toolCallId, params) => {
|
|
14687
|
+
const groupId = params.group_id || defaultGroupId;
|
|
14688
|
+
if (!groupId) {
|
|
14689
|
+
return errorResult("No group_id provided and could not resolve from session context");
|
|
14690
|
+
}
|
|
14691
|
+
if (!botIdentityId) {
|
|
14692
|
+
return errorResult("No botIdentityId configured");
|
|
14693
|
+
}
|
|
14694
|
+
const imagePath = params.image_path;
|
|
14695
|
+
const ext = imagePath.substring(imagePath.lastIndexOf(".")).toLowerCase();
|
|
14696
|
+
if (!(ext in IMAGE_MIME_TYPES2)) {
|
|
14697
|
+
return errorResult(`Unsupported image format: ${ext}. Use .jpg, .png, or .webp`);
|
|
14698
|
+
}
|
|
14699
|
+
let imageBuffer;
|
|
14700
|
+
try {
|
|
14701
|
+
imageBuffer = await readFile3(imagePath);
|
|
14702
|
+
} catch {
|
|
14703
|
+
return errorResult(`Cannot read image: ${imagePath}`);
|
|
14704
|
+
}
|
|
14705
|
+
if (imageBuffer.length > MAX_IMAGE_BYTES2) {
|
|
14706
|
+
return errorResult(
|
|
14707
|
+
`Image exceeds 5MB limit (${(imageBuffer.length / 1024 / 1024).toFixed(1)}MB)`
|
|
14708
|
+
);
|
|
14709
|
+
}
|
|
14710
|
+
const contentType = IMAGE_MIME_TYPES2[ext];
|
|
14711
|
+
try {
|
|
14712
|
+
const presign = await client.createPost({
|
|
14713
|
+
identityId: botIdentityId,
|
|
14714
|
+
contentType,
|
|
14715
|
+
content: params.caption || void 0,
|
|
14716
|
+
groupId
|
|
14717
|
+
});
|
|
14718
|
+
await client.uploadToPresignedUrl(presign.uploadUrl, imageBuffer, contentType);
|
|
14719
|
+
return jsonResult({
|
|
14720
|
+
ok: true,
|
|
14721
|
+
postId: presign.post.id,
|
|
14722
|
+
imageUrl: presign.imageUrl,
|
|
14723
|
+
groupId
|
|
14724
|
+
});
|
|
14725
|
+
} catch (error) {
|
|
14726
|
+
if (error instanceof QuiuboApiError) {
|
|
14727
|
+
return errorResult(`Quiubo API error: ${error.status} \u2014 ${error.body}`);
|
|
14728
|
+
}
|
|
14729
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
14730
|
+
return errorResult(`Post creation failed: ${msg}`);
|
|
14731
|
+
}
|
|
14732
|
+
}
|
|
14733
|
+
};
|
|
14734
|
+
}
|
|
14735
|
+
|
|
14523
14736
|
// src/polling-gateway.ts
|
|
14524
14737
|
var PollingGateway = class {
|
|
14525
14738
|
client;
|
|
@@ -14667,6 +14880,7 @@ var plugin = {
|
|
|
14667
14880
|
register(api) {
|
|
14668
14881
|
setQuiuboRuntime(api.runtime);
|
|
14669
14882
|
api.registerChannel({ plugin: quiuboPlugin });
|
|
14883
|
+
api.registerTool(createQuiuboPostToolFactory, { name: "quiubo_create_post" });
|
|
14670
14884
|
}
|
|
14671
14885
|
};
|
|
14672
14886
|
var index_default = plugin;
|