antenna-fyi 1.2.2 → 1.2.3
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/bin/antenna.js +6 -0
- package/lib/cli.js +59 -1
- package/lib/hermes-plugin/__init__.py +17 -0
- package/lib/hermes-plugin/schemas.py +86 -0
- package/lib/hermes-plugin/tools.py +168 -0
- package/package.json +1 -1
- package/skill/SKILL.md +14 -0
package/bin/antenna.js
CHANGED
|
@@ -8,7 +8,9 @@ import {
|
|
|
8
8
|
handleCheckin,
|
|
9
9
|
handleMatches,
|
|
10
10
|
handleDiscover,
|
|
11
|
+
handleEvent,
|
|
11
12
|
handleBind,
|
|
13
|
+
handlePass,
|
|
12
14
|
handleSetup,
|
|
13
15
|
handleStatus,
|
|
14
16
|
handleInstallSkill,
|
|
@@ -34,8 +36,12 @@ async function main() {
|
|
|
34
36
|
return handleMatches(f);
|
|
35
37
|
case "discover":
|
|
36
38
|
return handleDiscover(f);
|
|
39
|
+
case "event":
|
|
40
|
+
return handleEvent(f);
|
|
37
41
|
case "bind":
|
|
38
42
|
return handleBind(f);
|
|
43
|
+
case "pass":
|
|
44
|
+
return handlePass(f);
|
|
39
45
|
case "serve": {
|
|
40
46
|
const { startMcpServer } = await import("../lib/mcp.js");
|
|
41
47
|
return startMcpServer();
|
package/lib/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// antenna CLI command handlers
|
|
2
2
|
|
|
3
|
-
import { scan, getProfile, setProfile, accept, checkMatches, checkin, createBindToken, discover } from "./core.js";
|
|
3
|
+
import { scan, getProfile, setProfile, accept, checkMatches, checkin, createBindToken, discover, createEvent, joinEvent, eventScan, pass as passUser } from "./core.js";
|
|
4
4
|
import { createInterface } from "readline";
|
|
5
5
|
import { existsSync, mkdirSync, copyFileSync } from "fs";
|
|
6
6
|
import { join, dirname } from "path";
|
|
@@ -125,6 +125,50 @@ export async function handleDiscover(f) {
|
|
|
125
125
|
});
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
+
export async function handleEvent(f) {
|
|
129
|
+
const sub = f._?.[0] || Object.keys(f).find(k => ["create", "join", "scan"].includes(k));
|
|
130
|
+
|
|
131
|
+
if (f.create || (!f.join && !f.scan && f.name)) {
|
|
132
|
+
if (!f.name) return console.error("Usage: antenna event --create --name 'AI Meetup'");
|
|
133
|
+
const result = await createEvent({ name: f.name, device_id: f.id || null, lat: f.lat ? +f.lat : undefined, lng: f.lng ? +f.lng : undefined });
|
|
134
|
+
console.log(`\n🎉 Event created!\n`);
|
|
135
|
+
console.log(` Name: ${result.name}`);
|
|
136
|
+
console.log(` Code: ${result.code}`);
|
|
137
|
+
console.log(` URL: ${result.url}`);
|
|
138
|
+
console.log(` Ends: ${result.ends_at}\n`);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (f.join) {
|
|
143
|
+
if (!f.code || !f.id) return console.error("Usage: antenna event --join --code abc123 --id telegram:123");
|
|
144
|
+
const result = await joinEvent({ code: f.code, device_id: f.id });
|
|
145
|
+
if (result.joined) {
|
|
146
|
+
console.log(`\n✅ Joined "${result.name}" (${result.code})\n`);
|
|
147
|
+
} else {
|
|
148
|
+
console.log(`\n❌ ${result.error}\n`);
|
|
149
|
+
}
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (f.scan) {
|
|
154
|
+
if (!f.code) return console.error("Usage: antenna event --scan --code abc123 [--id telegram:123]");
|
|
155
|
+
const result = await eventScan({ code: f.code, device_id: f.id || null });
|
|
156
|
+
if (result.count === 0) return console.log("\n🏟️ No participants yet.\n");
|
|
157
|
+
console.log(`\n🏟️ ${result.count} people in this event:\n`);
|
|
158
|
+
result.profiles.forEach((p) => {
|
|
159
|
+
console.log(` ${p.emoji} ${p.name}`);
|
|
160
|
+
if (p.line1) console.log(` ${p.line1}`);
|
|
161
|
+
console.log(` ref: ${p.ref}\n`);
|
|
162
|
+
});
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
console.log(`Usage:
|
|
167
|
+
antenna event --create --name 'AI Meetup' [--id telegram:123]
|
|
168
|
+
antenna event --join --code abc123 --id telegram:123
|
|
169
|
+
antenna event --scan --code abc123 [--id telegram:123]`);
|
|
170
|
+
}
|
|
171
|
+
|
|
128
172
|
export async function handleBind(f) {
|
|
129
173
|
if (!f.id) return console.error("Usage: antenna bind --id telegram:123");
|
|
130
174
|
const result = await createBindToken({ device_id: f.id });
|
|
@@ -134,6 +178,17 @@ export async function handleBind(f) {
|
|
|
134
178
|
console.log();
|
|
135
179
|
}
|
|
136
180
|
|
|
181
|
+
export async function handlePass(f) {
|
|
182
|
+
if (!f.id) return console.error("Usage: antenna pass --id telegram:123 --target telegram:789");
|
|
183
|
+
if (!f.target && !f.ref) return console.error("Usage: antenna pass --id telegram:123 --target telegram:789 (or --ref 1)");
|
|
184
|
+
const result = await passUser({
|
|
185
|
+
device_id: f.id,
|
|
186
|
+
target_device_id: f.target,
|
|
187
|
+
ref: f.ref,
|
|
188
|
+
});
|
|
189
|
+
console.log("✅ " + (result.message || "Passed."));
|
|
190
|
+
}
|
|
191
|
+
|
|
137
192
|
export async function handleSetup(f) {
|
|
138
193
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
139
194
|
const ask = (q) => new Promise((resolve) => rl.question(q, resolve));
|
|
@@ -303,7 +358,10 @@ Usage:
|
|
|
303
358
|
antenna checkin --id telegram:123 --lat 39.99 --lng 116.48
|
|
304
359
|
antenna profile --id telegram:123 [--name Yi --emoji 🦦 --line1 '...']
|
|
305
360
|
antenna accept --id telegram:123 --target telegram:789 [--contact 'WeChat: yi']
|
|
361
|
+
antenna pass --id telegram:123 --target telegram:789 (or --ref 1)
|
|
306
362
|
antenna matches --id telegram:123
|
|
363
|
+
antenna discover --id telegram:123
|
|
364
|
+
antenna event --create --name 'AI Meetup' | --join --code abc123 | --scan --code abc123
|
|
307
365
|
antenna bind --id telegram:123
|
|
308
366
|
antenna serve Start MCP server (stdio transport)
|
|
309
367
|
antenna setup Interactive profile setup [--id telegram:123]
|
|
@@ -13,15 +13,27 @@ from .tools import (
|
|
|
13
13
|
handle_checkin,
|
|
14
14
|
handle_check_matches,
|
|
15
15
|
handle_bind,
|
|
16
|
+
handle_pass,
|
|
17
|
+
handle_discover,
|
|
18
|
+
handle_event_create,
|
|
19
|
+
handle_event_join,
|
|
20
|
+
handle_event_scan,
|
|
16
21
|
_sb,
|
|
17
22
|
_device_id,
|
|
18
23
|
_my_device_ids,
|
|
24
|
+
)
|
|
25
|
+
from .schemas import (
|
|
19
26
|
SCAN_SCHEMA,
|
|
20
27
|
PROFILE_SCHEMA,
|
|
21
28
|
ACCEPT_SCHEMA,
|
|
22
29
|
CHECKIN_SCHEMA,
|
|
23
30
|
CHECK_MATCHES_SCHEMA,
|
|
24
31
|
BIND_SCHEMA,
|
|
32
|
+
PASS_SCHEMA,
|
|
33
|
+
DISCOVER_SCHEMA,
|
|
34
|
+
EVENT_CREATE_SCHEMA,
|
|
35
|
+
EVENT_JOIN_SCHEMA,
|
|
36
|
+
EVENT_SCAN_SCHEMA,
|
|
25
37
|
)
|
|
26
38
|
import re
|
|
27
39
|
import time
|
|
@@ -44,6 +56,11 @@ def register(ctx):
|
|
|
44
56
|
ctx.register_tool("antenna_checkin", CHECKIN_SCHEMA, handle_checkin)
|
|
45
57
|
ctx.register_tool("antenna_check_matches", CHECK_MATCHES_SCHEMA, handle_check_matches)
|
|
46
58
|
ctx.register_tool("antenna_bind", BIND_SCHEMA, handle_bind)
|
|
59
|
+
ctx.register_tool("antenna_pass", PASS_SCHEMA, handle_pass)
|
|
60
|
+
ctx.register_tool("antenna_discover", DISCOVER_SCHEMA, handle_discover)
|
|
61
|
+
ctx.register_tool("antenna_event_create", EVENT_CREATE_SCHEMA, handle_event_create)
|
|
62
|
+
ctx.register_tool("antenna_event_join", EVENT_JOIN_SCHEMA, handle_event_join)
|
|
63
|
+
ctx.register_tool("antenna_event_scan", EVENT_SCAN_SCHEMA, handle_event_scan)
|
|
47
64
|
|
|
48
65
|
# ── Hook: auto-detect location + check web GPS events ─────────
|
|
49
66
|
def on_pre_llm(messages, **kwargs):
|
|
@@ -134,3 +134,89 @@ BIND_SCHEMA = {
|
|
|
134
134
|
"required": ["sender_id", "channel"],
|
|
135
135
|
},
|
|
136
136
|
}
|
|
137
|
+
|
|
138
|
+
PASS_SCHEMA = {
|
|
139
|
+
"name": "antenna_pass",
|
|
140
|
+
"description": "Pass/skip a person. They won't be recommended again.",
|
|
141
|
+
"parameters": {
|
|
142
|
+
"type": "object",
|
|
143
|
+
"properties": {
|
|
144
|
+
"sender_id": {"type": "string", "description": "The sender's user ID"},
|
|
145
|
+
"channel": {"type": "string", "description": "Platform name"},
|
|
146
|
+
"ref": {
|
|
147
|
+
"type": "string",
|
|
148
|
+
"description": "Ref number from scan/discover results (e.g. '1')",
|
|
149
|
+
},
|
|
150
|
+
"target_device_id": {
|
|
151
|
+
"type": "string",
|
|
152
|
+
"description": "Device ID (use ref instead when possible)",
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
"required": ["sender_id", "channel"],
|
|
156
|
+
},
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
DISCOVER_SCHEMA = {
|
|
160
|
+
"name": "antenna_discover",
|
|
161
|
+
"description": (
|
|
162
|
+
"Get today's global recommendation — the person most similar to you "
|
|
163
|
+
"worldwide. 1 per day, no repeats."
|
|
164
|
+
),
|
|
165
|
+
"parameters": {
|
|
166
|
+
"type": "object",
|
|
167
|
+
"properties": {
|
|
168
|
+
"sender_id": {"type": "string", "description": "The sender's user ID"},
|
|
169
|
+
"channel": {"type": "string", "description": "Platform name"},
|
|
170
|
+
},
|
|
171
|
+
"required": ["sender_id", "channel"],
|
|
172
|
+
},
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
EVENT_CREATE_SCHEMA = {
|
|
176
|
+
"name": "antenna_event_create",
|
|
177
|
+
"description": (
|
|
178
|
+
"Create an event. Returns a shareable link (antenna.fyi/e/CODE) "
|
|
179
|
+
"for participants to join."
|
|
180
|
+
),
|
|
181
|
+
"parameters": {
|
|
182
|
+
"type": "object",
|
|
183
|
+
"properties": {
|
|
184
|
+
"name": {"type": "string", "description": "Event name"},
|
|
185
|
+
"sender_id": {"type": "string", "description": "The sender's user ID"},
|
|
186
|
+
"channel": {"type": "string", "description": "Platform name"},
|
|
187
|
+
"lat": {"type": "number", "description": "Event latitude"},
|
|
188
|
+
"lng": {"type": "number", "description": "Event longitude"},
|
|
189
|
+
"starts_at": {"type": "string", "description": "Start time ISO"},
|
|
190
|
+
"ends_at": {"type": "string", "description": "End time ISO"},
|
|
191
|
+
},
|
|
192
|
+
"required": ["name", "sender_id", "channel"],
|
|
193
|
+
},
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
EVENT_JOIN_SCHEMA = {
|
|
197
|
+
"name": "antenna_event_join",
|
|
198
|
+
"description": "Join an event by its code from the event URL.",
|
|
199
|
+
"parameters": {
|
|
200
|
+
"type": "object",
|
|
201
|
+
"properties": {
|
|
202
|
+
"code": {"type": "string", "description": "Event code"},
|
|
203
|
+
"sender_id": {"type": "string", "description": "The sender's user ID"},
|
|
204
|
+
"channel": {"type": "string", "description": "Platform name"},
|
|
205
|
+
},
|
|
206
|
+
"required": ["code", "sender_id", "channel"],
|
|
207
|
+
},
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
EVENT_SCAN_SCHEMA = {
|
|
211
|
+
"name": "antenna_event_scan",
|
|
212
|
+
"description": "Scan people in an event. No distance limit.",
|
|
213
|
+
"parameters": {
|
|
214
|
+
"type": "object",
|
|
215
|
+
"properties": {
|
|
216
|
+
"code": {"type": "string", "description": "Event code"},
|
|
217
|
+
"sender_id": {"type": "string", "description": "The sender's user ID"},
|
|
218
|
+
"channel": {"type": "string", "description": "Platform name"},
|
|
219
|
+
},
|
|
220
|
+
"required": ["code", "sender_id", "channel"],
|
|
221
|
+
},
|
|
222
|
+
}
|
|
@@ -8,6 +8,7 @@ import json
|
|
|
8
8
|
import math
|
|
9
9
|
import os
|
|
10
10
|
import time
|
|
11
|
+
import urllib.request
|
|
11
12
|
|
|
12
13
|
try:
|
|
13
14
|
from supabase import create_client
|
|
@@ -277,6 +278,173 @@ def handle_check_matches(params: dict) -> str:
|
|
|
277
278
|
BASE_URL = "https://www.antenna.fyi"
|
|
278
279
|
|
|
279
280
|
|
|
281
|
+
def handle_pass(params: dict) -> str:
|
|
282
|
+
sb = _sb()
|
|
283
|
+
did = _device_id(params["sender_id"], params["channel"])
|
|
284
|
+
|
|
285
|
+
ref = params.get("ref")
|
|
286
|
+
target = params.get("target_device_id")
|
|
287
|
+
if ref and ref in _last_ref_map:
|
|
288
|
+
target = _last_ref_map[ref]
|
|
289
|
+
if not target and ref:
|
|
290
|
+
# Try resolve via RPC
|
|
291
|
+
try:
|
|
292
|
+
resp = sb.rpc("resolve_ref", {"p_device_id": did, "p_ref": ref}).execute()
|
|
293
|
+
if resp.data:
|
|
294
|
+
target = resp.data.get("target_device_id")
|
|
295
|
+
except Exception:
|
|
296
|
+
pass
|
|
297
|
+
if not target:
|
|
298
|
+
return _ok({"error": "No target. Use 'ref' from scan/discover results or 'target_device_id'."})
|
|
299
|
+
|
|
300
|
+
sb.rpc("pass_user", {
|
|
301
|
+
"p_device_id": did,
|
|
302
|
+
"p_target_device_id": target,
|
|
303
|
+
}).execute()
|
|
304
|
+
|
|
305
|
+
return _ok({"passed": True, "message": "已跳过,不会再推荐这个人了。"})
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def handle_discover(params: dict) -> str:
|
|
309
|
+
sb = _sb()
|
|
310
|
+
did = _device_id(params["sender_id"], params["channel"])
|
|
311
|
+
|
|
312
|
+
resp = sb.rpc("global_discover", {"p_device_id": did}).execute()
|
|
313
|
+
results = resp.data or []
|
|
314
|
+
|
|
315
|
+
if not results:
|
|
316
|
+
return _ok({"count": 0, "message": "今天没有新的全球推荐了,明天再来看看。"})
|
|
317
|
+
|
|
318
|
+
global _last_ref_map
|
|
319
|
+
_last_ref_map = {}
|
|
320
|
+
profiles = []
|
|
321
|
+
|
|
322
|
+
# Get my profile for match reason
|
|
323
|
+
my_prof = sb.rpc("get_profile", {"p_device_id": did}).execute()
|
|
324
|
+
my_data = my_prof.data or {}
|
|
325
|
+
my_lines = [my_data.get("line1", ""), my_data.get("line2", ""), my_data.get("line3", "")]
|
|
326
|
+
|
|
327
|
+
for i, p in enumerate(results):
|
|
328
|
+
ref = str(i + 1)
|
|
329
|
+
_last_ref_map[ref] = p.get("device_id")
|
|
330
|
+
|
|
331
|
+
their_lines = [p.get("line1", ""), p.get("line2", ""), p.get("line3", "")]
|
|
332
|
+
|
|
333
|
+
# Generate match reason via Edge Function
|
|
334
|
+
match_reason = None
|
|
335
|
+
try:
|
|
336
|
+
req = urllib.request.Request(
|
|
337
|
+
f"{BUILTIN_URL}/functions/v1/generate-match-reason",
|
|
338
|
+
data=json.dumps({"my_lines": my_lines, "their_lines": their_lines}).encode(),
|
|
339
|
+
headers={"Content-Type": "application/json", "Authorization": f"Bearer {BUILTIN_KEY}"},
|
|
340
|
+
)
|
|
341
|
+
res = urllib.request.urlopen(req, timeout=10)
|
|
342
|
+
body = json.loads(res.read().decode())
|
|
343
|
+
match_reason = body.get("reason")
|
|
344
|
+
except Exception:
|
|
345
|
+
pass
|
|
346
|
+
|
|
347
|
+
profile = {
|
|
348
|
+
"ref": ref,
|
|
349
|
+
"emoji": p.get("emoji") or "\ud83d\udc64",
|
|
350
|
+
"name": p.get("display_name") or "匿名",
|
|
351
|
+
"line1": p.get("line1"),
|
|
352
|
+
"line2": p.get("line2"),
|
|
353
|
+
"line3": p.get("line3"),
|
|
354
|
+
}
|
|
355
|
+
if match_reason:
|
|
356
|
+
profile["match_reason"] = match_reason
|
|
357
|
+
profiles.append(profile)
|
|
358
|
+
|
|
359
|
+
return _ok({
|
|
360
|
+
"count": len(profiles),
|
|
361
|
+
"profiles": profiles,
|
|
362
|
+
"instruction": "这是全球推荐。根据你对用户的了解,判断是否值得推荐,写一句个性化的匹配理由。使用 ref 编号引用。",
|
|
363
|
+
})
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
def handle_event_create(params: dict) -> str:
|
|
367
|
+
sb = _sb()
|
|
368
|
+
did = _device_id(params["sender_id"], params["channel"])
|
|
369
|
+
|
|
370
|
+
rpc_params = {
|
|
371
|
+
"p_device_id": did,
|
|
372
|
+
"p_name": params["name"],
|
|
373
|
+
}
|
|
374
|
+
if params.get("lat") is not None:
|
|
375
|
+
rpc_params["p_lat"] = params["lat"]
|
|
376
|
+
if params.get("lng") is not None:
|
|
377
|
+
rpc_params["p_lng"] = params["lng"]
|
|
378
|
+
if params.get("starts_at"):
|
|
379
|
+
rpc_params["p_starts_at"] = params["starts_at"]
|
|
380
|
+
if params.get("ends_at"):
|
|
381
|
+
rpc_params["p_ends_at"] = params["ends_at"]
|
|
382
|
+
|
|
383
|
+
resp = sb.rpc("create_event", rpc_params).execute()
|
|
384
|
+
data = resp.data or {}
|
|
385
|
+
|
|
386
|
+
code = data.get("code", "")
|
|
387
|
+
return _ok({
|
|
388
|
+
"created": True,
|
|
389
|
+
"name": params["name"],
|
|
390
|
+
"code": code,
|
|
391
|
+
"url": f"{BASE_URL}/e/{code}",
|
|
392
|
+
"message": f"活动已创建!分享链接给参加的人:{BASE_URL}/e/{code}",
|
|
393
|
+
})
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
def handle_event_join(params: dict) -> str:
|
|
397
|
+
sb = _sb()
|
|
398
|
+
did = _device_id(params["sender_id"], params["channel"])
|
|
399
|
+
|
|
400
|
+
resp = sb.rpc("join_event", {
|
|
401
|
+
"p_device_id": did,
|
|
402
|
+
"p_code": params["code"],
|
|
403
|
+
}).execute()
|
|
404
|
+
data = resp.data or {}
|
|
405
|
+
|
|
406
|
+
if data.get("joined"):
|
|
407
|
+
return _ok({"joined": True, "name": data.get("name", ""), "message": f"已加入活动 \"{data.get('name', '')}\"!"})
|
|
408
|
+
return _ok({"joined": False, "error": data.get("error", "加入失败")})
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def handle_event_scan(params: dict) -> str:
|
|
412
|
+
sb = _sb()
|
|
413
|
+
did = _device_id(params["sender_id"], params["channel"])
|
|
414
|
+
|
|
415
|
+
resp = sb.rpc("event_participants_list", {
|
|
416
|
+
"p_code": params["code"],
|
|
417
|
+
}).execute()
|
|
418
|
+
results = resp.data or []
|
|
419
|
+
|
|
420
|
+
others = [p for p in results if p.get("device_id") != did]
|
|
421
|
+
|
|
422
|
+
if not others:
|
|
423
|
+
return _ok({"count": 0, "profiles": [], "message": "活动里还没有其他人。"})
|
|
424
|
+
|
|
425
|
+
global _last_ref_map
|
|
426
|
+
_last_ref_map = {}
|
|
427
|
+
profiles = []
|
|
428
|
+
for i, p in enumerate(others):
|
|
429
|
+
ref = str(i + 1)
|
|
430
|
+
_last_ref_map[ref] = p.get("device_id")
|
|
431
|
+
profiles.append({
|
|
432
|
+
"ref": ref,
|
|
433
|
+
"emoji": p.get("emoji") or "\ud83d\udc64",
|
|
434
|
+
"name": p.get("display_name") or "匿名",
|
|
435
|
+
"line1": p.get("line1"),
|
|
436
|
+
"line2": p.get("line2"),
|
|
437
|
+
"line3": p.get("line3"),
|
|
438
|
+
"source": "event",
|
|
439
|
+
})
|
|
440
|
+
|
|
441
|
+
return _ok({
|
|
442
|
+
"count": len(profiles),
|
|
443
|
+
"profiles": profiles,
|
|
444
|
+
"instruction": "这些是活动参加者。根据你对用户的了解,推荐值得认识的人。使用 ref 编号引用。",
|
|
445
|
+
})
|
|
446
|
+
|
|
447
|
+
|
|
280
448
|
def handle_bind(params: dict) -> str:
|
|
281
449
|
sb = _sb()
|
|
282
450
|
did = _device_id(params["sender_id"], params["channel"])
|
package/package.json
CHANGED
package/skill/SKILL.md
CHANGED
|
@@ -122,6 +122,20 @@ Get today's global recommendation — the person most similar to you worldwide.
|
|
|
122
122
|
- If all users have been recommended, returns a message saying "wait for new people"
|
|
123
123
|
- Use this in the daily cron job, or when user asks "find someone interesting globally"
|
|
124
124
|
|
|
125
|
+
### `antenna_pass`
|
|
126
|
+
Pass/skip a person. They won't be recommended again.
|
|
127
|
+
- `sender_id`, `channel`: from context
|
|
128
|
+
- `ref`: ref number from scan/discover results (e.g. '1')
|
|
129
|
+
- `target_device_id`: device ID (use ref instead when possible)
|
|
130
|
+
- Use when the user says "skip", "pass", "not interested", etc.
|
|
131
|
+
|
|
132
|
+
### `antenna_checkin`
|
|
133
|
+
Check in at a location — update your position so others can find you when they scan.
|
|
134
|
+
- `lat`, `lng`: coordinates (required)
|
|
135
|
+
- `sender_id`, `channel`: from context
|
|
136
|
+
- `place_name`: optional name of the place
|
|
137
|
+
- Use when the user says "I'm at XX" or wants to be discoverable without scanning others
|
|
138
|
+
|
|
125
139
|
## Data Transparency — what Antenna sends
|
|
126
140
|
|
|
127
141
|
Antenna only communicates with Supabase (bcudjloikmpcqwcptuyd.supabase.co) via HTTPS.
|