@tloncorp/tlon-skill 0.1.11 → 0.2.1
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/README.md +45 -7
- package/SKILL.md +137 -10
- package/package.json +8 -7
- package/references/hooks-examples/auto-react.hoon +24 -0
- package/references/hooks-examples/delete-old-posts.hoon +18 -0
- package/references/hooks-examples/word-filter.hoon +52 -0
- package/references/hooks.md +366 -0
- package/references/urbit-api.md +113 -0
package/README.md
CHANGED
|
@@ -26,7 +26,10 @@ curl -L https://registry.npmjs.org/@tloncorp/tlon-skill-linux-x64/-/tlon-skill-l
|
|
|
26
26
|
|
|
27
27
|
**Option 1: CLI flags (highest priority)**
|
|
28
28
|
```bash
|
|
29
|
-
#
|
|
29
|
+
# Cookie-based auth (fastest - ship parsed from cookie)
|
|
30
|
+
tlon --url https://your-ship.tlon.network --cookie "urbauth-~your-ship=0v..." contacts self
|
|
31
|
+
|
|
32
|
+
# Code-based auth (requires all three)
|
|
30
33
|
tlon --url https://your-ship.tlon.network --ship ~your-ship --code sampel-ticlyt-migfun-falmel contacts self
|
|
31
34
|
|
|
32
35
|
# Or use a config file
|
|
@@ -35,11 +38,20 @@ tlon --config ~/ships/my-ship.json contacts self
|
|
|
35
38
|
|
|
36
39
|
Config file format:
|
|
37
40
|
```json
|
|
41
|
+
// Cookie-based (ship derived from cookie)
|
|
42
|
+
{"url": "https://your-ship.tlon.network", "cookie": "urbauth-~your-ship=0v..."}
|
|
43
|
+
|
|
44
|
+
// Code-based
|
|
38
45
|
{"url": "https://your-ship.tlon.network", "ship": "~your-ship", "code": "sampel-ticlyt-migfun-falmel"}
|
|
39
46
|
```
|
|
40
47
|
|
|
41
48
|
**Option 2: Environment variables**
|
|
42
49
|
```bash
|
|
50
|
+
# Cookie-based (ship derived from cookie)
|
|
51
|
+
export URBIT_URL="https://your-ship.tlon.network"
|
|
52
|
+
export URBIT_COOKIE="urbauth-~your-ship=0v..."
|
|
53
|
+
|
|
54
|
+
# Code-based
|
|
43
55
|
export URBIT_URL="https://your-ship.tlon.network"
|
|
44
56
|
export URBIT_SHIP="~your-ship"
|
|
45
57
|
export URBIT_CODE="sampel-ticlyt-migfun-falmel"
|
|
@@ -49,16 +61,41 @@ export URBIT_CODE="sampel-ticlyt-migfun-falmel"
|
|
|
49
61
|
|
|
50
62
|
If you have OpenClaw configured with a Tlon channel, credentials are loaded automatically.
|
|
51
63
|
|
|
64
|
+
## Cookie Caching
|
|
65
|
+
|
|
66
|
+
The skill automatically caches auth cookies to `~/.tlon/cache/<ship>.json` after successful authentication.
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# First time - auth and cache
|
|
70
|
+
$ tlon --url https://zod.tlon.network --ship ~zod --code abcd-efgh contacts self
|
|
71
|
+
Note: Credentials cached for ~zod. Next time just run: tlon <command>
|
|
72
|
+
|
|
73
|
+
# After that - no flags needed!
|
|
74
|
+
$ tlon contacts self
|
|
75
|
+
|
|
76
|
+
# Multiple cached ships? Specify which one:
|
|
77
|
+
$ tlon --ship ~zod contacts self
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Clear cache: `rm ~/.tlon/cache/*.json`
|
|
81
|
+
|
|
82
|
+
## Cookie vs Code Authentication
|
|
83
|
+
|
|
84
|
+
- **Cookie-based auth**: Uses a pre-authenticated session cookie. Faster since it skips login.
|
|
85
|
+
- **Code-based auth**: Performs a login request to get a session cookie.
|
|
86
|
+
|
|
87
|
+
The ship name is embedded in the cookie (`urbauth-~ship=...`), so you don't need to specify it separately with cookie auth.
|
|
88
|
+
|
|
52
89
|
## Multi-Ship Usage
|
|
53
90
|
|
|
54
|
-
If you have credentials for multiple ships, you can operate on behalf of any of them
|
|
91
|
+
If you have credentials for multiple ships, you can operate on behalf of any of them:
|
|
55
92
|
|
|
56
93
|
```bash
|
|
57
94
|
# Act as a different ship
|
|
58
95
|
tlon --config ~/ships/bot.json channels groups
|
|
59
96
|
|
|
60
97
|
# Or pass credentials directly
|
|
61
|
-
tlon --url https://bot.tlon.network --
|
|
98
|
+
tlon --url https://bot.tlon.network --cookie "urbauth-~bot=0v..." contacts self
|
|
62
99
|
```
|
|
63
100
|
|
|
64
101
|
## Usage
|
|
@@ -82,11 +119,12 @@ tlon groups create "My Group" --description "A cool group"
|
|
|
82
119
|
|
|
83
120
|
## Features
|
|
84
121
|
|
|
85
|
-
- **Activity**: Mentions, replies, unreads
|
|
86
|
-
- **Channels**: List DMs, group DMs, subscribed groups
|
|
122
|
+
- **Activity**: Mentions, replies, unreads (with nicknames)
|
|
123
|
+
- **Channels**: List DMs, group DMs, subscribed groups (nicknames shown), reader/writer permissions
|
|
87
124
|
- **Contacts**: List, get, update profiles
|
|
88
|
-
- **Groups**: Create, join, invite, roles, privacy
|
|
89
|
-
- **
|
|
125
|
+
- **Groups**: Create, join, invite, roles, privacy (member nicknames shown)
|
|
126
|
+
- **Hooks**: Manage channel hooks (add, edit, delete, order, config, cron)
|
|
127
|
+
- **Messages**: History, search (author nicknames shown)
|
|
90
128
|
- **DMs**: Send, react, accept/decline
|
|
91
129
|
- **Posts**: React, delete
|
|
92
130
|
- **Notebook**: Post to diary channels
|
package/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: tlon
|
|
3
|
-
description: Interact with Tlon/Urbit API. Use for contacts (get/update profiles), listing channels/groups, fetching message history, posting to channels, sending DMs, and
|
|
3
|
+
description: Interact with Tlon/Urbit API. Use for contacts (get/update profiles), listing channels/groups, fetching message history, posting to channels, sending DMs, group management, and exposing content to the clearweb.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Tlon Skill
|
|
@@ -23,21 +23,37 @@ curl -L https://registry.npmjs.org/@tloncorp/tlon-skill-darwin-arm64/-/tlon-skil
|
|
|
23
23
|
|
|
24
24
|
Replace `darwin-arm64` with `darwin-x64` or `linux-x64` as needed.
|
|
25
25
|
|
|
26
|
+
|
|
26
27
|
## Configuration
|
|
27
28
|
|
|
28
29
|
**CLI Flags (highest priority):**
|
|
29
30
|
```bash
|
|
30
|
-
#
|
|
31
|
+
# Cookie-based auth (fastest - ship parsed from cookie name)
|
|
32
|
+
tlon --url https://your-ship.tlon.network --cookie "urbauth-~your-ship=0v..." <command>
|
|
33
|
+
|
|
34
|
+
# Code-based auth (requires url + ship + code)
|
|
31
35
|
tlon --url https://your-ship.tlon.network --ship ~your-ship --code sampel-ticlyt-migfun-falmel <command>
|
|
32
36
|
|
|
33
37
|
# Or load from a JSON config file
|
|
34
38
|
tlon --config ~/ships/my-ship.json <command>
|
|
35
39
|
```
|
|
36
40
|
|
|
37
|
-
Config file format:
|
|
41
|
+
Config file format:
|
|
42
|
+
```json
|
|
43
|
+
// Cookie-based (ship derived from cookie)
|
|
44
|
+
{"url": "...", "cookie": "urbauth-~ship=..."}
|
|
45
|
+
|
|
46
|
+
// Code-based
|
|
47
|
+
{"url": "...", "ship": "~...", "code": "..."}
|
|
48
|
+
```
|
|
38
49
|
|
|
39
50
|
**Environment Variables:**
|
|
40
51
|
```bash
|
|
52
|
+
# Cookie-based (ship derived from cookie)
|
|
53
|
+
export URBIT_URL="https://your-ship.tlon.network"
|
|
54
|
+
export URBIT_COOKIE="urbauth-~your-ship=0v..."
|
|
55
|
+
|
|
56
|
+
# Code-based
|
|
41
57
|
export URBIT_URL="https://your-ship.tlon.network"
|
|
42
58
|
export URBIT_SHIP="~your-ship"
|
|
43
59
|
export URBIT_CODE="sampel-ticlyt-migfun-falmel"
|
|
@@ -45,7 +61,43 @@ export URBIT_CODE="sampel-ticlyt-migfun-falmel"
|
|
|
45
61
|
|
|
46
62
|
**OpenClaw:** If configured with a Tlon channel, credentials load automatically.
|
|
47
63
|
|
|
48
|
-
**Resolution order:** CLI flags → `TLON_CONFIG_FILE` → `
|
|
64
|
+
**Resolution order:** CLI flags → `TLON_CONFIG_FILE` → `URL + COOKIE` → `URL + SHIP + CODE` → `--ship` with cache → OpenClaw config → cached ships (auto-select if only one)
|
|
65
|
+
|
|
66
|
+
**Cookie vs Code:**
|
|
67
|
+
- **Cookie-based:** Uses pre-authenticated session cookie. Ship is parsed from the cookie name (`urbauth-~ship=...`). Fastest option.
|
|
68
|
+
- **Code-based:** Performs login to get session cookie. Requires URL + ship + code.
|
|
69
|
+
|
|
70
|
+
You can provide both cookie and code — cookie is used first, code serves as fallback if cookie expires.
|
|
71
|
+
|
|
72
|
+
## Cookie Caching
|
|
73
|
+
|
|
74
|
+
The skill automatically caches auth cookies to `~/.tlon/cache/<ship>.json` after successful authentication. This makes subsequent invocations much faster by skipping the login request.
|
|
75
|
+
|
|
76
|
+
**How it works:**
|
|
77
|
+
```bash
|
|
78
|
+
# First time - authenticates and caches
|
|
79
|
+
$ tlon --url https://zod.tlon.network --ship ~zod --code abcd-efgh contacts self
|
|
80
|
+
~zod
|
|
81
|
+
Note: Credentials cached for ~zod. Next time just run: tlon <command>
|
|
82
|
+
|
|
83
|
+
# After that - no flags needed (if only one cached ship)
|
|
84
|
+
$ tlon contacts self
|
|
85
|
+
~zod
|
|
86
|
+
|
|
87
|
+
# With multiple cached ships - specify which one
|
|
88
|
+
$ tlon --ship ~zod contacts self
|
|
89
|
+
$ tlon --ship ~bus contacts self
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Cache behavior:**
|
|
93
|
+
- Cached cookies are URL-specific (won't use a cookie for the wrong host)
|
|
94
|
+
- If only one ship is cached, it's auto-selected (no flags needed)
|
|
95
|
+
- If multiple ships are cached, you'll be prompted to specify with `--ship`
|
|
96
|
+
- The skill reminds you when you pass credentials that aren't needed
|
|
97
|
+
|
|
98
|
+
**Clear cache:** `rm ~/.tlon/cache/*.json`
|
|
99
|
+
|
|
100
|
+
|
|
49
101
|
|
|
50
102
|
## Multi-Ship Usage
|
|
51
103
|
|
|
@@ -71,7 +123,7 @@ tlon --config ~/ships/moon.json contacts self
|
|
|
71
123
|
|
|
72
124
|
### Activity
|
|
73
125
|
|
|
74
|
-
Check recent notifications and unread counts.
|
|
126
|
+
Check recent notifications and unread counts. Ships are shown with nicknames when available.
|
|
75
127
|
|
|
76
128
|
```bash
|
|
77
129
|
tlon activity mentions --limit 10 # Recent mentions (max 25)
|
|
@@ -82,18 +134,32 @@ tlon activity unreads # Unread counts per channel
|
|
|
82
134
|
|
|
83
135
|
### Channels
|
|
84
136
|
|
|
85
|
-
List and manage channels.
|
|
137
|
+
List and manage channels. DMs and group DMs show nicknames when available.
|
|
86
138
|
|
|
87
139
|
```bash
|
|
88
|
-
tlon channels dms # List DM contacts
|
|
89
|
-
tlon channels group-dms # List group DMs (clubs)
|
|
140
|
+
tlon channels dms # List DM contacts (with nicknames)
|
|
141
|
+
tlon channels group-dms # List group DMs (clubs, with nicknames)
|
|
90
142
|
tlon channels groups # List subscribed groups
|
|
91
143
|
tlon channels all # List everything
|
|
92
144
|
tlon channels info chat/~host/slug # Get channel details
|
|
93
145
|
tlon channels update chat/~host/slug --title "New Title" # Update metadata
|
|
94
146
|
tlon channels delete chat/~host/slug # Delete a channel
|
|
147
|
+
|
|
148
|
+
# Writers (who can post)
|
|
149
|
+
tlon channels add-writers chat/~host/slug admin member # Add write access
|
|
150
|
+
tlon channels del-writers chat/~host/slug member # Remove write access
|
|
151
|
+
|
|
152
|
+
# Readers (who can view - requires group flag)
|
|
153
|
+
tlon channels add-readers ~host/group chat/~host/slug admin # Restrict viewing
|
|
154
|
+
tlon channels del-readers ~host/group chat/~host/slug admin # Open viewing
|
|
95
155
|
```
|
|
96
156
|
|
|
157
|
+
Notes on permissions:
|
|
158
|
+
- Empty writers list = anyone in the group can post (default for chat)
|
|
159
|
+
- Empty readers list = anyone in the group can view (default)
|
|
160
|
+
- Diaries default to admin-only writers
|
|
161
|
+
- Roles must exist in the group (use `tlon groups add-role` first)
|
|
162
|
+
|
|
97
163
|
### Contacts
|
|
98
164
|
|
|
99
165
|
Manage contacts and profiles.
|
|
@@ -124,7 +190,7 @@ tlon groups leave ~host/slug # Leave a group
|
|
|
124
190
|
tlon groups delete ~host/slug # Delete (host only)
|
|
125
191
|
tlon groups update ~host/slug --title "..." [--description "..."]
|
|
126
192
|
|
|
127
|
-
# Members
|
|
193
|
+
# Members (shown with nicknames when available)
|
|
128
194
|
tlon groups invite ~host/slug ~ship1 ~ship2 # Invite members
|
|
129
195
|
tlon groups kick ~host/slug ~ship1 # Kick members
|
|
130
196
|
tlon groups ban ~host/slug ~ship1 # Ban members
|
|
@@ -146,9 +212,52 @@ tlon groups add-channel ~host/slug "Name" [--kind chat|diary|heap]
|
|
|
146
212
|
|
|
147
213
|
Group format: `~host-ship/group-slug`
|
|
148
214
|
|
|
215
|
+
### Hooks
|
|
216
|
+
|
|
217
|
+
Manage channel hooks — functions that run on triggers (posts, replies, reactions, crons).
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
# List and inspect
|
|
221
|
+
tlon hooks list # List all hooks
|
|
222
|
+
tlon hooks get 0v1a.2b3c4 # Get hook details and source
|
|
223
|
+
|
|
224
|
+
# Manage hooks
|
|
225
|
+
tlon hooks init my-hook --type on-post # Create starter template (on-post|cron|moderation)
|
|
226
|
+
tlon hooks add my-hook ./my-hook.hoon # Add a new hook from file
|
|
227
|
+
tlon hooks edit 0v1a.2b3c4 --name "New Name" # Rename a hook
|
|
228
|
+
tlon hooks edit 0v1a.2b3c4 --src ./updated.hoon # Update source
|
|
229
|
+
tlon hooks delete 0v1a.2b3c4 # Delete a hook
|
|
230
|
+
|
|
231
|
+
# Configure for channels
|
|
232
|
+
tlon hooks order chat/~host/slug 0v1a 0v2b 0v3c # Set execution order
|
|
233
|
+
tlon hooks config 0v1a chat/~host/slug key1=val1 # Configure hook instance
|
|
234
|
+
|
|
235
|
+
# Scheduled execution
|
|
236
|
+
tlon hooks cron 0v1a ~h1 # Run hourly (global)
|
|
237
|
+
tlon hooks cron 0v1a ~m30 --nest chat/~host/slug # Run every 30m for channel
|
|
238
|
+
tlon hooks rest 0v1a # Stop cron job
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Notes:
|
|
242
|
+
- Hook IDs are @uv format (e.g., `0v1a.2b3c4...`)
|
|
243
|
+
- Schedules use @dr format: `~h1` (1 hour), `~m30` (30 minutes), `~d1` (1 day)
|
|
244
|
+
- Hooks run in order when triggered; use `order` to set priority
|
|
245
|
+
- Use `config` to pass channel-specific settings to a hook instance
|
|
246
|
+
|
|
247
|
+
**Writing Hooks:** See `references/hooks.md` for full documentation on writing hooks, including:
|
|
248
|
+
- Event types (`on-post`, `on-reply`, `cron`, `wake`)
|
|
249
|
+
- Bowl context (channel, group, config access)
|
|
250
|
+
- Effects (channel actions, group actions, scheduled wakes)
|
|
251
|
+
- Config handling with clam (`;;`)
|
|
252
|
+
|
|
253
|
+
**Examples:** See `references/hooks-examples/` for starter templates:
|
|
254
|
+
- `auto-react.hoon` — React to new posts with emoji
|
|
255
|
+
- `delete-old-posts.hoon` — Cron job to clean up old messages
|
|
256
|
+
- `word-filter.hoon` — Block posts containing banned words
|
|
257
|
+
|
|
149
258
|
### Messages
|
|
150
259
|
|
|
151
|
-
Read and search message history.
|
|
260
|
+
Read and search message history. Authors are shown with nicknames when available.
|
|
152
261
|
|
|
153
262
|
```bash
|
|
154
263
|
tlon messages dm ~sampel --limit 20 # DM history (max 50)
|
|
@@ -175,6 +284,24 @@ tlon dms accept ~sampel # Accept DM invite
|
|
|
175
284
|
tlon dms decline ~sampel # Decline DM invite
|
|
176
285
|
```
|
|
177
286
|
|
|
287
|
+
### Expose
|
|
288
|
+
|
|
289
|
+
Publish Tlon content to the clearweb via the %expose agent.
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
tlon expose list # List all exposed content
|
|
293
|
+
tlon expose show chat/~host/slug/170.141... # Expose a post publicly
|
|
294
|
+
tlon expose hide chat/~host/slug/170.141... # Hide an exposed post
|
|
295
|
+
tlon expose check diary/~host/blog/170.141... # Check if a post is exposed
|
|
296
|
+
tlon expose url diary/~host/blog/170.141... # Get the public URL
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Cite path formats:
|
|
300
|
+
- Simplified: `chat/~host/channel/170.141...` (auto-expands)
|
|
301
|
+
- Full: `/1/chan/chat/~host/channel/msg/170.141...`
|
|
302
|
+
|
|
303
|
+
Channel kinds map to content types: chat→msg, diary→note, heap→curio
|
|
304
|
+
|
|
178
305
|
### Posts
|
|
179
306
|
|
|
180
307
|
Manage channel posts (reactions, edits, deletes).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tloncorp/tlon-skill",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Tlon/Urbit skill for OpenClaw agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
"files": [
|
|
10
10
|
"bin/",
|
|
11
11
|
"scripts/postinstall.js",
|
|
12
|
-
"SKILL.md"
|
|
12
|
+
"SKILL.md",
|
|
13
|
+
"references/"
|
|
13
14
|
],
|
|
14
15
|
"scripts": {
|
|
15
16
|
"build": "node scripts/build.js",
|
|
@@ -20,13 +21,13 @@
|
|
|
20
21
|
"postinstall": "node scripts/postinstall.js"
|
|
21
22
|
},
|
|
22
23
|
"optionalDependencies": {
|
|
23
|
-
"@tloncorp/tlon-skill-darwin-arm64": "0.1
|
|
24
|
-
"@tloncorp/tlon-skill-darwin-x64": "0.1
|
|
25
|
-
"@tloncorp/tlon-skill-linux-x64": "0.1
|
|
26
|
-
"@tloncorp/tlon-skill-linux-arm64": "0.1
|
|
24
|
+
"@tloncorp/tlon-skill-darwin-arm64": "0.2.1",
|
|
25
|
+
"@tloncorp/tlon-skill-darwin-x64": "0.2.1",
|
|
26
|
+
"@tloncorp/tlon-skill-linux-x64": "0.2.1",
|
|
27
|
+
"@tloncorp/tlon-skill-linux-arm64": "0.2.1"
|
|
27
28
|
},
|
|
28
29
|
"devDependencies": {
|
|
29
|
-
"@tloncorp/api": "git+https://github.com/tloncorp/api-beta.git#
|
|
30
|
+
"@tloncorp/api": "git+https://github.com/tloncorp/api-beta.git#c5eb1720d94435b62503605acb294ef42a5440b3",
|
|
30
31
|
"@urbit/aura": "^3.0.0",
|
|
31
32
|
"@types/node": "^22.0.0",
|
|
32
33
|
"typescript": "^5.9.3"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
:: Auto-react hook: reacts to new posts with configured emoji
|
|
2
|
+
:: Config: emoji (default 👍)
|
|
3
|
+
::
|
|
4
|
+
|= [=event:h =bowl:h]
|
|
5
|
+
^- outcome:h
|
|
6
|
+
:: Extract config with defaults
|
|
7
|
+
=+ ;;(emoji=cord (~(gut by config.bowl) 'emoji' '👍'))
|
|
8
|
+
:: Only react to new posts
|
|
9
|
+
?. ?=([%on-post %add *] event)
|
|
10
|
+
&+[[[%allowed event] ~] state.hook.bowl]
|
|
11
|
+
:: Don't react to our own posts
|
|
12
|
+
?: =(author.post.event our.bowl)
|
|
13
|
+
&+[[[%allowed event] ~] state.hook.bowl]
|
|
14
|
+
:: Need channel context
|
|
15
|
+
?~ channel.bowl
|
|
16
|
+
&+[[[%allowed event] ~] state.hook.bowl]
|
|
17
|
+
:: React to the post
|
|
18
|
+
=/ react-effect=effect:h
|
|
19
|
+
:* %channels
|
|
20
|
+
%channel
|
|
21
|
+
nest.u.channel.bowl
|
|
22
|
+
[%post [%add-react id.post.event our.bowl emoji]]
|
|
23
|
+
==
|
|
24
|
+
&+[[[%allowed event] [react-effect ~]] state.hook.bowl]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
:: Disappearing messages: deletes posts older than configured delay
|
|
2
|
+
:: From: https://github.com/tloncorp/hooks/blob/master/hooks/disappearing.hoon
|
|
3
|
+
:: Config: delay (default ~s30 = 30 seconds)
|
|
4
|
+
::
|
|
5
|
+
|= [=event:h bowl:h]
|
|
6
|
+
^- outcome:h
|
|
7
|
+
=- &+[[[%allowed event] -] state.hook]
|
|
8
|
+
?. ?=(%cron -.event) ~
|
|
9
|
+
^- (list effect:h)
|
|
10
|
+
=+ ;;(delay=@dr (~(gut by config) 'delay' ~s30))
|
|
11
|
+
=/ cutoff (sub now delay)
|
|
12
|
+
?~ channel ~
|
|
13
|
+
%+ murn
|
|
14
|
+
(tap:on-v-posts:c (lot:on-v-posts:c posts.u.channel ~ `cutoff))
|
|
15
|
+
|= [=id-post:c post=(may:c v-post:c)]
|
|
16
|
+
^- (unit effect:h)
|
|
17
|
+
?: ?=(%| -.post) ~
|
|
18
|
+
`[%channels %channel nest.u.channel %post %del id-post]
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
:: Word filter hook: blocks posts containing banned words
|
|
2
|
+
:: Config: words (comma-separated list of words to block)
|
|
3
|
+
::
|
|
4
|
+
|= [=event:h =bowl:h]
|
|
5
|
+
^- outcome:h
|
|
6
|
+
|^
|
|
7
|
+
:: Only filter new posts
|
|
8
|
+
?. ?=([%on-post %add *] event)
|
|
9
|
+
&+[[[%allowed event] ~] state.hook.bowl]
|
|
10
|
+
:: Get banned words from config (comma-separated)
|
|
11
|
+
=+ ;;(words-cord=cord (~(gut by config.bowl) 'words' ''))
|
|
12
|
+
:: Skip if no words configured
|
|
13
|
+
?: =('' words-cord)
|
|
14
|
+
&+[[[%allowed event] ~] state.hook.bowl]
|
|
15
|
+
:: Split on commas into list of tapes
|
|
16
|
+
=/ banned=(list tape)
|
|
17
|
+
(split-on-comma (trip words-cord))
|
|
18
|
+
:: Get message content
|
|
19
|
+
=/ content=tape (extract-text content.post.event)
|
|
20
|
+
:: Check if any banned word appears in content
|
|
21
|
+
=/ has-banned=?
|
|
22
|
+
%+ lien banned
|
|
23
|
+
|= word=tape
|
|
24
|
+
!=(~ (find word content))
|
|
25
|
+
:: If found, deny
|
|
26
|
+
?: has-banned
|
|
27
|
+
&+[[[%denied `'Message contains prohibited content'] ~] state.hook.bowl]
|
|
28
|
+
:: Otherwise allow
|
|
29
|
+
&+[[[%allowed event] ~] state.hook.bowl]
|
|
30
|
+
::
|
|
31
|
+
++ split-on-comma
|
|
32
|
+
|= txt=tape
|
|
33
|
+
^- (list tape)
|
|
34
|
+
=/ idx (find "," txt)
|
|
35
|
+
?~ idx
|
|
36
|
+
?~ txt *(list tape)
|
|
37
|
+
~[txt]
|
|
38
|
+
:- (scag u.idx txt)
|
|
39
|
+
$(txt (slag +(u.idx) txt))
|
|
40
|
+
::
|
|
41
|
+
++ extract-text
|
|
42
|
+
|= =story:c
|
|
43
|
+
^- tape
|
|
44
|
+
?~ story ""
|
|
45
|
+
=/ verse i.story
|
|
46
|
+
?. ?=(%inline -.verse) ""
|
|
47
|
+
?~ p.verse ""
|
|
48
|
+
=/ inl i.p.verse
|
|
49
|
+
?@ inl
|
|
50
|
+
(trip inl)
|
|
51
|
+
""
|
|
52
|
+
--
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
# Tlon Channel Hooks
|
|
2
|
+
|
|
3
|
+
Hooks are hoon functions that modify events, cause effects, and/or build state for channels.
|
|
4
|
+
|
|
5
|
+
## Version Compatibility
|
|
6
|
+
|
|
7
|
+
Hooks are tightly coupled to `channels-server` / `tlon-apps` types.
|
|
8
|
+
When docs/examples drift from runtime types, compilation can fail even if logic is correct.
|
|
9
|
+
|
|
10
|
+
Recommended practice:
|
|
11
|
+
- Pin your local references to a known `tlon-apps` commit/tag
|
|
12
|
+
- Record that version in PRs when adding/updating hooks
|
|
13
|
+
- Prefer examples in this folder that were verified against the current runtime
|
|
14
|
+
|
|
15
|
+
## Hook Structure
|
|
16
|
+
|
|
17
|
+
```hoon
|
|
18
|
+
++ hook
|
|
19
|
+
$: id=id-hook
|
|
20
|
+
version=%0
|
|
21
|
+
name=@t
|
|
22
|
+
meta=data:m
|
|
23
|
+
src=@t
|
|
24
|
+
compiled=(unit vase)
|
|
25
|
+
state=vase
|
|
26
|
+
config=(map nest config)
|
|
27
|
+
==
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
- `id` - unique identifier (@uv format)
|
|
31
|
+
- `version` - the version this hook was written for
|
|
32
|
+
- `name` - human-readable display name
|
|
33
|
+
- `meta` - standard metadata (title/image/desc/cover)
|
|
34
|
+
- `src` - the source code for the hook
|
|
35
|
+
- `compiled` - result of compiling hoon to nock
|
|
36
|
+
- `state` - container to collect/persist data
|
|
37
|
+
- `config` - configurations for each channel
|
|
38
|
+
|
|
39
|
+
## Events
|
|
40
|
+
|
|
41
|
+
Hooks respond to four event types:
|
|
42
|
+
|
|
43
|
+
```hoon
|
|
44
|
+
+$ event
|
|
45
|
+
$% [%on-post on-post]
|
|
46
|
+
[%on-reply on-reply]
|
|
47
|
+
[%cron ~]
|
|
48
|
+
[%wake waiting-hook]
|
|
49
|
+
==
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### on-post events
|
|
53
|
+
```hoon
|
|
54
|
+
+$ on-post
|
|
55
|
+
$% [%add post=v-post]
|
|
56
|
+
[%edit original=v-post =essay]
|
|
57
|
+
[%del original=v-post]
|
|
58
|
+
[%react post=v-post =ship react=(unit react)]
|
|
59
|
+
==
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### on-reply events
|
|
63
|
+
```hoon
|
|
64
|
+
+$ on-reply
|
|
65
|
+
$% [%add parent=v-post reply=v-reply]
|
|
66
|
+
[%edit parent=v-post original=v-reply =memo]
|
|
67
|
+
[%del parent=v-post original=v-reply]
|
|
68
|
+
[%react parent=v-post reply=v-reply =ship react=(unit react)]
|
|
69
|
+
==
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Bowl (Context)
|
|
73
|
+
|
|
74
|
+
Hooks receive ambient state via the bowl:
|
|
75
|
+
|
|
76
|
+
```hoon
|
|
77
|
+
+$ bowl
|
|
78
|
+
$: channel=(unit [=nest v-channel]) :: current channel (null for global cron)
|
|
79
|
+
group=(unit group-ui:g) :: group data
|
|
80
|
+
channels=v-channels :: all hosted channels
|
|
81
|
+
=hook :: this hook's data
|
|
82
|
+
=config :: channel-specific config
|
|
83
|
+
now=time :: current time
|
|
84
|
+
our=ship :: host ship
|
|
85
|
+
src=ship :: triggering ship
|
|
86
|
+
eny=@ :: entropy
|
|
87
|
+
==
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Important:** Access patterns depend on whether you use a face:
|
|
91
|
+
- `|= [=event:h =bowl:h]` (with face) → access via `config.bowl`, `channel.bowl`, `state.hook.bowl`
|
|
92
|
+
- `|= [=event:h bowl:h]` (no face) → access via `config`, `channel`, `state.hook`
|
|
93
|
+
|
|
94
|
+
## Return Type
|
|
95
|
+
|
|
96
|
+
```hoon
|
|
97
|
+
+$ outcome (each return tang)
|
|
98
|
+
|
|
99
|
+
+$ return
|
|
100
|
+
$: result=event-result
|
|
101
|
+
effects=(list effect)
|
|
102
|
+
new-state=vase
|
|
103
|
+
==
|
|
104
|
+
|
|
105
|
+
+$ event-result
|
|
106
|
+
$% [%allowed =event] :: allow event, optionally transform it
|
|
107
|
+
[%denied msg=(unit cord)] :: block event with optional message
|
|
108
|
+
==
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Effects
|
|
112
|
+
|
|
113
|
+
Hooks can trigger actions on other agents:
|
|
114
|
+
|
|
115
|
+
```hoon
|
|
116
|
+
+$ effect
|
|
117
|
+
$% [%channels =a-channels] :: channel actions
|
|
118
|
+
[%groups =action:g] :: group actions (ban, kick, etc)
|
|
119
|
+
[%activity =action:a] :: activity actions
|
|
120
|
+
[%dm =action:dm:ch] :: DM actions
|
|
121
|
+
[%club =action:club:ch] :: group DM actions
|
|
122
|
+
[%contacts =action:co] :: contact actions
|
|
123
|
+
[%wait waiting-hook] :: schedule delayed execution
|
|
124
|
+
==
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Channel Effects
|
|
128
|
+
|
|
129
|
+
The most common effect pattern for channel actions:
|
|
130
|
+
|
|
131
|
+
```hoon
|
|
132
|
+
:: React to a post
|
|
133
|
+
[%channels %channel nest [%post [%add-react post-id ship emoji]]]
|
|
134
|
+
|
|
135
|
+
:: Delete a post
|
|
136
|
+
[%channels %channel nest %post %del post-id]
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**Note:** Use actual unicode emoji (`'👍'`, `'🔥'`) not shortcodes (`:thumbsup:`).
|
|
140
|
+
|
|
141
|
+
## Config
|
|
142
|
+
|
|
143
|
+
Config is `(map @t *)` on the Hoon side.
|
|
144
|
+
From CLI, values are sent as text and then clammed/coerced in-hook.
|
|
145
|
+
Use clam (`;;`) to extract typed values with defaults:
|
|
146
|
+
|
|
147
|
+
```hoon
|
|
148
|
+
:: With bowl face (=bowl:h)
|
|
149
|
+
=+ ;;(delay=@dr (~(gut by config.bowl) 'delay' ~s30))
|
|
150
|
+
=+ ;;(emoji=cord (~(gut by config.bowl) 'emoji' '👍'))
|
|
151
|
+
|
|
152
|
+
:: Without bowl face (bowl:h)
|
|
153
|
+
=+ ;;(delay=@dr (~(gut by config) 'delay' ~s30))
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
CLI tips:
|
|
157
|
+
- Prefer simple text/cord values first (`password=owl-pass`)
|
|
158
|
+
- For booleans/durations, verify with `hooks get <id>` after setting config
|
|
159
|
+
- If a config poke fails, inspect the exact update payload from CLI output
|
|
160
|
+
|
|
161
|
+
## Plaintext Helper Pattern
|
|
162
|
+
|
|
163
|
+
The hook subject includes a `flatten` gate via `channel-utils`.
|
|
164
|
+
For moderation/search-like use cases, prefer this default:
|
|
165
|
+
|
|
166
|
+
```hoon
|
|
167
|
+
=/ text=tape
|
|
168
|
+
(trip (flatten content.post.event))
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
`flatten` intentionally focuses on searchable/user-visible text and may skip some structures
|
|
172
|
+
(e.g. code blocks or other rich content forms depending on story shape).
|
|
173
|
+
|
|
174
|
+
If you need strict/full extraction (including code), implement a custom story walker gate.
|
|
175
|
+
|
|
176
|
+
## Writing a Hook
|
|
177
|
+
|
|
178
|
+
Basic hook template (with bowl face):
|
|
179
|
+
|
|
180
|
+
```hoon
|
|
181
|
+
|= [=event:h =bowl:h]
|
|
182
|
+
^- outcome:h
|
|
183
|
+
:: Return success with: [allowed-result effects new-state]
|
|
184
|
+
&+[[[%allowed event] ~] state.hook.bowl]
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Basic hook template (without bowl face):
|
|
188
|
+
|
|
189
|
+
```hoon
|
|
190
|
+
|= [=event:h bowl:h]
|
|
191
|
+
^- outcome:h
|
|
192
|
+
:: Return success with: [allowed-result effects new-state]
|
|
193
|
+
&+[[[%allowed event] ~] state.hook]
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Example: Auto-react to new posts
|
|
197
|
+
|
|
198
|
+
```hoon
|
|
199
|
+
:: Auto-react hook: reacts to new posts with configured emoji
|
|
200
|
+
:: Config: emoji (default 👍)
|
|
201
|
+
::
|
|
202
|
+
|= [=event:h =bowl:h]
|
|
203
|
+
^- outcome:h
|
|
204
|
+
:: Extract config with defaults
|
|
205
|
+
=+ ;;(emoji=cord (~(gut by config.bowl) 'emoji' '👍'))
|
|
206
|
+
:: Only react to new posts
|
|
207
|
+
?. ?=([%on-post %add *] event)
|
|
208
|
+
&+[[[%allowed event] ~] state.hook.bowl]
|
|
209
|
+
:: Don't react to our own posts
|
|
210
|
+
?: =(author.post.event our.bowl)
|
|
211
|
+
&+[[[%allowed event] ~] state.hook.bowl]
|
|
212
|
+
:: Need channel context
|
|
213
|
+
?~ channel.bowl
|
|
214
|
+
&+[[[%allowed event] ~] state.hook.bowl]
|
|
215
|
+
:: React to the post
|
|
216
|
+
=/ react-effect=effect:h
|
|
217
|
+
:* %channels
|
|
218
|
+
%channel
|
|
219
|
+
nest.u.channel.bowl
|
|
220
|
+
[%post [%add-react id.post.event our.bowl emoji]]
|
|
221
|
+
==
|
|
222
|
+
&+[[[%allowed event] [react-effect ~]] state.hook.bowl]
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**Key points:**
|
|
226
|
+
- Check `author.post.event` (not `src.bowl`) for self-detection
|
|
227
|
+
- Check `channel.bowl` for null before accessing `u.channel.bowl`
|
|
228
|
+
- Effect structure: `[%channels %channel nest [%post [%add-react ...]]]`
|
|
229
|
+
- Use actual emoji unicode, not shortcodes
|
|
230
|
+
|
|
231
|
+
### Example: Disappearing messages (cron)
|
|
232
|
+
|
|
233
|
+
From [tloncorp/hooks](https://github.com/tloncorp/hooks/blob/master/hooks/disappearing.hoon):
|
|
234
|
+
|
|
235
|
+
```hoon
|
|
236
|
+
:: Disappearing messages: deletes posts older than configured delay
|
|
237
|
+
:: Config: delay (default ~s30 = 30 seconds)
|
|
238
|
+
::
|
|
239
|
+
|= [=event:h bowl:h]
|
|
240
|
+
^- outcome:h
|
|
241
|
+
=- &+[[[%allowed event] -] state.hook]
|
|
242
|
+
?. ?=(%cron -.event) ~
|
|
243
|
+
^- (list effect:h)
|
|
244
|
+
=+ ;;(delay=@dr (~(gut by config) 'delay' ~s30))
|
|
245
|
+
=/ cutoff (sub now delay)
|
|
246
|
+
?~ channel ~
|
|
247
|
+
%+ murn
|
|
248
|
+
(tap:on-v-posts:c (lot:on-v-posts:c posts.u.channel ~ `cutoff))
|
|
249
|
+
|= [=id-post:c post=(may:c v-post:c)]
|
|
250
|
+
^- (unit effect:h)
|
|
251
|
+
?: ?=(%| -.post) ~
|
|
252
|
+
`[%channels %channel nest.u.channel %post %del id-post]
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
**Key points:**
|
|
256
|
+
- Uses `bowl:h` without face → access `config`, `channel`, `state.hook` directly
|
|
257
|
+
- Uses `on-v-posts:c` ordered map with `lot:` for cutoff filtering
|
|
258
|
+
- Uses `(may:c v-post:c)` type - posts can be deleted (`%|`) or present (`%&`)
|
|
259
|
+
- Check `?=(%| -.post)` to skip already-deleted posts
|
|
260
|
+
|
|
261
|
+
### Example: Word filter
|
|
262
|
+
|
|
263
|
+
```hoon
|
|
264
|
+
:: Word filter hook: blocks posts containing banned words
|
|
265
|
+
:: Config: words (comma-separated list of words to block)
|
|
266
|
+
::
|
|
267
|
+
|= [=event:h =bowl:h]
|
|
268
|
+
^- outcome:h
|
|
269
|
+
|^
|
|
270
|
+
:: Only filter new posts
|
|
271
|
+
?. ?=([%on-post %add *] event)
|
|
272
|
+
&+[[[%allowed event] ~] state.hook.bowl]
|
|
273
|
+
:: Get banned words from config (comma-separated)
|
|
274
|
+
=+ ;;(words-cord=cord (~(gut by config.bowl) 'words' ''))
|
|
275
|
+
:: Skip if no words configured
|
|
276
|
+
?: =('' words-cord)
|
|
277
|
+
&+[[[%allowed event] ~] state.hook.bowl]
|
|
278
|
+
:: Split on commas into list of tapes
|
|
279
|
+
=/ banned=(list tape)
|
|
280
|
+
(split-on-comma (trip words-cord))
|
|
281
|
+
:: Get message content
|
|
282
|
+
=/ content=tape (extract-text content.post.event)
|
|
283
|
+
:: Check if any banned word appears in content
|
|
284
|
+
=/ has-banned=?
|
|
285
|
+
%+ lien banned
|
|
286
|
+
|= word=tape
|
|
287
|
+
!=(~ (find word content))
|
|
288
|
+
:: If found, deny
|
|
289
|
+
?: has-banned
|
|
290
|
+
&+[[[%denied `'Message contains prohibited content'] ~] state.hook.bowl]
|
|
291
|
+
:: Otherwise allow
|
|
292
|
+
&+[[[%allowed event] ~] state.hook.bowl]
|
|
293
|
+
::
|
|
294
|
+
++ split-on-comma
|
|
295
|
+
|= txt=tape
|
|
296
|
+
^- (list tape)
|
|
297
|
+
=/ idx (find "," txt)
|
|
298
|
+
?~ idx
|
|
299
|
+
?~ txt *(list tape)
|
|
300
|
+
~[txt]
|
|
301
|
+
:- (scag u.idx txt)
|
|
302
|
+
$(txt (slag +(u.idx) txt))
|
|
303
|
+
::
|
|
304
|
+
++ extract-text
|
|
305
|
+
|= =story:c
|
|
306
|
+
^- tape
|
|
307
|
+
?~ story ""
|
|
308
|
+
=/ verse i.story
|
|
309
|
+
?. ?=(%inline -.verse) ""
|
|
310
|
+
?~ p.verse ""
|
|
311
|
+
=/ inl i.p.verse
|
|
312
|
+
?@ inl
|
|
313
|
+
(trip inl)
|
|
314
|
+
""
|
|
315
|
+
--
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
**Key points:**
|
|
319
|
+
- Access post content via `content.post.event`
|
|
320
|
+
- Use `lien` to check if any word in list matches
|
|
321
|
+
- Helper arms (`++`) for text processing go inside `|^` ... `--`
|
|
322
|
+
- Config supports comma-separated values
|
|
323
|
+
|
|
324
|
+
## CLI Commands
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
# Add a hook
|
|
328
|
+
tlon hooks add "my-hook" ./hook.hoon
|
|
329
|
+
|
|
330
|
+
# Configure for a channel
|
|
331
|
+
tlon hooks config <id> chat/~host/channel delay=~m5 emoji=👍
|
|
332
|
+
|
|
333
|
+
# Set execution order
|
|
334
|
+
tlon hooks order chat/~host/channel <id1> <id2>
|
|
335
|
+
|
|
336
|
+
# Schedule periodic execution
|
|
337
|
+
tlon hooks cron <id> ~h1 --nest chat/~host/channel
|
|
338
|
+
|
|
339
|
+
# List hooks
|
|
340
|
+
tlon hooks list
|
|
341
|
+
|
|
342
|
+
# Watch for hook updates (debugging)
|
|
343
|
+
tlon hooks watch
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
## Testing Hooks (Dojo)
|
|
347
|
+
|
|
348
|
+
Test without affecting channels:
|
|
349
|
+
|
|
350
|
+
```
|
|
351
|
+
-groups!hooks-run <event> [%origin nest optional-state optional-config] <src>
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
## Common Pitfalls
|
|
355
|
+
|
|
356
|
+
1. **Bowl face confusion** - `=bowl:h` vs `bowl:h` changes how you access fields
|
|
357
|
+
2. **Wrong effect structure** - channel effects need exact nesting: `[%channels %channel nest ...]`
|
|
358
|
+
3. **Emoji shortcodes** - use actual unicode (`'👍'`), not `:thumbsup:`
|
|
359
|
+
4. **Self-detection** - check `author.post.event`, not `src.bowl`
|
|
360
|
+
5. **Deleted posts** - in cron, posts can be `%|` (deleted) or `%&` (present)
|
|
361
|
+
|
|
362
|
+
## Type References
|
|
363
|
+
|
|
364
|
+
- Full hooks types: https://github.com/tloncorp/tlon-apps/blob/develop/desk/sur/hooks.hoon
|
|
365
|
+
- Channel types (v-post, v-reply, etc): https://github.com/tloncorp/tlon-apps/blob/develop/desk/sur/channels.hoon
|
|
366
|
+
- Official hooks examples: https://github.com/tloncorp/hooks
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Urbit HTTP API Reference
|
|
2
|
+
|
|
3
|
+
Quick reference for the Urbit HTTP API used by this skill.
|
|
4
|
+
|
|
5
|
+
## Authentication
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { Urbit } from "@urbit/http-api";
|
|
9
|
+
|
|
10
|
+
const api = await Urbit.authenticate({
|
|
11
|
+
ship: "sampel-palnet", // without ~
|
|
12
|
+
url: "https://myship.tlon.network",
|
|
13
|
+
code: "lidlut-tabwed-...",
|
|
14
|
+
verbose: false,
|
|
15
|
+
});
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Core Operations
|
|
19
|
+
|
|
20
|
+
### Scry (Read)
|
|
21
|
+
```typescript
|
|
22
|
+
const result = await api.scry<T>({ app: "app-name", path: "/path" });
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Poke (Write)
|
|
26
|
+
```typescript
|
|
27
|
+
await api.poke({ app: "app-name", mark: "mark-name", json: { ... } });
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Subscribe (Real-time)
|
|
31
|
+
```typescript
|
|
32
|
+
await api.subscribe({
|
|
33
|
+
app: "app-name",
|
|
34
|
+
path: "/path",
|
|
35
|
+
event: (data) => { ... },
|
|
36
|
+
quit: () => { ... },
|
|
37
|
+
});
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Contacts Agent
|
|
41
|
+
|
|
42
|
+
### Scry Paths
|
|
43
|
+
- `/all` - All peers with merged profile data (ContactRolodex)
|
|
44
|
+
- `/v1/book` - All contacts with user overrides (ContactBookScryResult)
|
|
45
|
+
|
|
46
|
+
### Poke Marks
|
|
47
|
+
- `contact-action` - Legacy profile edits
|
|
48
|
+
- `{ edit: [{ nickname: "name" }, { bio: "..." }] }`
|
|
49
|
+
- `contact-action-1` - New contact operations
|
|
50
|
+
- `{ meet: ["~ship1", "~ship2"] }` - Sync profiles
|
|
51
|
+
- `{ page: { kip: "~ship", contact: {} } }` - Add contact
|
|
52
|
+
- `{ wipe: ["~ship"] }` - Remove contact
|
|
53
|
+
- `{ edit: { kip: "~ship", contact: { nickname: {...} } } }` - Edit contact
|
|
54
|
+
|
|
55
|
+
### Profile Fields
|
|
56
|
+
```typescript
|
|
57
|
+
interface ContactBookProfile {
|
|
58
|
+
nickname?: { type: "text", value: string };
|
|
59
|
+
bio?: { type: "text", value: string };
|
|
60
|
+
status?: { type: "text", value: string };
|
|
61
|
+
avatar?: { type: "look", value: string }; // URL
|
|
62
|
+
cover?: { type: "look", value: string }; // URL
|
|
63
|
+
color?: { type: "tint", value: string }; // Hex without #
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Chat Agent
|
|
68
|
+
|
|
69
|
+
### Scry Paths
|
|
70
|
+
- `/dm` - List of DM ship names (string[])
|
|
71
|
+
- `/clubs` - Group DMs (Clubs)
|
|
72
|
+
- `/blocked` - Blocked ships
|
|
73
|
+
- `/v3/dm/~ship/writs/newest/{count}/light` - DM message history (light = no replies)
|
|
74
|
+
- `/v3/dm/~ship/writs/newest/{count}/heavy` - DM message history (heavy = with replies)
|
|
75
|
+
- `/v3/club/{clubId}/writs/newest/{count}/light` - Group DM message history
|
|
76
|
+
- `/v2/dm/~ship/writs/writ/id/{author}/{postId}` - Single DM post with replies
|
|
77
|
+
|
|
78
|
+
### Poke Marks
|
|
79
|
+
- `chat-dm-action-1` - Send DM
|
|
80
|
+
- `chat-club-action-1` - Group DM operations
|
|
81
|
+
- `chat-remark-action` - Mark read
|
|
82
|
+
|
|
83
|
+
## Groups Agent
|
|
84
|
+
|
|
85
|
+
### Scry Paths
|
|
86
|
+
- `/groups/v2` - All subscribed groups
|
|
87
|
+
- `/groups/v2/~host/group-name` - Specific group
|
|
88
|
+
|
|
89
|
+
## Channels Agent
|
|
90
|
+
|
|
91
|
+
### Scry Paths
|
|
92
|
+
- `/v1/hooks/preview/{nest}` - Channel preview
|
|
93
|
+
- `/{nest}/search/...` - Search messages
|
|
94
|
+
|
|
95
|
+
### Poke Marks
|
|
96
|
+
- `channel-action` - Legacy channel operations
|
|
97
|
+
- `channel-action-1` - New channel operations
|
|
98
|
+
|
|
99
|
+
## Common Types
|
|
100
|
+
|
|
101
|
+
### Flag (Group ID)
|
|
102
|
+
Format: `~host/group-name`
|
|
103
|
+
|
|
104
|
+
### Nest (Channel ID)
|
|
105
|
+
Format: `{kind}/~host/channel-name`
|
|
106
|
+
Kinds: `chat`, `diary`, `heap`
|
|
107
|
+
|
|
108
|
+
## Tips
|
|
109
|
+
|
|
110
|
+
1. Ship names in pokes/responses include `~` prefix
|
|
111
|
+
2. Use `formatUd(unixToDa(timestamp))` for message IDs
|
|
112
|
+
3. Profile changes sync automatically to peers
|
|
113
|
+
4. Scries are synchronous reads; pokes are async writes
|