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 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 || !_knownDeviceIds.has(targetDeviceId)) return;
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 that have been active in last 24h
1322
+ // Get all profiles with valid notification targets
1314
1323
  const { data: activeProfiles } = await supabase
1315
- .rpc("nearby_profiles", { p_lat: 0, p_lng: 0, p_radius_m: 999999999 })
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.add(notifyKey);
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
- // Clean up follow-up crons
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 deviceId of _knownDeviceIds) {
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
- _notifiedMatches.add(key);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "antenna-openclaw-plugin",
3
- "version": "1.3.3",
3
+ "version": "1.3.5",
4
4
  "description": "Antenna — agent-mediated nearby people discovery for OpenClaw",
5
5
  "openclaw": {
6
6
  "extensions": ["./index.ts"]
@@ -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 - marks you as present at the event location. Optionally updates GPS.
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)`