antenna-openclaw-plugin 1.3.28 → 1.3.29

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.
@@ -0,0 +1,177 @@
1
+ -- ================================================================
2
+ -- Drift Bottles (漂流瓶) - Migration for Antenna
3
+ -- Run this on Supabase SQL Editor
4
+ -- ================================================================
5
+
6
+ -- Table
7
+ CREATE TABLE IF NOT EXISTS drift_bottles (
8
+ id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
9
+ sender_device_id text NOT NULL,
10
+ message text NOT NULL CHECK (char_length(message) <= 500),
11
+ picked_by_device_id text,
12
+ reply text CHECK (reply IS NULL OR char_length(reply) <= 500),
13
+ reply_read boolean DEFAULT false,
14
+ created_at timestamptz DEFAULT now(),
15
+ picked_at timestamptz,
16
+ replied_at timestamptz,
17
+ expired boolean DEFAULT false
18
+ );
19
+
20
+ -- Indexes
21
+ CREATE INDEX IF NOT EXISTS idx_drift_bottles_sender ON drift_bottles(sender_device_id);
22
+ CREATE INDEX IF NOT EXISTS idx_drift_bottles_picked_by ON drift_bottles(picked_by_device_id);
23
+ CREATE INDEX IF NOT EXISTS idx_drift_bottles_available ON drift_bottles(picked_by_device_id) WHERE picked_by_device_id IS NULL AND expired = false;
24
+
25
+ -- RPC: throw_drift_bottle
26
+ CREATE OR REPLACE FUNCTION throw_drift_bottle(p_device_id text, p_message text)
27
+ RETURNS json LANGUAGE plpgsql SECURITY DEFINER AS $$
28
+ DECLARE
29
+ v_id uuid;
30
+ v_count bigint;
31
+ BEGIN
32
+ INSERT INTO drift_bottles (sender_device_id, message)
33
+ VALUES (p_device_id, p_message)
34
+ RETURNING id INTO v_id;
35
+
36
+ SELECT count(*) INTO v_count FROM drift_bottles WHERE sender_device_id = p_device_id AND expired = false;
37
+
38
+ RETURN json_build_object('bottle_id', v_id, 'total_thrown', v_count);
39
+ END;
40
+ $$;
41
+
42
+ -- RPC: pick_drift_bottle
43
+ CREATE OR REPLACE FUNCTION pick_drift_bottle(p_device_id text)
44
+ RETURNS json LANGUAGE plpgsql SECURITY DEFINER AS $$
45
+ DECLARE
46
+ v_bottle record;
47
+ BEGIN
48
+ -- Check if user already has an unpicked bottle waiting for reply
49
+ SELECT id INTO v_bottle FROM drift_bottles
50
+ WHERE picked_by_device_id = p_device_id AND reply IS NULL AND expired = false
51
+ LIMIT 1;
52
+
53
+ IF FOUND THEN
54
+ RETURN json_build_object('error', 'you_have_unreplied_bottle', 'bottle_id', v_bottle.id,
55
+ 'message', '你还有一个捡起来的漂流瓶没回复呢。先回复那个吧!');
56
+ END IF;
57
+
58
+ -- Pick a random bottle (not your own, not expired, not picked)
59
+ SELECT * INTO v_bottle FROM drift_bottles
60
+ WHERE picked_by_device_id IS NULL
61
+ AND sender_device_id != p_device_id
62
+ AND expired = false
63
+ AND created_at > now() - interval '7 days'
64
+ ORDER BY random()
65
+ LIMIT 1;
66
+
67
+ IF NOT FOUND THEN
68
+ RETURN json_build_object('found', false, 'message', '海上暂时没有漂流瓶了。试试自己扔一个?');
69
+ END IF;
70
+
71
+ UPDATE drift_bottles SET picked_by_device_id = p_device_id, picked_at = now()
72
+ WHERE id = v_bottle.id;
73
+
74
+ RETURN json_build_object(
75
+ 'found', true,
76
+ 'bottle_id', v_bottle.id,
77
+ 'message', v_bottle.message,
78
+ 'thrown_at', v_bottle.created_at
79
+ );
80
+ END;
81
+ $$;
82
+
83
+ -- RPC: reply_drift_bottle
84
+ CREATE OR REPLACE FUNCTION reply_drift_bottle(p_bottle_id uuid, p_device_id text, p_reply text)
85
+ RETURNS json LANGUAGE plpgsql SECURITY DEFINER AS $$
86
+ DECLARE
87
+ v_bottle record;
88
+ BEGIN
89
+ SELECT * INTO v_bottle FROM drift_bottles WHERE id = p_bottle_id;
90
+
91
+ IF NOT FOUND THEN
92
+ RETURN json_build_object('error', 'not_found', 'message', '找不到这个漂流瓶。');
93
+ END IF;
94
+
95
+ IF v_bottle.picked_by_device_id != p_device_id THEN
96
+ RETURN json_build_object('error', 'not_yours', 'message', '这不是你捡的漂流瓶。');
97
+ END IF;
98
+
99
+ IF v_bottle.reply IS NOT NULL THEN
100
+ RETURN json_build_object('error', 'already_replied', 'message', '你已经回复过这个漂流瓶了。');
101
+ END IF;
102
+
103
+ UPDATE drift_bottles SET reply = p_reply, replied_at = now() WHERE id = p_bottle_id;
104
+
105
+ RETURN json_build_object('success', true, 'message', '回复已放入瓶中,漂回去了 🌊');
106
+ END;
107
+ $$;
108
+
109
+ -- RPC: check_drift_bottles
110
+ CREATE OR REPLACE FUNCTION check_drift_bottles(p_device_id text)
111
+ RETURNS json LANGUAGE plpgsql SECURITY DEFINER AS $$
112
+ DECLARE
113
+ v_new_replies json;
114
+ v_unreplied json;
115
+ BEGIN
116
+ -- New replies to bottles I threw
117
+ SELECT json_agg(json_build_object(
118
+ 'bottle_id', id,
119
+ 'my_message', message,
120
+ 'reply', reply,
121
+ 'replied_at', replied_at
122
+ )) INTO v_new_replies
123
+ FROM drift_bottles
124
+ WHERE sender_device_id = p_device_id
125
+ AND reply IS NOT NULL
126
+ AND reply_read = false;
127
+
128
+ -- Mark them as read
129
+ UPDATE drift_bottles SET reply_read = true
130
+ WHERE sender_device_id = p_device_id AND reply IS NOT NULL AND reply_read = false;
131
+
132
+ -- Bottles I picked but haven't replied to
133
+ SELECT json_agg(json_build_object(
134
+ 'bottle_id', id,
135
+ 'message', message,
136
+ 'picked_at', picked_at
137
+ )) INTO v_unreplied
138
+ FROM drift_bottles
139
+ WHERE picked_by_device_id = p_device_id AND reply IS NULL AND expired = false;
140
+
141
+ RETURN json_build_object(
142
+ 'new_replies', COALESCE(v_new_replies, '[]'::json),
143
+ 'unreplied_picks', COALESCE(v_unreplied, '[]'::json)
144
+ );
145
+ END;
146
+ $$;
147
+
148
+ -- RPC: get_my_bottles
149
+ CREATE OR REPLACE FUNCTION get_my_bottles(p_device_id text)
150
+ RETURNS json LANGUAGE plpgsql SECURITY DEFINER AS $$
151
+ DECLARE
152
+ v_bottles json;
153
+ BEGIN
154
+ SELECT json_agg(json_build_object(
155
+ 'bottle_id', id,
156
+ 'message', message,
157
+ 'status', CASE
158
+ WHEN reply IS NOT NULL THEN 'replied'
159
+ WHEN picked_by_device_id IS NOT NULL THEN 'picked'
160
+ ELSE 'floating'
161
+ END,
162
+ 'reply', reply,
163
+ 'created_at', created_at,
164
+ 'replied_at', replied_at
165
+ ) ORDER BY created_at DESC) INTO v_bottles
166
+ FROM drift_bottles
167
+ WHERE sender_device_id = p_device_id AND expired = false;
168
+
169
+ RETURN json_build_object('bottles', COALESCE(v_bottles, '[]'::json));
170
+ END;
171
+ $$;
172
+
173
+ -- Enable RLS
174
+ ALTER TABLE drift_bottles ENABLE ROW LEVEL SECURITY;
175
+
176
+ -- Policy: allow all via RPC (functions are SECURITY DEFINER)
177
+ CREATE POLICY "Allow anon via RPC" ON drift_bottles FOR ALL USING (true);
@@ -23,8 +23,8 @@
23
23
  },
24
24
  "matchExpiryHours": {
25
25
  "type": "number",
26
- "description": "Hours before match visibility decays (time decay model)",
27
- "default": 168
26
+ "description": "Hours before a match expires",
27
+ "default": 24
28
28
  },
29
29
  "maxMatches": {
30
30
  "type": "number",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "antenna-openclaw-plugin",
3
- "version": "1.3.28",
3
+ "version": "1.3.29",
4
4
  "description": "Antenna \u2014 agent-mediated nearby people discovery for OpenClaw",
5
5
  "openclaw": {
6
6
  "extensions": [
@@ -10,4 +10,4 @@
10
10
  "dependencies": {
11
11
  "@supabase/supabase-js": "^2.49.0"
12
12
  }
13
- }
13
+ }