Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.

📬 OpenAI 邮件可视化接收器

接收邮件后自动解析 OpenAI(验证码、登录链接、登录提醒、账单等)并在日志中以美观、可读的方式展示关键数据,同时保存到 SQLite 方便后续查询。

✉️ 邮件地址

发送到本 val 的专属地址(在 main.tslinks.email 中查看)即可触发处理。

🎲 随机收件地址

Web 页面现在由 React + Vite 构建,支持创建随机 Email trigger 别名,适合用不同收件地址注册/接收验证码。 Dashboard 只展示“当前可用邮箱”,历史生成过的别名会保留在 SQLite 中用于追踪,但不会再作为列表打扰日常使用。

可接收邮箱列表会显示每个 Email trigger 文件已生成过多少个邮箱,也就是本服务成功把该文件改名的次数。非当前邮箱、非 Cookie 修改目标的文件可以直接在列表里删除,用来清理 Val Town 中堆积的 alias-*.ts 文件。

需要先配置:

环境变量作用
TIGA_EMAIL_DASHBOARD_TOKEN可选;首次设置 TOTP 时的管理员引导 token,也兼容自动化 API 鉴权
TIGA_EMAIL_VAL_ID当前 Val 的 id,用于调用 Val Town API 读取/创建/删除 Email trigger 文件
TIGA_VALTOWN_API_KEY推荐使用的 Val Town API token,需要包含 val:write 权限,避免平台保留变量名被覆盖
VAL_TOWN_API_KEY兼容旧配置;若在 Val Town 运行时权限不足,请改用 TIGA_VALTOWN_API_KEY
TIGA_VALTOWN_COOKIE可选;也可以首次登录 Dashboard 后在网页中保存,保存后会持久化到 SQLite

网页中保存的 Val Town Cookie、Val ID、Val Town API token、复用接收器路径/File ID 都会存到服务端 SQLite,不会再依赖浏览器 sessionStorage。因此同一套服务在不同设备上登录后,都能继续使用已经保存的配置和当前邮箱。

🔐 Dashboard TOTP 登录

首次访问 Dashboard 时会进入 TOTP 设置流程:

  1. 页面显示二维码。
  2. 使用认证器 App 扫描二维码。
  3. 输入 6 位动态验证码完成设置。

设置成功后,TOTP 密钥会持久化保存到 SQLite。下次会话过期或换设备访问时,只需要输入认证器中的动态验证码,不再显示二维码或密钥。

如果已配置 TIGA_EMAIL_DASHBOARD_TOKEN,首次设置 TOTP 时需要先输入该 token,避免公开 HTTP 入口被他人抢先初始化。

页面还支持保存 Val Town 登录 Cookie、API token、Val ID 和要复用的接收器文件。 默认复用 main.ts 这个 Email trigger;如果填写了 File ID,会优先用 File ID,避免接口查询路径失败。Cookie/API token 会保存在服务端 SQLite。当前项目处于开发阶段,已登录 Dashboard 会明文回显这些配置,方便直接查看和修改。

Cookie 获取步骤:

  1. 登录 https://www.val.town,打开浏览器 DevTools 的 Network 面板。
  2. 在 Val Town 页面里手动修改一次 Email trigger 的邮箱 label。
  3. 找到 POST https://www.val.town/api/trpc/files.valLabelSet 请求。
  4. 在 Request Headers 里复制完整的 Cookie header 值,或直接复制 __Secure-better-auth.session_token 的 Value 值。
  5. 必须保留登录会话 cookie。当前新版 Val Town 常见的是 __Secure-better-auth.session_token,旧版可能是 __Secure-next-auth.session-token;如果出现 .0.1 这类分片,所有分片都要一起保留。本地 / 非 HTTPS 环境可能去掉 __Secure- 前缀。csrf-tokencallback-url、分析 cookie 通常不是这个接口鉴权的必要值。只粘贴 session token 裸值时,后端会自动补成 __Secure-better-auth.session_token=...

设置 Cookie 后,本项目会优先调用 Val Town Web 前端使用的内部 tRPC 接口 files.valLabelSet,把已配置的现有 Email trigger 改成指定邮箱或 固定前缀-随机后缀@valtown.email,不会反复创建新的 alias-*.ts 文件。 读取、创建和删除 Email trigger 文件都走 Val Town REST API 的 API token;Cookie 只用于这个固定邮箱名改名接口。

如果 Cookie 缺失或失效,Dashboard 不会直接创建系统分配邮箱,而是先提示二次确认。 只有确认后,才会调用官方 Val Town API 新建一个系统分配的 Email trigger 文件,并把这个新文件记录为后续复用对象。缺少 Val ID 或可用的 Val Town API token 时,请求会返回明确错误。

🧱 前端开发

前端工程在 frontend/

cd frontend npm install npm run build

构建产物输出到仓库根目录的 dist/web.ts 会用 Val Town 的静态文件能力服务 dist/index.htmldist/assets/*

Val Town 对单文件大小有限制,前端生产包会把 React、React Query、Lucide 和 QRCode 等大依赖作为 esm.sh 外部 ESM 模块加载,避免把过大的 JS bundle 推送到 Val Town。frontend/package-lock.json 只用于本地构建,不需要部署到 Val Town。

注意:这个自定义邮箱步骤依赖 Val Town 内部接口 /api/trpc/files.valLabelSet,不是公开稳定 API。Cookie 过期或 Val Town 前端接口变更时,需要重新设置 Cookie 或调整代码。

🔐 自动登录验证码接口

给 Codex Vault 这类外部工具使用时,不建议让调用方解析完整收件箱。服务提供两个窄接口:

  1. POST /api/openai-receivers

    • Body: { "email": "tigacodex5121311@valtown.email" }
    • 可传 allowSystemFallback: false,要求必须绑定调用方指定的精确邮箱;这种模式下 Cookie 不可用、改名失败或达到改名上限时会快速返回结构化错误,不会继续创建系统分配邮箱。
    • 如果调用方自己安全保存了 Val Town Cookie,也可以额外传 valTownCookie;服务只把它用于本次“指定邮箱”创建,不会写入 SQLite。
    • 默认会复用已配置的 Email trigger 并改成指定邮箱;如果 Cookie 不可用或固定邮箱改名失败, 服务会自动创建 Val Town 系统分配的新 Email trigger,并把响应里的 email 返回给调用方。
    • 如果当前复用的 Email trigger 达到 Val Town 30 次改名上限,服务会自动创建一个新的 Email trigger 文件,把它设为后续复用对象,并重试绑定本次指定邮箱;成功响应里会带 rotatedManagedReceiver: true,调用方不需要中断注册流程。
    • 需要 TIGA_EMAIL_DASHBOARD_TOKEN 鉴权;如果需要创建精确地址,还需要通过环境变量或 Dashboard 持久化配置 Val ID、Val Town API token、Val Town Cookie 和复用接收器路径/File ID。
    • 失败响应会带 stagedetails / diagnostics.steps,用于定位具体卡点,例如 alias_cache_lookupmanaged_alias_lookupvaltown_label_setsystem_alias_create_apisystem_alias_create_cookie。接口内部有总时限保护, 会在 Val Town 60 秒平台硬超时前返回 alias_operation_deadline_exceeded
  2. GET /api/openai-code?email=...&notBeforeMs=...&waitMs=25000

    • 作用:只返回目标邮箱最新的 OpenAI 6 位验证码。
    • 成功返回:
{ "ok": true, "email": "tigacodex5121311@valtown.email", "verificationCode": "915219", "receivedAt": "2026-05-16T07:20:00.000Z" }

未收到验证码时返回 verification_code_timeout,调用方可以继续轮询或把错误原因展示给用户。

📐 项目结构

Rendering mermaid diagram...
文件作用
main.tsEmail 触发入口,串起解析→渲染→存储
parser.ts识别 OpenAI 邮件、分类、抽取验证码 / 链接 / 登录信息
renderer.ts把解析结果排成带框线、对齐良好的控制台输出(支持中文/emoji)
storage.ts把每封邮件存入 received_emails_v1
test.ts本地模拟测试,可直接 Run

🔑 提取的关键字段

  • 验证码:6 位数字(多种正则兜底,截图里的 915219 可被识别)
  • 登录链接auth.openai.com / magic link
  • 登录提醒:IP / 位置 / 设备 / 时间
  • 附件列表:名称 / 类型 / 大小
  • 基本信息:发件人 / 域名 / 主题 / 接收时间(北京时间)

🖼️ 日志样例

┌── 📬 收到新邮件  🔐 验证码 (Verification Code) ────────────────┐
├── 基本信息 ────────────────────────────────────────────────────┤
│ 发件人:       OpenAI <noreply@tm.openai.com>                   │
│ 域名:         tm.openai.com                                    │
│ 收件人:       leixinlin1@example.com                           │
│ 主题:         你的 ChatGPT 临时验证码                          │
│ 接收时间:     2026/05/12 11:31:28 (北京时间)                   │
│ 是否 OpenAI:  ✅ 是                                            │
├── 🔑 关键数据 ─────────────────────────────────────────────────┤
│ 验证码:       🔢  915219                                       │
├── 📝 正文预览 ─────────────────────────────────────────────────┤
│ OpenAI 输入此临时验证码以继续: 915219 如果并非你本人尝试创建  │
│ ChatGPT 帐户,请忽略此电子邮件。 谨致问候 ChatGPT 团队          │
├── 📊 元信息 ───────────────────────────────────────────────────┤
│ 正文长度:     text 79 字符 / html 0 字符                       │
└────────────────────────────────────────────────────────────────┘

🗄️ 查询历史邮件

可以在 Val Town 的 SQLite 控制台执行:

SELECT received_at, email_type, from_addr, subject, verification_code FROM received_emails_v1 ORDER BY id DESC LIMIT 20;

🧪 本地测试

打开 test.ts 点击 Run,即可看到两份样例邮件(OpenAI 验证码 + 普通邮件)的可视化日志。

dist
5/16/2026
frontend
5/16/2026
server
5/16/2026
.gitattributes
6/22/2026
.gitignore
5/16/2026
AGENTS.md
6/22/2026
README.md
6/22/2026
E
alias-cvr7fgrop22026052320260523174923.ts
5/23/2026
E
alias-dnshe20260620112949.ts
6/20/2026
E
alias-nerver20260601205135.ts
6/1/2026
E
alias-okx20260623010131.ts
6/22/2026
E
alias-temp20260621153049.ts
6/21/2026
E
alias-temp20260627161601.ts
6/27/2026
E
alias-temp20260628221226.ts
6/28/2026
E
alias-temp20260705103036.ts
7/5/2026
E
alias-temp20260705112606.ts
7/5/2026
E
alias-temp20260705112830.ts
7/5/2026
E
alias-temp20260707233659.ts
7/7/2026
E
alias-temp20260709163822.ts
7/9/2026
E
alias-temp20260718141734.ts
7/18/2026
E
alias-temp20260720123503.ts
7/20/2026
E
alias-temp20260729012949.ts
7/28/2026
E
alias-temp20260729131029.ts
7/29/2026
E
alias-temp20260730002002.ts
7/29/2026
E
alias-temp20260730020334.ts
7/29/2026
E
alias-temp20260808212926.ts
8/8/2026
E
alias-temp20260820135218.ts
8/20/2026
E
alias-temp20260820140744.ts
8/20/2026
E
alias-temp20260820161347.ts
8/20/2026
E
alias-temp20260822135936.ts
8/22/2026
E
alias-temp20260822141350.ts
8/22/2026
E
alias-temp20260822162500.ts
8/22/2026
E
alias-temp20260831191824.ts
8/31/2026
E
alias-temp20260903152936.ts
9/3/2026
E
alias-temp20260909175022.ts
E
alias-temp20260915000942.ts
E
alias-temp20260915154212.ts
alias-tigacodex5121311.ts
5/16/2026
main.ts
6/22/2026
new-file-789.ts
5/21/2026
parser.ts
renderer.ts
6/22/2026
sender-website.ts
storage.ts
test.ts
6/23/2026
verification-backfill.ts
7/23/2026
H
web.ts
7/23/2026