antenna-openclaw-plugin 1.3.3 → 1.3.5
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/index.ts +25 -15
- package/package.json +1 -1
- package/skills/antenna/SKILL.md +3 -1
package/index.ts
CHANGED
|
@@ -1006,7 +1006,7 @@ export default function register(api: any) {
|
|
|
1006
1006
|
}));
|
|
1007
1007
|
|
|
1008
1008
|
const incomingAccepts = rawIncoming.map((m: any, i: number) => ({
|
|
1009
|
-
ref: String(i + 1),
|
|
1009
|
+
ref: String(mutualMatches.length + i + 1),
|
|
1010
1010
|
_device_id: m.target_id,
|
|
1011
1011
|
name: m.name || "匿名",
|
|
1012
1012
|
emoji: m.emoji || "👤",
|
|
@@ -1024,6 +1024,14 @@ export default function register(api: any) {
|
|
|
1024
1024
|
if (incomingAccepts.length > 0) messages.push(`${incomingAccepts.length} 个人想认识你,等你回应`);
|
|
1025
1025
|
if (messages.length === 0) messages.push("你接受了一些匹配,但对方还没有回应。耐心等等 ⏳");
|
|
1026
1026
|
|
|
1027
|
+
// Persist ref map so accept(ref) resolves correctly
|
|
1028
|
+
const _refMap: Record<string, string> = {};
|
|
1029
|
+
for (const m of mutualMatches) _refMap[m.ref] = m._device_id;
|
|
1030
|
+
for (const m of incomingAccepts) _refMap[m.ref] = m._device_id;
|
|
1031
|
+
if (deviceId && Object.keys(_refMap).length > 0) {
|
|
1032
|
+
try { await supabase.rpc("save_scan_refs", { p_owner: deviceId, p_refs: _refMap }); } catch { /* best effort */ }
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1027
1035
|
return ok({
|
|
1028
1036
|
mutual_matches: mutualMatches,
|
|
1029
1037
|
incoming_accepts: incomingAccepts,
|
|
@@ -1181,11 +1189,10 @@ export default function register(api: any) {
|
|
|
1181
1189
|
async (payload: any) => {
|
|
1182
1190
|
try {
|
|
1183
1191
|
const targetDeviceId = payload.new?.device_id_b;
|
|
1184
|
-
if (!targetDeviceId
|
|
1192
|
+
if (!targetDeviceId) return;
|
|
1185
1193
|
|
|
1186
1194
|
const key = `${payload.new.device_id_a}→${targetDeviceId}`;
|
|
1187
1195
|
if (_notifiedMatches.has(key)) return;
|
|
1188
|
-
_notifiedMatches.add(key);
|
|
1189
1196
|
|
|
1190
1197
|
const parts = targetDeviceId.split(":");
|
|
1191
1198
|
if (parts.length < 2) return;
|
|
@@ -1210,11 +1217,13 @@ export default function register(api: any) {
|
|
|
1210
1217
|
notifyUser(channel, userId,
|
|
1211
1218
|
`[Antenna] 🎉 双向匹配!${emoji} ${name} 也接受了你!${contact}\n\n用 antenna_check_matches 查看详情。`,
|
|
1212
1219
|
logger);
|
|
1220
|
+
_notifiedMatches.add(key);
|
|
1213
1221
|
stopFollowUpCron(targetDeviceId, payload.new.device_id_a, logger);
|
|
1214
1222
|
} else {
|
|
1215
1223
|
notifyUser(channel, userId,
|
|
1216
1224
|
`[Antenna] 📩 ${emoji} ${name} 想认识你!看看 TA 的名片,决定要不要接受?\n\n用 antenna_check_matches 查看详情。`,
|
|
1217
1225
|
logger);
|
|
1226
|
+
_notifiedMatches.add(key);
|
|
1218
1227
|
}
|
|
1219
1228
|
} catch (err: any) {
|
|
1220
1229
|
logger.warn("Antenna: realtime match handler error:", err.message);
|
|
@@ -1310,10 +1319,9 @@ export default function register(api: any) {
|
|
|
1310
1319
|
const cfg = getConfig(api);
|
|
1311
1320
|
const supabase = getSupabase(cfg);
|
|
1312
1321
|
|
|
1313
|
-
// Get all profiles
|
|
1322
|
+
// Get all profiles with valid notification targets
|
|
1314
1323
|
const { data: activeProfiles } = await supabase
|
|
1315
|
-
.rpc("
|
|
1316
|
-
.select("device_id");
|
|
1324
|
+
.rpc("get_notification_targets", { p_since: "7 days" });
|
|
1317
1325
|
|
|
1318
1326
|
if (!activeProfiles?.length) return;
|
|
1319
1327
|
|
|
@@ -1343,7 +1351,7 @@ export default function register(api: any) {
|
|
|
1343
1351
|
|
|
1344
1352
|
for (const match of newMatches) {
|
|
1345
1353
|
const notifyKey = `${match.device_id_a}→${match.device_id_b}`;
|
|
1346
|
-
_notifiedMatches.
|
|
1354
|
+
if (_notifiedMatches.has(notifyKey)) continue;
|
|
1347
1355
|
|
|
1348
1356
|
// Is this a new mutual match?
|
|
1349
1357
|
if (match.device_id_a === deviceId) {
|
|
@@ -1358,7 +1366,7 @@ export default function register(api: any) {
|
|
|
1358
1366
|
`[Antenna] 🎉 双向匹配成功!${emoji} ${name} 也接受了你!${contact}\n\n用 antenna_check_matches 查看详情。`,
|
|
1359
1367
|
logger,
|
|
1360
1368
|
);
|
|
1361
|
-
|
|
1369
|
+
_notifiedMatches.add(notifyKey);
|
|
1362
1370
|
stopFollowUpCron(deviceId, match.device_id_b, logger);
|
|
1363
1371
|
}
|
|
1364
1372
|
} else if (match.device_id_b === deviceId) {
|
|
@@ -1368,21 +1376,21 @@ export default function register(api: any) {
|
|
|
1368
1376
|
const emoji = theirProfile?.emoji || "👤";
|
|
1369
1377
|
const iAccepted = myMatches.find((m: any) => m.device_id_b === match.device_id_a);
|
|
1370
1378
|
if (iAccepted) {
|
|
1371
|
-
// I already accepted them → mutual!
|
|
1372
1379
|
const contact = match.contact_info_a ? `\n对方的联系方式:${match.contact_info_a}` : "";
|
|
1373
1380
|
notifyUser(
|
|
1374
1381
|
channel, userId,
|
|
1375
1382
|
`[Antenna] 🎉 双向匹配成功!${emoji} ${name} 也接受了你!${contact}\n\n用 antenna_check_matches 查看详情。`,
|
|
1376
1383
|
logger,
|
|
1377
1384
|
);
|
|
1385
|
+
_notifiedMatches.add(notifyKey);
|
|
1378
1386
|
stopFollowUpCron(deviceId, match.device_id_a, logger);
|
|
1379
1387
|
} else {
|
|
1380
|
-
// They accepted me but I haven't responded
|
|
1381
1388
|
notifyUser(
|
|
1382
1389
|
channel, userId,
|
|
1383
1390
|
`[Antenna] 📩 ${emoji} ${name} 想认识你!看看 TA 的名片,决定要不要接受?\n\n用 antenna_check_matches 查看详情。`,
|
|
1384
1391
|
logger,
|
|
1385
1392
|
);
|
|
1393
|
+
_notifiedMatches.add(notifyKey);
|
|
1386
1394
|
}
|
|
1387
1395
|
}
|
|
1388
1396
|
}
|
|
@@ -1393,8 +1401,9 @@ export default function register(api: any) {
|
|
|
1393
1401
|
}
|
|
1394
1402
|
}
|
|
1395
1403
|
|
|
1396
|
-
// ── Event approval polling ──
|
|
1397
|
-
for (const
|
|
1404
|
+
// ── Event approval polling (use notification targets, not _knownDeviceIds) ──
|
|
1405
|
+
for (const profile of activeProfiles) {
|
|
1406
|
+
const deviceId = profile.device_id;
|
|
1398
1407
|
try {
|
|
1399
1408
|
const { data: events } = await supabase.rpc("get_my_event_updates", { p_device_id: deviceId });
|
|
1400
1409
|
if (!events?.length) continue;
|
|
@@ -1403,19 +1412,20 @@ export default function register(api: any) {
|
|
|
1403
1412
|
const channel = parts[0];
|
|
1404
1413
|
const userId = parts.slice(1).join(":");
|
|
1405
1414
|
for (const ev of events) {
|
|
1406
|
-
const key = `event:${ev.event_id}:${ev.status}`;
|
|
1415
|
+
const key = `event:${deviceId}:${ev.event_id}:${ev.status}`;
|
|
1407
1416
|
if (_notifiedMatches.has(key)) continue;
|
|
1408
|
-
|
|
1409
|
-
if (ev.status === "active" && ev.role !== "creator" && ev.role !== "cohost") {
|
|
1417
|
+
if (ev.status === "active" && ev.role !== "creator" && ev.role !== "cohost" && ev.requires_approval) {
|
|
1410
1418
|
notifyUser(channel, userId,
|
|
1411
1419
|
`[Antenna] ✅ 你的申请已通过!欢迎加入「${ev.event_name}」`,
|
|
1412
1420
|
logger,
|
|
1413
1421
|
);
|
|
1422
|
+
_notifiedMatches.add(key);
|
|
1414
1423
|
} else if (ev.status === "rejected") {
|
|
1415
1424
|
notifyUser(channel, userId,
|
|
1416
1425
|
`[Antenna] ❌ 你的申请未通过「${ev.event_name}」`,
|
|
1417
1426
|
logger,
|
|
1418
1427
|
);
|
|
1428
|
+
_notifiedMatches.add(key);
|
|
1419
1429
|
}
|
|
1420
1430
|
}
|
|
1421
1431
|
} catch { /* silent */ }
|
package/package.json
CHANGED
package/skills/antenna/SKILL.md
CHANGED
|
@@ -344,7 +344,7 @@ Scan people in an event. No distance limit - returns all participants.
|
|
|
344
344
|
- Returns profiles with `source: "event"` tag
|
|
345
345
|
|
|
346
346
|
### `antenna_event_checkin`
|
|
347
|
-
Check in at an event
|
|
347
|
+
Check in at an event — marks you as present at the event location. Optionally updates GPS.
|
|
348
348
|
- `code`: event code
|
|
349
349
|
- `sender_id`, `channel`: from context
|
|
350
350
|
- `chat_id`: REQUIRED for notifications
|
|
@@ -352,6 +352,7 @@ Check in at an event - marks you as present at the event location. Optionally up
|
|
|
352
352
|
- **Event must have started** (`starts_at <= now`). Cannot check in before start time.
|
|
353
353
|
- **Must be within 1km** of event location.
|
|
354
354
|
- **Must have `status: active`** (approved participants only, not pending).
|
|
355
|
+
- **Check-in is automatic on join.** Only call this manually if the user explicitly asks to check in. Do not prompt the user about check-in.
|
|
355
356
|
|
|
356
357
|
### `antenna_event_upload_image`
|
|
357
358
|
Upload an image for an event OG preview. Returns a public URL.
|
|
@@ -414,6 +415,7 @@ Share the event URL with the user.
|
|
|
414
415
|
- If screening questions returned: **ask the user each question**, collect answers, then call `antenna_event_join(code, application_context="answers")` again
|
|
415
416
|
- If `status: pending` → "waiting for organizer approval"
|
|
416
417
|
- If `status: active` → user is in! Auto check-in if event started + GPS within 1km.
|
|
418
|
+
- **Do NOT ask the user about check-in.** Check-in is automatic — if the response has `checked_in: true`, just confirm they're in. If `checked_in: false`, ignore it silently. Users don't need to know about or manage check-in.
|
|
417
419
|
|
|
418
420
|
### Scanning an event
|
|
419
421
|
1. Call `antenna_event_scan(code)`
|