我让 Claude Code 搭了一个 X 私信和提及监听器——完全没碰 X 的 API — UnifyPort
X 的 API 没有免费套餐。读取一条私信收费 $0.015,读取一条帖子收费 $0.005。如果你是一个两人团队,只想知道有人在 X 上给你发了私信或提及了你,官方开发者平台会要求你先充值、创建项目、等待审批、开启按量计费——你还一条消息都没处理。
我跳过了这一切。打开 Claude Code,把 UnifyPort 的 webhook 接口文档粘贴到上下文里,让它用 Node.js 搭建一个 X 私信和提及监听器。四十分钟后,我拿到了一个 Express 服务,能校验每条交付的签名、把事件写入结构化日志、并将告警推送到 Discord 频道。不需要 X 开发者账号,不用按次计费,不用排队审批。
你最终会得到什么
一个 Node.js Express 服务器:
- 接收 UnifyPort 统一 webhook 的
message.received事件 - 使用 HMAC-SHA256 和
signing_secret校验每条交付的签名 - 把每条 X 消息写入
messages.jsonl文件 - 通过 webhook 向 Discord 频道发送告警
耗时:不到一小时。你需要一个 UnifyPort 工作区,通过 UnifyPort Exporter 浏览器扩展连接 X 账号——一键会话导入,无需开发者凭证——以及一个 Discord webhook URL。
准备工作:把文档喂给 Claude Code
在提示之前,先把 API 文档送入代理的上下文。Claude Code 能直接读文件,所以在项目根目录创建 unifyport-reference.md,包含:
message.received事件负载结构x-unifyport-signature请求头和 HMAC-SHA256 签名验证说明POST /v1/webhook-endpoints创建调用
也可以把这些内容直接粘贴到对话中。关键点:真实的字段名输入,正确的代码输出。没有文档,代理会编造一个 X 专用 SDK 或不存在的 dm.received 事件类型。
Cursor、Windsurf、Copilot 用法相同——通过 @Docs、文档面板或直接粘贴文档到上下文。工具是次要的,文档是核心。
构建过程:逐条提示
第一个提示——骨架:
读取 unifyport-reference.md。用 Express 写一个 server.js,包含 POST /webhook 路由。当 event 是 “message.received” 时,打印 provider、from 和 text 字段。所有请求返回 200。
Claude Code 输出:
import express from "express";
const app = express();
app.use(express.json());
app.post("/webhook", (req, res) => {
const evt = req.body;
if (evt.event === "message.received") {
console.log(`[${evt.provider}] ${evt.from}: ${evt.text}`);
}
res.sendStatus(200);
});
app.listen(3000, () => console.log("listening on :3000"));
六行处理器。因为事件结构在六个平台上完全一致,不需要任何 X 专用的解析——evt.provider 显示 "x",但结构和 WhatsApp 或 Telegram 消息完全相同。
第二个提示——签名验证:
每次交付都包含 x-unifyport-signature 请求头。用环境变量中的 signing_secret 进行 HMAC-SHA256 验证。HMAC 必须对原始请求体计算——不能对重新序列化的 JSON 计算。验证失败返回 401。使用时间安全比较。
Claude Code 加上验证层:
import crypto from "crypto";
const SIGNING_SECRET = process.env.UNIFYPORT_SIGNING_SECRET;
app.use(express.json({
verify: (req, _res, buf) => { req.rawBody = buf; }
}));
function isValid(req) {
const sig = req.get("x-unifyport-signature") || "";
const expected = crypto
.createHmac("sha256", SIGNING_SECRET)
.update(req.rawBody)
.digest("hex");
return sig.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
}
verify 回调在 Express 解析 JSON 之前捕获原始 buffer——如果代理用了 req.body 再序列化成 JSON,HMAC 永远不会匹配。因为提示指定了”原始请求体”,代理一次就写对了。
第三个提示——日志记录和 Discord 告警:
添加两个功能:(1) 把每个 message.received 事件追加到 messages.jsonl——每行一个 JSON 对象,包含 timestamp、provider、from、text 和 message_id。(2) 向环境变量 DISCORD_WEBHOOK_URL 指定的 Discord webhook 发送一行告警。
完整的 server.js:
import express from "express";
import crypto from "crypto";
import { appendFileSync } from "fs";
const SIGNING_SECRET = process.env.UNIFYPORT_SIGNING_SECRET;
const DISCORD_WEBHOOK = process.env.DISCORD_WEBHOOK_URL;
const app = express();
app.use(express.json({
verify: (req, _res, buf) => { req.rawBody = buf; }
}));
function isValid(req) {
const sig = req.get("x-unifyport-signature") || "";
const expected = crypto
.createHmac("sha256", SIGNING_SECRET)
.update(req.rawBody)
.digest("hex");
return sig.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
}
app.post("/webhook", async (req, res) => {
if (!isValid(req)) return res.sendStatus(401);
const evt = req.body;
if (evt.event === "message.received") {
const record = JSON.stringify({
timestamp: evt.timestamp,
provider: evt.provider,
from: evt.from,
text: evt.text,
message_id: evt.message_id,
});
appendFileSync("messages.jsonl", record + "\n");
if (DISCORD_WEBHOOK) {
await fetch(DISCORD_WEBHOOK, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
content: `**[${evt.provider}]** ${evt.from}: ${evt.text}`,
}),
});
}
}
res.sendStatus(200);
});
app.listen(3000, () => console.log("listening on :3000"));
不到 50 行。签名验证、磁盘日志、Discord 转发全部就位。处理器不会对 provider 做分支判断——事件结构相同,不管消息来自 X、WhatsApp 还是其他平台。
运行并看到 X 消息到达
在 UnifyPort 控制台连接你的 X 账号。X 使用 UnifyPort Exporter 浏览器扩展进行会话导入——点击扩展图标、授权,几秒钟内账号就连接好了。不需要开发者后台,不需要申请 API 密钥,不需要审批队列。
注册一个 webhook 端点指向你的服务器,设置 subscribed_events: ["message.received"] 和 signing_secret。启动服务,然后从另一个账号给你的 X 账号发一条测试私信。事件到达:
{
"event": "message.received",
"account_id": "acct_7Kp3mR",
"provider": "x",
"from": "user_9d4f2b",
"text": "Hey, are you still taking freelance projects?",
"timestamp": 1750521600,
"message_id": "x_msg_8c1e5a"
}
你的服务验证签名、把记录追加到 messages.jsonl、并将告警发送到 Discord。如果签名校验失败——401——检查 UNIFYPORT_SIGNING_SECRET 是否与 webhook 端点上设置的值一致。
扩展:零代码改动添加 Telegram
在同一个工作区连接 Telegram 账号,订阅同一个 webhook 端点。Telegram 消息以完全相同的结构到达——不同的 provider 字段,结构一模一样:
{
"event": "message.received",
"account_id": "acct_2Xn5wL",
"provider": "telegram",
"from": "user_4a7c8d",
"text": "Can you hop on a call tomorrow?",
"timestamp": 1750521660,
"message_id": "tg_msg_3b9f1e"
}
不需要新的处理器,不需要 Telegram 专用的解析逻辑。messages.jsonl 文件现在同时包含 X 和 Telegram 事件,使用相同的 schema,Discord 显示两个来源的告警。后续添加 WhatsApp、LINE、Zalo 或 TikTok 也是同样的流程——连接账号,已有的处理器自动处理一切。
这和直接对接 X API 的区别不仅是成本——而是准入门槛。X 要求通过审批的开发者账号、充值的项目和按次计费的承诺。UnifyPort 的会话导入路径通过浏览器连接你的个人 X 账号,把每条私信和提及归一化为与其他五个平台完全相同的事件结构。把 Claude Code 指向这个接口——一个事件 schema、一个签名机制——你就能在午饭前跑起一个带验证、日志、告警的监听器。然后添加其他所有平台,代码一行不改。