antenna-openclaw-plugin 1.2.20 → 1.2.22
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 +28 -5
- package/package.json +1 -1
- package/skills/antenna/SKILL.md +38 -0
package/index.ts
CHANGED
|
@@ -124,24 +124,35 @@ function cronJobId(deviceA: string, deviceB: string): string {
|
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
/** Send a real-time notification to a user via openclaw message send */
|
|
127
|
-
function notifyUser(
|
|
127
|
+
async function notifyUser(
|
|
128
128
|
channel: string,
|
|
129
129
|
userId: string,
|
|
130
130
|
message: string,
|
|
131
131
|
logger: any,
|
|
132
|
-
): void {
|
|
132
|
+
): Promise<void> {
|
|
133
133
|
const deviceId = `${channel}:${userId}`;
|
|
134
|
-
|
|
134
|
+
let chatId = _channelContext.get(deviceId);
|
|
135
|
+
|
|
136
|
+
// Fallback: read from DB if not in memory
|
|
137
|
+
if (!chatId) {
|
|
138
|
+
try {
|
|
139
|
+
const cfg = getConfig(api);
|
|
140
|
+
const sb = getSupabase(cfg);
|
|
141
|
+
const { data } = await sb.rpc("get_profile", { p_device_id: deviceId });
|
|
142
|
+
if (data?.last_chat_id) {
|
|
143
|
+
chatId = data.last_chat_id;
|
|
144
|
+
_channelContext.set(deviceId, chatId);
|
|
145
|
+
}
|
|
146
|
+
} catch {}
|
|
147
|
+
}
|
|
135
148
|
|
|
136
149
|
try {
|
|
137
150
|
if (chatId) {
|
|
138
|
-
// Use message send with known chat context
|
|
139
151
|
execSync(
|
|
140
152
|
`openclaw message send --channel ${channel} --target ${chatId} -m ${JSON.stringify(message)}`,
|
|
141
153
|
{ timeout: 30_000, encoding: "utf-8" },
|
|
142
154
|
);
|
|
143
155
|
} else {
|
|
144
|
-
// Fallback: try deliver
|
|
145
156
|
execSync(
|
|
146
157
|
`openclaw agent` +
|
|
147
158
|
` --message ${JSON.stringify(message)}` +
|
|
@@ -156,6 +167,12 @@ function notifyUser(
|
|
|
156
167
|
logger.warn(`Antenna: notify failed for ${channel}:${userId}: ${err.message}`);
|
|
157
168
|
}
|
|
158
169
|
}
|
|
170
|
+
}
|
|
171
|
+
logger.info(`Antenna: notified ${channel}:${userId} (chat=${chatId || 'deliver'})`);
|
|
172
|
+
} catch (err: any) {
|
|
173
|
+
logger.warn(`Antenna: notify failed for ${channel}:${userId}: ${err.message}`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
159
176
|
|
|
160
177
|
function startFollowUpCron(
|
|
161
178
|
deviceId: string,
|
|
@@ -1412,6 +1429,12 @@ export default function register(api: any) {
|
|
|
1412
1429
|
const deviceId = `${ch}:${senderId}`;
|
|
1413
1430
|
_channelContext.set(deviceId, chatId);
|
|
1414
1431
|
_knownDeviceIds.add(deviceId);
|
|
1432
|
+
// Persist to DB
|
|
1433
|
+
try {
|
|
1434
|
+
const cfg = getConfig(api);
|
|
1435
|
+
const sb = getSupabase(cfg);
|
|
1436
|
+
sb.rpc("upsert_profile", { p_device_id: deviceId, p_last_chat_id: chatId }).then(() => {}).catch(() => {});
|
|
1437
|
+
} catch {}
|
|
1415
1438
|
}
|
|
1416
1439
|
|
|
1417
1440
|
// --- Auto-scan on location ---
|
package/package.json
CHANGED
package/skills/antenna/SKILL.md
CHANGED
|
@@ -365,3 +365,41 @@ Add a co-host to the event. Only creator can add.
|
|
|
365
365
|
- `code`: event code
|
|
366
366
|
- `sender_id`, `channel`: from context
|
|
367
367
|
- `ref`: participant ref number to promote to co-host
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
## Part 2: Event Behavior Guide
|
|
372
|
+
|
|
373
|
+
### Creating an event
|
|
374
|
+
Collect info through conversation (ask one by one, don't dump all at once):
|
|
375
|
+
1. **Event name** (required) — "活动叫什么名字?"
|
|
376
|
+
2. **Description** — "简单描述一下这个活动?"
|
|
377
|
+
3. **Time** — "什么时候开始?大概多长?" (convert to starts_at / ends_at ISO strings)
|
|
378
|
+
4. **Location** — "活动在哪里?" If user gives an address, geocode it. If vague, generate a bind link after creation.
|
|
379
|
+
5. **Approval** — "需要审批参与者吗?" If yes:
|
|
380
|
+
6. **Screening questions** — "你想问报名者什么问题?" Collect as a list.
|
|
381
|
+
|
|
382
|
+
Then call `antenna_event_create` with all collected info.
|
|
383
|
+
If no GPS, call `antenna_bind(purpose="event", event_code=CODE)` and send the link.
|
|
384
|
+
Share the event URL with the user.
|
|
385
|
+
|
|
386
|
+
### Joining an event
|
|
387
|
+
1. Extract the code from `antenna.fyi/events/CODE`
|
|
388
|
+
2. Call `antenna_event_join(code)` — this checks everything:
|
|
389
|
+
- If no profile → "Create a profile first"
|
|
390
|
+
- If event requires approval and no `application_context` provided → returns `needs_screening: true` + `screening_questions`
|
|
391
|
+
- If screening questions returned: **ask the user each question**, collect answers, then call `antenna_event_join(code, application_context="answers")` again
|
|
392
|
+
- If `status: pending` → "waiting for organizer approval"
|
|
393
|
+
- If `status: active` → user is in! Auto check-in if event started + GPS within 1km.
|
|
394
|
+
|
|
395
|
+
### Scanning an event
|
|
396
|
+
1. Call `antenna_event_scan(code)`
|
|
397
|
+
2. Hosts see pending participants with `application_context` (screening answers)
|
|
398
|
+
3. Recommend who to meet based on user's interests
|
|
399
|
+
4. Creator/co-host appears with organizer badge
|
|
400
|
+
|
|
401
|
+
### Approving/rejecting participants
|
|
402
|
+
Only creator or co-host can approve/reject:
|
|
403
|
+
- `antenna_event_approve(code, ref)` → participant becomes active
|
|
404
|
+
- `antenna_event_reject(code, ref)` → participant is rejected
|
|
405
|
+
- Notifications are sent automatically to the applicant
|