1. 短信取件码自动进滴答清单卡在哪一步快递到了驿站短信躺在通知栏里看一眼“哦到了”然后继续忙别的。等想起来去取取件码已经被十几条推送压到下面了。这个场景我猜你也不陌生。我试过用正则把取件码提取出来推到飞书机器人通知是有了但飞书消息只是“通知”取没取、看没看没法标记一堆消息堆在那里还是分不清哪个取了哪个没取。后来想通了这事儿得用 GTD 思路收到短信自动往滴答清单里加一条待办取完货打个勾闭环。但落地的时候卡住了——读取短信的工具比如通知滤盒发出来的 Webhook 格式是固定的跟滴答清单的 API 接口要求对不上两头接不上。客户端改不了那就只能在服务端做中转。为了这点小功能买服务器太重了找了一圈EdgeOne Pages 比较合适有国内节点延迟低免费阶段拿来跑轻量 Serverless 代码刚好。但真正写代码的时候问题又来了——EdgeOne Pages 是新产品AI 对它的 API 不熟不给文档就容易瞎编滴答清单的 OAuth 授权流程也有点绕Token 写入 KV 存储的逻辑反复出错。这篇文章要解决的就是这条链路用自然语言指挥 Gemini 3.0在 EdgeOne Pages 上部署 Serverless Webhook把短信取件码自动写进滴答清单。适合谁看如果你也在用通知滤盒、Tasker 这类工具做短信自动化或者想找一个轻量的 Serverless 中转方案这篇可以直接跟着做。核心工具是 TaoToken 统一 Key 配置 Gemini 3.0 EdgeOne Pages Functions 滴答清单 Open API。2. TaoToken 统一 Key 配置settings.json 与 config.toml 骨架在开始写业务代码之前先把模型调用的配置搞定。TaoToken 的作用是提供一个统一的 API Key让你不用在多个模型平台之间来回切换。官网入口在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 。如果你用的是 VS Code 系的 AI 编程插件比如 Continue、Cline 这类配置一般写在settings.json里。下面是一个可复制的骨架把YOUR_TAOTOKEN_KEY换成你在控制台生成的 Key 就行{ taotoken.apiKey: YOUR_TAOTOKEN_KEY, taotoken.baseUrl: https://taotoken.net/api, taotoken.model: gemini-3.0-pro, taotoken.timeout: 60000, taotoken.maxTokens: 8192 }如果你用的是命令行工具或者某些支持 TOML 配置的 Agent 框架config.toml的写法是这样的[provider.taotoken] api_key YOUR_TAOTOKEN_KEY base_url https://taotoken.net/api model gemini-3.0-pro timeout 60 [provider.taotoken.params] temperature 0.3 max_tokens 8192这里有几个参数值得说明。temperature设成 0.3 是因为写代码场景需要稳定输出太高了 AI 容易自由发挥。max_tokens给到 8192 是因为 EdgeOne Pages 的函数代码加上注释和错误处理长度不算短。timeout设 60 秒是给网络波动留余量实测下来 Gemini 3.0 在这个场景下响应速度够快很少超时。Key 的获取路径进入控制台后找到 API Keys 页面新建一个 Key复制出来填到上面的配置里。如果你还没注册可以先从模型对话页面体验一下 Gemini 3.0 的代码能力确认符合预期再接入。注意API Key 不要硬编码在业务代码里提交到 Git 仓库。EdgeOne Pages 支持环境变量把 Key 放在环境变量里代码里用env.TAOTOKEN_KEY读取。配置写完之后可以先跑一个最小验证请求确认 Key 和网络都通curl -X POST https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer YOUR_TAOTOKEN_KEY \ -H Content-Type: application/json \ -d { model: gemini-3.0-pro, messages: [{role: user, content: 回复 ok}], max_tokens: 10 }返回里能看到choices[0].message.content包含ok就说明配置没问题可以进入下一步写业务函数了。3. EdgeOne Pages Functions 接收短信 Webhook这一步的目标是创建一个边缘函数接收通知滤盒发来的短信内容提取驿站名称和取件码然后调用滴答清单 API 创建待办。先建项目。在 EdgeOne Pages 控制台新建一个项目项目名比如kdToDida。然后在项目里创建 Functions 目录新建kdSMS.js。EdgeOne Pages Functions 的入口格式是这样的export async function onRequest(context) { const { request, env } context; const url new URL(request.url); const key url.searchParams.get(key); const text url.searchParams.get(text); if (!key || !text) { return new Response(JSON.stringify({ error: missing key or text }), { status: 400, headers: { Content-Type: application/json } }); } // 提取驿站和取件码 const parsed extractPickupInfo(text); if (!parsed) { await sendAlert(env, text); return new Response(JSON.stringify({ error: parse failed, raw: text }), { status: 422, headers: { Content-Type: application/json } }); } // 读取该用户的滴答清单 Token const accessToken await env.KD.get(${key}.accesstoken); if (!accessToken) { return new Response(JSON.stringify({ error: no token for user }), { status: 401, headers: { Content-Type: application/json } }); } // 创建待办 const result await createDidaTask(accessToken, parsed, text); return new Response(JSON.stringify(result), { headers: { Content-Type: application/json } }); }提取逻辑用正则匹配两种常见短信模板。菜鸟驿站的格式是“【菜鸟驿站】您的xx包裹已到xx店请23:59前凭6-5-0261扫码开门自助取件”兔喜生活的格式是“【兔喜生活】您有包裹已到达xx店取件码为49-5859地址:xx”。对应的正则function extractPickupInfo(text) { // 菜鸟驿站凭6-5-0261 let match text.match(/凭([0-9\-])扫码/); if (match) { const station text.match(/已到(.?)店/); return { title: ${station ? station[1] : 菜鸟驿站} ${match[1]}, code: match[1] }; } // 兔喜生活取件码为49-5859 match text.match(/取件码为([0-9\-])/); if (match) { const station text.match(/到达(.?)店/); return { title: ${station ? station[1] : 兔喜生活} ${match[1]}, code: match[1] }; } return null; }创建待办的函数调用滴答清单 Open API。注意时区要设成 UTC8提醒时间按规则处理当天 17:45 以前的设为当天 17:45以后的设为第二天 17:45。async function createDidaTask(accessToken, parsed, rawText) { const now new Date(); const beijingOffset 8 * 60; const localNow new Date(now.getTime() (beijingOffset now.getTimezoneOffset()) * 60000); const hour localNow.getHours(); const minute localNow.getMinutes(); let dueDate; if (hour 17 || (hour 17 minute 45)) { dueDate new Date(localNow); dueDate.setHours(17, 45, 0, 0); } else { dueDate new Date(localNow); dueDate.setDate(dueDate.getDate() 1); dueDate.setHours(17, 45, 0, 0); } const dueString dueDate.toISOString().replace(Z, 0800); const resp await fetch(https://api.dida365.com/open/v1/task, { method: POST, headers: { Authorization: Bearer ${accessToken}, Content-Type: application/json }, body: JSON.stringify({ title: parsed.title, content: rawText, dueDate: dueString, timeZone: Asia/Shanghai }) }); return await resp.json(); }告警函数用飞书 Webhook把短信原文带上方便排查解析失败的情况async function sendAlert(env, rawText) { const webhook env.FEISHU_WEBHOOK; if (!webhook) return; await fetch(webhook, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ msg_type: text, content: { text: 取件码解析失败原文${rawText} } }) }); }部署之前在本地测试一下。EdgeOne Pages 提供了 CLI 工具运行edgeone pages dev启动本地环境默认端口 8088。访问http://localhost:8088/kdSMS?keytesttext【菜鸟驿站】您的包裹已到菜鸟驿站店请23:59前凭6-5-0261扫码开门自助取件看返回的 JSON 里 title 是不是“菜鸟驿站 6-5-0261”。4. 滴答清单 OAuth 授权与 KV 存储写入滴答清单的写入需要 OAuth 授权拿到 Access Token。流程是用户访问/dida/login?keyusername跳转到滴答清单授权页授权后回调到/dida/callback后端用 code 换 Token写入 KV 存储。先创建 KV 命名空间。在 EdgeOne Pages 控制台找到 KV 存储新建一个命名空间名字比如KD然后绑定到项目。绑定之后代码里用env.KD访问。授权跳转函数export async function onRequest(context) { const { request, env } context; const url new URL(request.url); const key url.searchParams.get(key); if (!key) { return new Response(missing key, { status: 400 }); } const clientId env.DIDA_CLIENT_ID; const redirectUri encodeURIComponent(https://你的域名/dida/callback); const state Math.random().toString(36).slice(2); await env.KD.put(state.${state}, key, { expirationTtl: 600 }); const authUrl https://dida365.com/oauth/authorize?client_id${clientId}scopetasks:write%20tasks:readresponse_typecoderedirect_uri${redirectUri}state${state}; return Response.redirect(authUrl, 302); }回调函数用 code 换 Token。注意滴答清单要求把client_id:client_secret做 Base64 编码后放在 Authorization 头里export async function onRequest(context) { const { request, env } context; const url new URL(request.url); const code url.searchParams.get(code); const state url.searchParams.get(state); const key await env.KD.get(state.${state}); if (!key) { return new Response(invalid state, { status: 400 }); } const clientId env.DIDA_CLIENT_ID; const clientSecret env.DIDA_CLIENT_SECRET; const basicAuth btoa(${clientId}:${clientSecret}); const tokenResp await fetch(https://dida365.com/oauth/token, { method: POST, headers: { Authorization: Basic ${basicAuth}, Content-Type: application/x-www-form-urlencoded }, body: new URLSearchParams({ code: code, grant_type: authorization_code, scope: tasks:read tasks:write, redirect_uri: https://你的域名/dida/callback }) }); const tokenData await tokenResp.json(); if (!tokenData.access_token) { return new Response(JSON.stringify(tokenData), { status: 500 }); } await env.KD.put(${key}.accesstoken, tokenData.access_token); await env.KD.put(${key}.refreshtoken, tokenData.refresh_token); return new Response(授权成功可以关闭页面, { headers: { Content-Type: text/plain; charsetutf-8 } }); }这里踩过的坑一开始 KV 写入逻辑写成了从env里取变量名再 put实际上 EdgeOne Pages 的 KV 绑定之后直接用env.KD.put()就行不需要多一层。另外btoa在边缘函数环境里是可用的但要注意传入的字符串不能有非 Latin1 字符client_id 和 client_secret 都是 ASCII没问题。提示滴答清单的 Access Token 有效期是半年其实手动获取一次填进 KV 也能用很久。写自动化授权接口主要是为了多用户场景如果你只是自己用手动填 Token 更省事。5. 验证请求与端到端成功结果配置全部完成之后按顺序验证三个环节。第一验证短信接收和解析。访问https://你的域名/kdSMS?key你的keytext【菜鸟驿站】您的包裹已到菜鸟驿站店请23:59前凭6-5-0261扫码开门自助取件预期返回{ title: 菜鸟驿站 6-5-0261, code: 6-5-0261, taskId: xxxxxxxx }如果返回 422说明正则没匹配上检查短信模板是不是有变化。如果返回 401说明 KV 里没有这个 key 对应的 Token需要先走一遍授权流程。第二验证滴答清单待办创建。打开滴答清单 App 或者网页版看收件箱里是不是多了一条标题为“菜鸟驿站 6-5-0261”的待办内容里是完整短信原文提醒时间按规则设置。实测下来从请求发出到待办出现延迟在 1 秒以内。第三验证告警链路。故意发一条不匹配任何模板的短信https://你的域名/kdSMS?key你的keytext这是一条测试短信没有取件码飞书机器人应该收到告警消息内容包含短信原文。这一步验证的是解析失败时的兜底逻辑确保不会静默丢数据。端到端跑通之后在通知滤盒里配置 Webhook 规则。进入通知滤盒的“智能”-“增强”-“Webhook”创建规则App 选包含“短信”关键词填“菜鸟驿站”或“兔喜生活”URL 填https://你的域名/kdSMS?key你的keytext{android.text}保存之后等下一次快递短信到达滴答清单里就会自动出现待办。取完货打个勾闭环完成。6. 本篇常见错排查报错一Failed to construct Request: only String/ArrayBuffer/ArrayBufferView/Blob/ReadableStream/FormData is allowed as the body initializer这个报错出现在 OAuth 回调函数里原因是fetch的 body 传了一个对象而不是字符串。修复方式是把URLSearchParams转成字符串或者直接用URLSearchParams实例它本身是可接受的 body 类型。检查代码里body: new URLSearchParams({...})有没有被误写成body: {...}。报错二KV 写入成功但读不到EdgeOne Pages 的 KV 绑定名要和代码里用的一致。控制台绑定的时候填的变量名是KD代码里就必须用env.KD。如果绑定名是MY_KV代码里写env.KD就会拿到 undefined。另外 KV 的写入不是强一致的刚写完立刻读可能读不到等几秒再试。报错三滴答清单返回 401 UnauthorizedToken 过期或者 scope 不对。滴答清单的 Access Token 有效期半年但如果用户手动撤销了授权Token 会立即失效。检查 KV 里存的 Token 是不是最新的scope 是否包含tasks:write。如果 Token 过期用 refresh_token 换新的或者重新走一遍授权流程。报错四正则匹配不到取件码不同驿站的短信模板会变。菜鸟驿站有时候是“凭6-5-0261扫码”有时候是“取件码6-5-0261”兔喜生活有时候是“取件码为49-5859”有时候是“取件码49-5859”。建议把正则写成更宽松的匹配比如/取件码[为]?([0-9\-])/和/凭([0-9\-])扫码/都覆盖上。如果还是匹配不到看飞书告警里的原文把新模板加进去。报错五EdgeOne Pages 本地测试端口被占用edgeone pages dev默认用 8088 端口如果被占用会启动失败。可以加--port 8090换一个端口。另外本地测试的时候 KV 存储用的是本地模拟和线上数据不互通测试 Token 写入需要部署到线上再验证。排障过程中如果遇到 API 调用问题可以对照接入文档检查请求格式。模型调用相关的配置问题可以在模型对话页面发一条测试消息确认 Key 是否有效。如果你打算长期用这套方案做自动化Coding Plan 提供了更稳定的调用额度适合 Agent 类场景。整套方案跑通之后每天打开滴答清单哪些快递要拿一目了然。取完货打个勾这种掌控感比在通知栏里翻短信强太多了。