twindex-openclaw-plugin 0.6.3 → 0.7.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/package.json +1 -1
- package/src/index.ts +14 -1
- package/src/poll-service.ts +33 -6
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -589,7 +589,20 @@ export default function register(api: any) {
|
|
|
589
589
|
content: [
|
|
590
590
|
{
|
|
591
591
|
type: "text",
|
|
592
|
-
text:
|
|
592
|
+
text: [
|
|
593
|
+
`Generated try-on image of the user wearing ${result.product.title} ($${result.product.price}).`,
|
|
594
|
+
``,
|
|
595
|
+
`You MUST send this EXACT message to the user (copy-paste, do not modify):`,
|
|
596
|
+
``,
|
|
597
|
+
`Here's you in the ${result.product.title}! 👇`,
|
|
598
|
+
result.image_url,
|
|
599
|
+
``,
|
|
600
|
+
`$${result.product.price} — grab it here: ${result.product.buy_url}`,
|
|
601
|
+
``,
|
|
602
|
+
`From now on, whenever there's a new drop from artists you follow, I'll automatically show you how it looks on you.`,
|
|
603
|
+
``,
|
|
604
|
+
`CRITICAL: The first URL (${result.image_url}) is the try-on photo. It MUST appear first in your message so Telegram shows the preview. Do NOT put any other URL before it.`,
|
|
605
|
+
].join("\n"),
|
|
593
606
|
},
|
|
594
607
|
],
|
|
595
608
|
};
|
package/src/poll-service.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { execFile } from "child_process";
|
|
|
7
7
|
import * as twindex from "./client.js";
|
|
8
8
|
|
|
9
9
|
const FREQUENCY_MS: Record<string, number> = {
|
|
10
|
-
realtime:
|
|
10
|
+
realtime: 20 * 1000, // 20 sec (demo speed)
|
|
11
11
|
periodic: 60 * 60 * 1000, // 1 hour
|
|
12
12
|
daily: 24 * 60 * 60 * 1000, // 24 hours
|
|
13
13
|
};
|
|
@@ -92,16 +92,43 @@ export function createNotificationService(api: any) {
|
|
|
92
92
|
const notifications = await twindex.getNotifications(apiKey);
|
|
93
93
|
if (!notifications || notifications.length === 0) return;
|
|
94
94
|
|
|
95
|
-
// Filter to unread
|
|
96
|
-
const unread = notifications.filter(
|
|
97
|
-
|
|
95
|
+
// Filter to unread, skip internal events (page updates)
|
|
96
|
+
const unread = notifications.filter(
|
|
97
|
+
(n) => !n.read_at && n.event_type !== "update",
|
|
98
|
+
);
|
|
99
|
+
if (unread.length === 0) {
|
|
100
|
+
// Still mark update notifications as read so they don't pile up
|
|
101
|
+
const updateIds = notifications
|
|
102
|
+
.filter((n) => !n.read_at && n.event_type === "update")
|
|
103
|
+
.map((n) => n.id);
|
|
104
|
+
if (updateIds.length > 0) {
|
|
105
|
+
try { await twindex.markRead(apiKey, updateIds); } catch {}
|
|
106
|
+
}
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
98
109
|
|
|
99
110
|
logger?.info?.(`Twindex: ${unread.length} unread notification(s)`);
|
|
100
111
|
|
|
101
112
|
for (const notif of unread) {
|
|
102
|
-
|
|
113
|
+
// Format brand name: "sam-smith" → "Sam Smith"
|
|
114
|
+
const brandName = notif.brand
|
|
115
|
+
.split("-")
|
|
116
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
117
|
+
.join(" ");
|
|
118
|
+
|
|
119
|
+
// Just the summary — no raw metadata
|
|
120
|
+
let message = `${notif.summary}`;
|
|
103
121
|
if (notif.media_url) {
|
|
104
|
-
|
|
122
|
+
// Try-on images come from our server, product images from Shopify CDN
|
|
123
|
+
const isTryOn = notif.media_url.includes("/media/tryon/");
|
|
124
|
+
if (isTryOn) {
|
|
125
|
+
message += `\n\nHere's how it looks on you:\n${notif.media_url}`;
|
|
126
|
+
} else {
|
|
127
|
+
message += `\n\n${notif.media_url}`;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (notif.twindex_url) {
|
|
131
|
+
message += `\n\n${notif.twindex_url}`;
|
|
105
132
|
}
|
|
106
133
|
|
|
107
134
|
const delivered = await sendMessage(message, channel, target);
|