用 agent 自动化你在 REER 上的工作流

论坛开放 API,你可以让脚本或 agent 代表你读写内容。这篇讲怎么做,以及哪些该做、哪些不该做。

先说结论:不要把密码给 agent

Discourse 有专门的 User API Key 机制,走的是 OAuth 2.0 Device Authorization Grant —— 就是 gh auth login 那套:终端显示一个码,你去网页确认,脚本拿到一把带 scope 的密钥。

比交密码好在三点:scope 可限制(只读还是可写)、随时可撤销、密码从不离开你的脑子。

鉴权:完整可跑的代码

第一步,申请授权码。 需要一对 RSA 密钥,Discourse 会用你的公钥加密返回的 API key。

import json, secrets, subprocess, urllib.request

BASE = "https://reer.dev"
NONCE = secrets.token_hex(16)          # 后面要用它校验,别丢

subprocess.run(["openssl","genrsa","-out","agent_priv.pem","2048"], check=True)
pub = subprocess.run(["openssl","rsa","-in","agent_priv.pem","-pubout"],
                     capture_output=True, text=True, check=True).stdout

def post(path, payload):
    r = urllib.request.Request(BASE + path, data=json.dumps(payload).encode(), method="POST")
    r.add_header("Content-Type", "application/json")
    r.add_header("Accept", "application/json")
    with urllib.request.urlopen(r, timeout=30) as f:
        return json.load(f)

d = post("/user-api-key/device", {
    "nonce": NONCE,
    "client_id": "my-reer-agent",       # 自己起个稳定的标识
    "application_name": "My REER Agent", # 授权页会显示给你看
    "scopes": "read",                    # 先只要读权限
    "public_key": pub,
})

print("打开:", d["verification_uri"])
print("输入:", d["user_code"])

跑完会得到类似:

打开: https://reer.dev/user-api-key/activate
输入: VEGK-YMWK

第二步,轮询等授权。 有效期 600 秒,轮询间隔 5 秒(服务端会告诉你,别写死)。

import time, base64

while True:
    r = post("/user-api-key/device/poll", {"device_code": d["device_code"]})
    if r.get("status") == "authorization_pending":
        time.sleep(d["interval"]); continue
    break

第三步,解密。 返回的是用你公钥加密过的 base64,用私钥解开:

from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding

priv = serialization.load_pem_private_key(open("agent_priv.pem","rb").read(), password=None)
raw  = priv.decrypt(base64.b64decode(r["payload"]), padding.PKCS1v15())
data = json.loads(raw)

assert data["nonce"] == NONCE, "nonce 不匹配,可能被中间人掉包了"
api_key = data["key"]

nonce 那行断言别省。 它是防重放/防掉包的,服务端把你发的 nonce 原样加密回来,对不上就说明这把 key 不是你申请的那次。

第四步,用起来。

req = urllib.request.Request(f"{BASE}/latest.json")
req.add_header("User-Api-Key", api_key)

请求头是 User-Api-Key,不是 Api-Key(后者是管理员全局密钥,两码事)。

值得做的:读为主的自动化

这些是我觉得真正有价值、也最适合 agent 的:

监控与提醒
盯住你关心的分区或标签,有新帖就推到你的 Telegram / 飞书 / 终端。/c/{slug}/{id}.json 和 /tag/{name}.json 都能直接拿 JSON。

个人摘要
每天把新帖拉下来,让模型按你的兴趣筛一遍,只把相关的推给你。论坛的 digest 邮件是按热度排的,你自己做能按你的方向排。

对论坛做检索
把帖子灌进向量库,在自己的工作流里检索。逆向的时候想起"这个问题好像有人讨论过",本地一查就出来,比翻网页快得多。

同步你自己的内容
博客写完的 writeup,脚本同步一份过来。这属于写入,但内容是你自己的,没有稀释问题。

批量打标签 / 整理草稿
自己的帖子自己整理,随便自动化。

写入:可以做,但请想清楚责任

scopes 填 read,write 就能发帖和回复。技术上没有任何阻碍。

但有两件事要说在前面:

第一,账号所有者对内容负责。 不管是你敲的还是 agent 生成的,出现在你账号下的东西就是你的。"是 AI 写的"不是免责理由。

第二,版规的第一条「技术优先」同样适用于机器写的帖子。 我们没有为 AI 内容单独加规则,因为不需要 —— 一个没有信息量的帖子该清理,跟作者是碳基还是硅基无关。反过来说,一篇 agent 辅助整理出来的、真的有干货的帖子,没人会因为它用了 AI 就反对。

社区规范:如果你的账号会自动发帖,请在个人简介里写明。这不是技术强制,是为了让读者知道对面是什么。别人看到"这是自动同步的 RSS"和以为"这是某人手打的经验",判断是不一样的。

速率限制

会撞到的几条:

限制 值
发帖间隔 5 秒
建主题间隔 15 秒
每日主题上限 20
新用户首日回复上限 10

碰到 429 就退避重试,别硬冲。写循环的时候先设一个总量上限,跑飞了的脚本比没写的脚本更糟。

scope 怎么选

scope 能干什么
read 读帖子、分区、标签、通知。大部分自动化只需要这个
write 发帖、回复、编辑、打标签
notifications 读通知、标记已读
message_bus 实时推送,做即时监控用

默认只申请 read。 需要写的时候再单独申请一把,别图省事一次要全。密钥泄露时,只读的那把造成的损失小得多。

撤销

在 /my/preferences/security 可以看到所有授权过的应用并随时撤销。脚本行为不对劲、或者你不用了,就去撤掉。


有人拿这个做出什么有意思的东西,欢迎回帖说说。特别想看监控和检索那两类 —— 那才是 agent 真正省时间的地方。