antenna-fyi 1.3.6 → 1.3.7
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/lib/cli.js +28 -2
- package/lib/core.js +27 -0
- package/lib/hermes-plugin/__init__.py +16 -0
- package/lib/hermes-plugin/plugin.yaml +1 -1
- package/lib/hermes-plugin/schemas.py +17 -0
- package/lib/hermes-plugin/tools.py +15 -0
- package/lib/mcp.js +22 -0
- package/package.json +1 -1
- package/skill/SKILL.md +10 -0
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, createEvent, endEvent, eventCheckin, joinEvent, eventScan, pass as passUser, uploadEventImage, updateEvent, approveParticipant, rejectParticipant, addCohost, getClient } from "./core.js";
|
|
3
|
+
import { scan, getProfile, setProfile, accept, checkMatches, checkin, createBindToken, discover, createEvent, endEvent, eventCheckin, joinEvent, eventScan, pass as passUser, uploadEventImage, updateEvent, approveParticipant, rejectParticipant, addCohost, sendEventMessage, getMyEventMessages, getClient } from "./core.js";
|
|
4
4
|
import { createInterface } from "readline";
|
|
5
5
|
import { existsSync, mkdirSync, copyFileSync, readFileSync, writeFileSync, unlinkSync, renameSync } from "fs";
|
|
6
6
|
import path from "path";
|
|
@@ -273,13 +273,25 @@ export async function handleEvent(f) {
|
|
|
273
273
|
return;
|
|
274
274
|
}
|
|
275
275
|
|
|
276
|
+
if (f.message || f.msg) {
|
|
277
|
+
if (!f.code || !f.id || !(f.message || f.msg)) return console.error("Usage: antenna event --message 'Hello everyone' --code abc123 --id <platform>:<user_id> [--ref 1]");
|
|
278
|
+
const result = await sendEventMessage({ code: f.code, device_id: f.id, message: f.message || f.msg, target_ref: f.ref || undefined });
|
|
279
|
+
if (result.sent) {
|
|
280
|
+
console.log(`\n✅ Message sent${result.broadcast ? ' (broadcast to all)' : ''}\n`);
|
|
281
|
+
} else {
|
|
282
|
+
console.log(`\n❌ ${result.error}\n`);
|
|
283
|
+
}
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
|
|
276
287
|
console.log(`Usage:
|
|
277
288
|
antenna event --create --name 'AI Meetup' --starts-at '...' --ends-at '...' [--id <platform>:<user_id>] [--lat 34.05 --lng -118.25] [--desc 'description'] [--og-image 'url'] [--requires-approval] [--screening-questions 'Q1|Q2']
|
|
278
289
|
antenna event --join --code abc123 --id <platform>:<user_id>
|
|
279
290
|
antenna event --scan --code abc123 [--id <platform>:<user_id>]
|
|
280
291
|
antenna event --checkin --code abc123 --id <platform>:<user_id> [--lat 34.05 --lng -118.24]
|
|
281
292
|
antenna event --end --code abc123 --id <platform>:<user_id>
|
|
282
|
-
antenna event --upload-image --code abc123 --file /path/to/image.png
|
|
293
|
+
antenna event --upload-image --code abc123 --file /path/to/image.png
|
|
294
|
+
antenna event --message 'Hello everyone' --code abc123 --id <platform>:<user_id> [--ref 1]`);
|
|
283
295
|
}
|
|
284
296
|
|
|
285
297
|
export async function handleBind(f) {
|
|
@@ -855,6 +867,20 @@ export async function handleWatch(f) {
|
|
|
855
867
|
}
|
|
856
868
|
}
|
|
857
869
|
} catch { /* silent */ }
|
|
870
|
+
|
|
871
|
+
// Poll event messages from hosts
|
|
872
|
+
try {
|
|
873
|
+
const { data: msgs } = await sb.rpc("get_my_event_messages", { p_device_id: id });
|
|
874
|
+
for (const msg of (msgs || [])) {
|
|
875
|
+
const key = `evtmsg:${msg.event_id}:${msg.created_at}`;
|
|
876
|
+
if (!notified.has(key)) {
|
|
877
|
+
notified.add(key);
|
|
878
|
+
saveNotified(notified);
|
|
879
|
+
const role = msg.sender_role === 'creator' ? '组织者' : '协办';
|
|
880
|
+
pushNotify(`📢 来自「${msg.event_name}」${role} ${msg.sender_emoji || ''} ${msg.sender_name}: ${msg.message}`);
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
} catch { /* silent */ }
|
|
858
884
|
}, 2 * 60 * 1000);
|
|
859
885
|
|
|
860
886
|
// Handle Ctrl+C
|
package/lib/core.js
CHANGED
|
@@ -686,3 +686,30 @@ export async function createBindToken({ device_id, purpose, event_code, supabase
|
|
|
686
686
|
: "发送这个链接给用户,在手机浏览器打开即可共享位置。",
|
|
687
687
|
};
|
|
688
688
|
}
|
|
689
|
+
|
|
690
|
+
// ─── sendEventMessage (host → participants) ─────────────────────────
|
|
691
|
+
|
|
692
|
+
export async function sendEventMessage({ code, device_id, message, target_ref, supabaseUrl, supabaseKey }) {
|
|
693
|
+
const sb = getClient(supabaseUrl, supabaseKey);
|
|
694
|
+
const params = {
|
|
695
|
+
p_code: code,
|
|
696
|
+
p_device_id: device_id,
|
|
697
|
+
p_message: message,
|
|
698
|
+
};
|
|
699
|
+
if (target_ref) params.p_target_ref = target_ref;
|
|
700
|
+
const { data, error } = await sb.rpc("send_event_message", params);
|
|
701
|
+
if (error) throw new Error(error.message);
|
|
702
|
+
return data;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
// ─── getMyEventMessages ──────────────────────────────────────────────
|
|
706
|
+
|
|
707
|
+
export async function getMyEventMessages({ device_id, supabaseUrl, supabaseKey }) {
|
|
708
|
+
const sb = getClient(supabaseUrl, supabaseKey);
|
|
709
|
+
const { data, error } = await sb.rpc("get_my_event_messages", { p_device_id: device_id });
|
|
710
|
+
if (error) throw new Error(error.message);
|
|
711
|
+
return {
|
|
712
|
+
messages: data || [],
|
|
713
|
+
count: (data || []).length,
|
|
714
|
+
};
|
|
715
|
+
}
|
|
@@ -32,6 +32,7 @@ from .tools import (
|
|
|
32
32
|
handle_event_approve,
|
|
33
33
|
handle_event_reject,
|
|
34
34
|
handle_event_add_host,
|
|
35
|
+
handle_event_message,
|
|
35
36
|
_sb,
|
|
36
37
|
_device_id,
|
|
37
38
|
_my_device_ids,
|
|
@@ -55,6 +56,7 @@ from .schemas import (
|
|
|
55
56
|
EVENT_APPROVE_SCHEMA,
|
|
56
57
|
EVENT_REJECT_SCHEMA,
|
|
57
58
|
EVENT_ADD_HOST_SCHEMA,
|
|
59
|
+
EVENT_MESSAGE_SCHEMA,
|
|
58
60
|
)
|
|
59
61
|
import re
|
|
60
62
|
import time
|
|
@@ -89,6 +91,7 @@ def register(ctx):
|
|
|
89
91
|
ctx.register_tool("antenna_event_approve", EVENT_APPROVE_SCHEMA, handle_event_approve)
|
|
90
92
|
ctx.register_tool("antenna_event_reject", EVENT_REJECT_SCHEMA, handle_event_reject)
|
|
91
93
|
ctx.register_tool("antenna_event_add_host", EVENT_ADD_HOST_SCHEMA, handle_event_add_host)
|
|
94
|
+
ctx.register_tool("antenna_event_message", EVENT_MESSAGE_SCHEMA, handle_event_message)
|
|
92
95
|
|
|
93
96
|
# ── Hook: auto-detect location + check web GPS events ─────────
|
|
94
97
|
def on_pre_llm(messages, **kwargs):
|
|
@@ -179,6 +182,19 @@ def register(ctx):
|
|
|
179
182
|
hints.append(
|
|
180
183
|
f"[Antenna] ❌ 你的申请未通过「{name}」"
|
|
181
184
|
)
|
|
185
|
+
|
|
186
|
+
# Check event messages from hosts
|
|
187
|
+
msg_resp = sb.rpc("get_my_event_messages", {"p_device_id": did}).execute()
|
|
188
|
+
for msg in (msg_resp.data or []):
|
|
189
|
+
key = f"evtmsg:{msg.get('event_id')}:{msg.get('created_at')}"
|
|
190
|
+
if key in _notified_match_keys:
|
|
191
|
+
continue
|
|
192
|
+
_notified_match_keys.add(key)
|
|
193
|
+
role_label = '组织者' if msg.get('sender_role') == 'creator' else '协办'
|
|
194
|
+
hints.append(
|
|
195
|
+
f"[Antenna] 📢 来自「{msg.get('event_name')}」{role_label} {msg.get('sender_emoji', '')} {msg.get('sender_name', '')}: {msg.get('message', '')}\n"
|
|
196
|
+
f"请立即通知用户这条消息。"
|
|
197
|
+
)
|
|
182
198
|
except Exception:
|
|
183
199
|
pass
|
|
184
200
|
now = time.time()
|
|
@@ -359,3 +359,20 @@ EVENT_ADD_HOST_SCHEMA = {
|
|
|
359
359
|
"required": ["code", "sender_id", "channel", "ref", "chat_id"],
|
|
360
360
|
},
|
|
361
361
|
}
|
|
362
|
+
|
|
363
|
+
EVENT_MESSAGE_SCHEMA = {
|
|
364
|
+
"name": "antenna_event_message",
|
|
365
|
+
"description": "Send a message to event participants. Only creator or co-host can send. Omit ref to broadcast to all.",
|
|
366
|
+
"parameters": {
|
|
367
|
+
"type": "object",
|
|
368
|
+
"properties": {
|
|
369
|
+
"code": {"type": "string", "description": "Event code"},
|
|
370
|
+
"sender_id": {"type": "string"},
|
|
371
|
+
"channel": {"type": "string"},
|
|
372
|
+
"chat_id": {"type": "string", "description": "REQUIRED for notifications. Pass chat/channel ID from message context."},
|
|
373
|
+
"message": {"type": "string", "description": "Message to send to participants"},
|
|
374
|
+
"ref": {"type": "string", "description": "Ref number of specific participant (omit for broadcast)"},
|
|
375
|
+
},
|
|
376
|
+
"required": ["code", "sender_id", "channel", "message", "chat_id"],
|
|
377
|
+
},
|
|
378
|
+
}
|
|
@@ -680,3 +680,18 @@ def handle_event_add_host(params: dict) -> str:
|
|
|
680
680
|
"p_code": params["code"], "p_device_id": did, "p_target_ref": params["ref"],
|
|
681
681
|
}).execute()
|
|
682
682
|
return _ok(resp.data or {"error": "add_cohost failed"})
|
|
683
|
+
|
|
684
|
+
|
|
685
|
+
def handle_event_message(params: dict) -> str:
|
|
686
|
+
"""Send a message to event participants. Only creator or co-host can send."""
|
|
687
|
+
sb = _sb()
|
|
688
|
+
did = _device_id(params["sender_id"], params["channel"], params.get("chat_id"))
|
|
689
|
+
rpc_params = {
|
|
690
|
+
"p_code": params["code"],
|
|
691
|
+
"p_device_id": did,
|
|
692
|
+
"p_message": params["message"],
|
|
693
|
+
}
|
|
694
|
+
if params.get("ref"):
|
|
695
|
+
rpc_params["p_target_ref"] = params["ref"]
|
|
696
|
+
resp = sb.rpc("send_event_message", rpc_params).execute()
|
|
697
|
+
return _ok(resp.data or {"error": "send_event_message failed"})
|
package/lib/mcp.js
CHANGED
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
approveParticipant,
|
|
24
24
|
rejectParticipant,
|
|
25
25
|
addCohost,
|
|
26
|
+
sendEventMessage,
|
|
26
27
|
deriveDeviceId,
|
|
27
28
|
} from "./core.js";
|
|
28
29
|
|
|
@@ -478,6 +479,27 @@ export async function startMcpServer() {
|
|
|
478
479
|
}
|
|
479
480
|
);
|
|
480
481
|
|
|
482
|
+
// ─── antenna_event_message ────────────────────────────────
|
|
483
|
+
|
|
484
|
+
server.tool(
|
|
485
|
+
"antenna_event_message",
|
|
486
|
+
"Send a message to event participants. Only creator or co-host can send. Omit ref to broadcast to all.",
|
|
487
|
+
{
|
|
488
|
+
code: z.string().describe("Event code"),
|
|
489
|
+
sender_id: z.string().describe("The sender's user ID"),
|
|
490
|
+
channel: z.string().describe("Channel name"),
|
|
491
|
+
chat_id: z.string().describe("REQUIRED for notifications"),
|
|
492
|
+
message: z.string().describe("Message to send"),
|
|
493
|
+
ref: z.string().optional().describe("Ref number of specific participant (omit for broadcast)"),
|
|
494
|
+
},
|
|
495
|
+
async ({ code, sender_id, channel, chat_id, message, ref }) => {
|
|
496
|
+
try {
|
|
497
|
+
const result = await sendEventMessage({ code, device_id: deriveDeviceId(sender_id, channel), message, target_ref: ref });
|
|
498
|
+
return jsonResult(result);
|
|
499
|
+
} catch (e) { return jsonResult({ error: e.message }); }
|
|
500
|
+
}
|
|
501
|
+
);
|
|
502
|
+
|
|
481
503
|
const transport = new StdioServerTransport();
|
|
482
504
|
await server.connect(transport);
|
|
483
505
|
}
|
package/package.json
CHANGED
package/skill/SKILL.md
CHANGED
|
@@ -388,6 +388,16 @@ Add a co-host to the event. Only creator can add.
|
|
|
388
388
|
- `chat_id`: REQUIRED for notifications
|
|
389
389
|
- `ref`: participant ref number to promote to co-host
|
|
390
390
|
|
|
391
|
+
### `antenna_event_message`
|
|
392
|
+
Send a message to event participants. Only creator or co-host can send.
|
|
393
|
+
- `code`: event code
|
|
394
|
+
- `sender_id`, `channel`: from context
|
|
395
|
+
- `chat_id`: REQUIRED for notifications
|
|
396
|
+
- `message`: the message text
|
|
397
|
+
- `ref`: optional — ref number of specific participant. Omit to broadcast to all active participants.
|
|
398
|
+
- Use when the host needs to notify participants about logistics, changes, or requests (e.g. "please share your WeChat in your profile").
|
|
399
|
+
- One-way: participants receive the message but cannot reply through this channel.
|
|
400
|
+
|
|
391
401
|
---
|
|
392
402
|
|
|
393
403
|
## Event Behavior Guide
|