x-bookmark-triage
Turns X/Twitter bookmarks into searchable, tiered knowledge cards posted to a Discord channel. Captures, scores, and optionally unbookmarks — so your bookmark list stays clean and your knowledge base grows automatically.
Works standalone — no OpenClaw required for core triage. OpenClaw integration is optional (adds scheduled polling via cron and bus events). See references/adapting.md for standalone setup.
Architecture
CODEBLOCK0
Prerequisites
bookmark.read (required) +
bookmark.write (for unbookmark) |
| Discord bot token | Needs
Send Messages permission in target channel |
| Anthropic API key |
ANTHROPIC_DEFAULT_KEY env var |
| Node.js 18+ | Scripts use
spawnSync, no npm deps |
Quick Start (8-step MVP)
- 1. Get credentials:
-
X OAuth 2.0 app: Create at
developer.x.com — Free tier. See
references/oauth-setup.md for step-by-step.
-
Discord bot token: Create at
discord.com/developers — Bot needs
Send Messages permission in your target channel.
-
Anthropic API key: From
console.anthropic.com
- 2. Run auth flow:
node scripts/x-oauth2-authorize.js → saves refresh token to INLINECODE9
- 3. Set env vars: Export
X_OAUTH2_CLIENT_ID, X_OAUTH2_CLIENT_SECRET, X_OAUTH2_REFRESH_TOKEN, DISCORD_BOT_TOKEN, INLINECODE14
- 4. Set your Discord channel: Set env var
KNOWLEDGE_INTAKE_CHANNEL_ID=<your-channel-id> (preferred), OR edit scripts/triage-url.js line 19 to hardcode it.
- 5. Test triage: INLINECODE17
- 6. Check Discord: You should see a formatted card in your channel
- 7. Verify dedup: Run the command again — should skip (already seen)
- 8. Done: Your first card is live. Next: set up scheduling (see Steps 3–4 below)
Setup
1. OAuth 2.0 Authorization
CODEBLOCK1
Store credentials in your gateway plist (or env):
CODEBLOCK2
The scripts also fall back to reading refresh_token from a secrets JSON file at runtime. See references/oauth-setup.md for full details.
2. Configure Target Channel
Set CHANNEL_ID at the top of scripts/triage-url.js to your Discord channel ID.
3. Register the Scheduled Poll (Optional — for automated polling)
CODEBLOCK3
Alternatively, drain all bookmarks one-time:
CODEBLOCK4
4. Register the launchd Poller (Optional — for automated polling)
Note: scripts/ai.watson.knowledge-intake-poll.plist is a template. Customize it first:
- 1. Edit the file to replace
/PATH/TO/skills/x-bookmark-triage/scripts/poll-channel.js with the actual path - Replace
YOUR_BOT_TOKEN, YOUR_ANTHROPIC_KEY, YOUR_CHANNEL_ID, /PATH/TO/workspace with real values - Copy to LaunchAgents and load:
CODEBLOCK5
Or use OpenClaw to manage it — see references/cron-setup.md.
Scripts
triage-url.js — Core triage engine
CODEBLOCK6
- - Fetches content (fxtwitter for tweets, markdown.new for web pages)
- Triages with Claude Haiku → produces tier, freshness, topic, tags, proposed action
- Posts formatted card to Discord
- Marks URL as seen in INLINECODE30
- Exits 0 on success or "already seen"; exits 1 on hard failure
bookmark-poll.js — Scheduled sweep (latest 20)
CODEBLOCK7
- - Refreshes OAuth token, fetches 20 most recent bookmarks
- Calls
triage-url.js for each unseen URL - Unbookmarks after successful triage (+ already-seen items)
- Emits
bookmark_sweep_complete to bus
backlog-sweep.js — One-time full drain
CODEBLOCK8
- - Pages through ALL bookmarks (up to API limit)
- INLINECODE35 : list URLs without triaging or unbookmarking
- INLINECODE36 : stop after N bookmarks
- INLINECODE37 : seconds between triage calls (default: 3)
- Emits
backlog_sweep_complete to bus
poll-channel.js — Discord drop handler
CODEBLOCK9
- - Polls a Discord channel every 2 min for new messages
- Extracts URLs from message content
- Calls
triage-url.js for each unseen URL - Intended to run as a persistent launchd service
Card Format
Tier 3–10 (work-relevant):
CODEBLOCK10
Tier 1–2 (off-topic/personal):
CODEBLOCK11
Tier Guide
| Tier | Label | Meaning |
|---|
| 9–10 | 🥇 Act now | Implement this week |
| 7–8 |
🥈 Worth tracking | Thread + investigate soon |
| 5–6 | 🥉 Knowledge doc | Revisit in a sprint |
| 3–4 | 📌 Low priority | Interesting, not actionable |
| 1–2 | 📌 Off-topic | Personal/entertainment — one-liner only |
Freshness Values
- -
evergreen 🌿 — timeless, still valid in 2 years - INLINECODE42 📅 — relevant but will age (benchmarks, roadmaps)
- INLINECODE43 ⏰ — time-sensitive, likely already stale
Topic Values (one per card)
INLINECODE44 · #veefriends · #product · #revenue · #openclaw · #personal · INLINECODE50
Token / Cost Estimate
Each triage call: ~3.5K avg input + 300 output tokens (claude-haiku-4-5)
- - Per URL: ~$0.003
- 20 new bookmarks/run: ~$0.06
- 2 runs/day at typical throughput: ~$3.60/month
- Full 1000-bookmark backlog: ~$3.50
Seen-URL Deduplication
URLs are tracked in data/knowledge-intake-seen.json. Cap: 2,000 entries (FIFO). Bookmarks already in the seen cache are still unbookmarked — they were captured in a prior run.
Refresh Token Rotation
X rotates refresh tokens on each use. The scripts:
- 1. Detect rotation (new token in response)
- Save new token to INLINECODE52
- Log a warning to update the plist env var
Watson auto-detects and updates the secrets file. For non-Watson deployments, monitor for this warning and update X_OAUTH2_REFRESH_TOKEN in your env.
References
- -
references/oauth-setup.md — Full OAuth 2.0 setup walkthrough - INLINECODE55 — OpenClaw cron registration for scheduled polls
- INLINECODE56 — How to adapt channel IDs, topics, triage prompt for your workspace
x-bookmark-triage
将X/Twitter书签转化为可搜索、分层的知识卡片,并发布到Discord频道。捕获、评分,并可选择取消书签——让你的书签列表保持整洁,知识库自动增长。
独立运行——核心分类功能无需OpenClaw。OpenClaw集成是可选的(通过cron和总线事件添加定时轮询)。独立部署请参见references/adapting.md。
架构
X 书签
│
├── 定时轮询(每天2次)→ bookmark-poll.js
├── 积压清理(一次性)→ backlog-sweep.js
└── 手动提交(通过@提及)→ poll-channel.js(launchd轮询器)
│
▼
triage-url.js(抓取 → Claude Haiku → 卡片)
│
▼
Discord #knowledge-intake(结构化卡片)
│
▼
X API DELETE /bookmarks/:id(取消书签)
前置条件
bookmark.read(必需)+ bookmark.write(用于取消书签)|
| Discord机器人令牌 | 需要在目标频道拥有发送消息权限 |
| Anthropic API密钥 | ANTHROPIC
DEFAULTKEY环境变量 |
| Node.js 18+ | 脚本使用spawnSync,无需npm依赖 |
快速入门(8步最小可行方案)
- 1. 获取凭证:
-
X OAuth 2.0应用: 在
developer.x.com创建 — 免费版即可。详细步骤见references/oauth-setup.md。
-
Discord机器人令牌: 在
discord.com/developers创建 — 机器人需要在目标频道拥有发送消息权限。
-
Anthropic API密钥: 从
console.anthropic.com获取
- 2. 运行认证流程: node scripts/x-oauth2-authorize.js → 将刷新令牌保存到data/x-oauth2-token-cache.json
- 3. 设置环境变量: 导出XOAUTH2CLIENTID、XOAUTH2CLIENTSECRET、XOAUTH2REFRESHTOKEN、DISCORDBOTTOKEN、ANTHROPICDEFAULT_KEY
- 4. 设置Discord频道: 设置环境变量KNOWLEDGEINTAKECHANNEL_ID=<你的频道ID>(推荐),或编辑scripts/triage-url.js第19行进行硬编码。
- 5. 测试分类: node scripts/triage-url.js https://x.com/@用户名/status/1234567890 --source manual drop
- 6. 检查Discord: 你应该能在频道中看到格式化的卡片
- 7. 验证去重: 再次运行命令 — 应跳过(已处理过)
- 8. 完成: 你的第一张卡片已上线。下一步:设置定时调度(参见下面的步骤3-4)
设置
1. OAuth 2.0授权
bash
运行交互式认证流程
node scripts/x-oauth2-authorize.js
→ 打开浏览器,捕获回调,保存到data/x-oauth2-token-cache.json
将凭证存储在网关plist(或环境变量)中:
XOAUTH2CLIENT_ID — 应用OAuth 2.0客户端ID
XOAUTH2CLIENT_SECRET — 应用OAuth 2.0客户端密钥
XOAUTH2REFRESH_TOKEN — 来自认证流程(由脚本自动轮换)
脚本在运行时也会回退到从secrets JSON文件中读取refresh_token。完整详情请参见references/oauth-setup.md。
2. 配置目标频道
在scripts/triage-url.js顶部将CHANNEL_ID设置为你的Discord频道ID。
3. 注册定时轮询(可选 — 用于自动轮询)
bash
注册cron任务(每天上午9点+下午5点)
在OpenClaw中:cron add — 参见references/cron-setup.md
你也可以手动运行:node scripts/bookmark-poll.js
或者,一次性清空所有书签:
bash
node scripts/backlog-sweep.js --delay 3
4. 注册launchd轮询器(可选 — 用于自动轮询)
注意: scripts/ai.watson.knowledge-intake-poll.plist是一个模板。请先自定义:
- 1. 编辑文件,将/PATH/TO/skills/x-bookmark-triage/scripts/poll-channel.js替换为实际路径
- 将YOURBOTTOKEN、YOURANTHROPICKEY、YOURCHANNELID、/PATH/TO/workspace替换为实际值
- 复制到LaunchAgents并加载:
bash
自定义plist后:
cp scripts/ai.watson.knowledge-intake-poll.plist ~/Library/LaunchAgents/
launchctl load ~/Library/LaunchAgents/ai.watson.knowledge-intake-poll.plist
或者使用OpenClaw进行管理 — 参见references/cron-setup.md。
脚本
triage-url.js — 核心分类引擎
bash
node scripts/triage-url.js [--source bookmark poll|manual drop|backlog sweep]
- - 抓取内容(推文使用fxtwitter,网页使用markdown.new)
- 使用Claude Haiku进行分类 → 生成层级、新鲜度、主题、标签、建议操作
- 将格式化卡片发布到Discord
- 将URL标记为已处理,记录在data/knowledge-intake-seen.json中
- 成功或已处理过时退出码为0;严重失败时退出码为1
bookmark-poll.js — 定时轮询(最新20条)
bash
node scripts/bookmark-poll.js
- - 刷新OAuth令牌,获取最近20条书签
- 对每个未处理的URL调用triage-url.js
- 成功分类后取消书签(+已处理过的项目)
- 向总线发送bookmarksweepcomplete事件
backlog-sweep.js — 一次性全量清理
bash
node scripts/backlog-sweep.js [--dry-run] [--limit 50] [--delay 3] [--no-unbookmark]
- - 遍历所有书签(直到API限制)
- --dry-run:列出URL但不进行分类或取消书签
- --limit N:处理N条书签后停止
- --delay N:分类调用之间的秒数(默认:3)
- 向总线发送backlogsweepcomplete事件
poll-channel.js — Discord提交处理器
bash
node scripts/poll-channel.js
或通过包装脚本:bash scripts/run-poll.sh
- - 每2分钟轮询Discord频道,检查新消息
- 从消息内容中提取URL
- 对每个未处理的URL调用triage-url.js
- 设计为作为持久化launchd服务运行
卡片格式
层级3–10(工作相关):
🗂️ 标题 @用户名
• 亮点:新颖/令人印象深刻的内容
• 重要性:与你工作的关联
• 关键特性:[列表]
• 建议操作:具体的下一步
• 层级:🥈 Tier 2 — 值得追踪(7/10)— 相关性摘要
• 新鲜度:🌿 常青 | 主题:#ai
• 标签:#tool #infra
• 链接:
层级1–2(无关/个人):
📌 标题 @用户名 — 一句话摘要 #personal
层级指南
🥈 值得追踪 | 跟进并尽快调研 |
| 5–6 | 🥉 知识文档 | 在一个sprint内回顾 |
| 3–4 | 📌 低优先级 | 有趣但不可操作 |
| 1–2 | 📌 无关 | 个人/娱乐 — 仅一句话 |
新鲜度值
- - evergreen 🌿 — 永恒,2年后仍然有效
- dated 📅 — 相关但会过时(基准