把 Webhook 文档丢给 Devin Desktop,20 分钟搞定 LINE 消息接收服务
Devin Desktop——2026 年初由 Windsurf 更名而来,搭载的 SWE-1.6 模型输出速度达到 950 tokens/s——是目前我身边开发者讨论最多的 AI 编码工具。用法很直接:把参考文档丢给它,描述你要什么,迭代到能跑为止。我想试试这套流程用在 webhook API 上效果如何。
任务:写一个 Python 服务,接收 LINE 消息的 webhook 事件,结构化日志输出——那种小团队会部署在消息队列或工单系统前面的 handler。不需要 LINE 官方账号、不需要 Messaging API 凭证、不需要管理 channel access token。只要入站消息以标准化 JSON 的形式到达。
最终产物
一个 FastAPI 服务(大约 40 行代码):
- 接收 UnifyPort 统一 webhook 的
message.received事件 - 使用
signing_secret进行 HMAC-SHA256 签名校验 - 将每条消息以结构化 JSON 记录日志——provider、sender、text、timestamp
- 所有事件返回 200,签名无效返回 401
耗时:约 20 分钟。你需要一个 UnifyPort 工作区并连接 LINE 账号(LINE App 扫码即可),以及 Python 3.10+。
为什么不直接用 LINE Messaging API?
官方路径:
- 创建 LINE 官方账号——需要 LINE Business ID,需要企业或实名个人认证
- 在 LINE Developers 控制台启用 Messaging API——配置 channel、生成 channel access token、设置 webhook URL
- 解析 LINE 的 webhook payload:事件结构是
{"events": [{"type": "message", "message": {"type": "text", "text": "..."}, "source": {"userId": "U..."}}]}——嵌套的、LINE 专有格式 - 验证签名:使用 channel secret 校验
x-line-signature——LINE 用的是 Base64 编码的 HMAC-SHA256,不是 hex - 处理 reply token:每个事件带一个有效期 30 秒的
replyToken,超时回复就静默失败
代码能跑,但只能跑 LINE。webhook payload 格式、签名方式、reply token 机制——没有一样能复用到 WhatsApp 或 Telegram。加第二个平台就要从零开始做第二套集成。
UnifyPort 的非官方接口连接个人 LINE 账号——扫码连接,不需要官方账号——将每条入站消息标准化为 message.received 事件,和 WhatsApp、Telegram、TikTok、Zalo、X 的格式完全一致。一个 handler 覆盖六个平台。
准备:把 API 文档喂给 Devin Desktop
打开 Devin Desktop,新建会话。第一次提问前,给它上下文:
- 粘贴 UnifyPort webhook 文档中的
message.received事件 payload:{ "event": "message.received", "account_id": "acct_7kQnWx", "provider": "line", "from": "user_a3f82c", "text": "能改到周五吗?", "timestamp": 1751270400, "message_id": "line_msg_5e9d21" } - 粘贴 HMAC-SHA256 签名校验部分——
x-unifyport-signatureheader、用signing_secret对原始请求体计算 hex digest
Devin 将粘贴的内容作为会话上下文索引。SWE-1.6 Fast 以 950 tok/s 处理,文档消化只需几秒。Cursor、Claude Code、Copilot 也一样——粘贴或附加即可。工具可以换,文档才是关键。
构建过程
第一个 prompt——handler 骨架:
根据我刚粘贴的 UnifyPort webhook 文档,写一个 FastAPI 服务,POST /webhook 路由。当事件为 “message.received” 时,用 Python 的 logging 模块以结构化 JSON 记录 provider、from、text 和 timestamp。所有事件返回 200。用 uvicorn 启动。
Devin 读取上下文并生成:
from fastapi import FastAPI, Request
import logging, json
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("inbound")
app = FastAPI()
@app.post("/webhook")
async def webhook(request: Request):
evt = await request.json()
if evt.get("event") == "message.received":
logger.info(json.dumps({
"provider": evt["provider"],
"from": evt["from"],
"text": evt["text"],
"timestamp": evt["timestamp"],
"message_id": evt["message_id"],
}))
return {"status": "ok"}
十二行 handler。没有 LINE 专用 import,没有 linebot SDK,没有 channel access token。事件结构足够扁平,无需额外解析。
第二个 prompt——签名校验:
加上 HMAC-SHA256 签名校验。header 是 x-unifyport-signature,secret 从环境变量 UNIFYPORT_SIGNING_SECRET 读取,HMAC 对原始请求体 bytes 计算——不是重新序列化的 JSON。校验失败返回 401。使用 timing-safe 比较。
Devin 添加校验层:
import hmac, hashlib, os
SIGNING_SECRET = os.environ["UNIFYPORT_SIGNING_SECRET"]
async def verify_signature(request: Request) -> bytes:
body = await request.body()
sig = request.headers.get("x-unifyport-signature", "")
expected = hmac.new(
SIGNING_SECRET.encode(), body, hashlib.sha256
).hexdigest()
if not hmac.compare_digest(sig, expected):
raise HTTPException(status_code=401, detail="invalid signature")
return body
使用 hmac.compare_digest 进行时序安全比较,操作对象是 request.body()——原始 bytes——而不是重新序列化 request.json()。
第三个 prompt——合并整理:
把签名校验合并到 webhook handler 里。校验通过后再将 body 解析为 JSON。加一个 GET /health 健康检查。加上 uvicorn.run,端口 8000。
完整的 server.py:
from fastapi import FastAPI, Request, HTTPException
import hmac, hashlib, os, json, logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("inbound")
SIGNING_SECRET = os.environ["UNIFYPORT_SIGNING_SECRET"]
app = FastAPI()
@app.get("/health")
async def health():
return {"status": "ok"}
@app.post("/webhook")
async def webhook(request: Request):
body = await request.body()
sig = request.headers.get("x-unifyport-signature", "")
expected = hmac.new(
SIGNING_SECRET.encode(), body, hashlib.sha256
).hexdigest()
if not hmac.compare_digest(sig, expected):
raise HTTPException(status_code=401, detail="invalid signature")
evt = json.loads(body)
if evt.get("event") == "message.received":
logger.info(json.dumps({
"provider": evt["provider"],
"from": evt["from"],
"text": evt["text"],
"timestamp": evt["timestamp"],
"message_id": evt["message_id"],
}))
return {"status": "ok"}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
不到 40 行。签名校验、结构化日志、可以直接部署在任何反向代理后面。
跑起来,看消息进来
在 UnifyPort 工作区连接 LINE 账号——LINE 支持扫码认证,打开 LINE App 扫一下就连上了。不需要 LINE 官方账号、不需要 Business ID、不需要配 Messaging API。
注册一个 webhook 端点指向你的服务器,设置 subscribed_events: ["message.received"] 和 signing_secret。启动服务:
UNIFYPORT_SIGNING_SECRET=your_secret python server.py
让朋友给你的 LINE 发条测试消息。事件到达:
{
"event": "message.received",
"account_id": "acct_3Xk1wL",
"provider": "line",
"from": "user_a3f82c",
"text": "能改到周五吗?",
"timestamp": 1751270400,
"message_id": "line_msg_5e9d21"
}
服务器校验签名,输出结构化日志。如果签名校验失败返回 401——检查 UNIFYPORT_SIGNING_SECRET 是否和 webhook 端点设置的一致。
加 WhatsApp,一行代码都不用改
在同一个工作区连接 WhatsApp 账号,订阅同一个 webhook 端点。WhatsApp 消息到达时,格式完全一样:
{
"event": "message.received",
"account_id": "acct_7kQnWx",
"provider": "whatsapp",
"from": "user_d4f29a",
"text": "订单确认,周一发货",
"timestamp": 1751270460,
"message_id": "wa_msg_8b3e71"
}
同一个 handler,同样的结构化日志。provider 字段从 "line" 变成 "whatsapp",代码路径完全一致。LINE Messaging API 的 handler 要加 WhatsApp 就得从零开始做一套完全不同的集成——不同的 webhook payload、不同的签名格式、不同的 SDK。这个 handler 覆盖六个平台,因为事件结构不变。
这就是这套流程:把 UnifyPort API 文档粘贴给 Devin Desktop,描述你要的 handler,迭代到能跑。工具写代码,标准化 webhook 让代码适用于所有平台。换成 Cursor、Claude Code 或 Copilot 也一样——文档不变,handler 不超过 40 行。对于做跨境电商的团队来说,东南亚市场 LINE、WhatsApp、Zalo 并行,一个 handler 全覆盖,省掉三套独立集成的维护成本。