gogcli 邮件打开追踪查询实战:gog gmail track opens 命令全解
【免费下载链接】gogcliGoogle Workspace in your terminal.项目地址: https://gitcode.com/GitHub_Trending/gogcl/gogcli
在 gogcli(一个把 Google Workspace 能力装进终端的命令行工具)中,gog gmail track opens是 Gmail 打开追踪(email open tracking)链路的“查询端”。本篇以命令参考页 gog-gmail-track-opens.md 为主体,完整覆盖该命令的用法、全部参数与输出格式,并结合 internal/cmd/gmail_track_opens.go 的源码实现与 docs/email-tracking.md 的架构说明,讲清楚两条查询路径(按 tracking ID 精确查询 / 按收件人与时间范围的管理端点查询)、时间过滤的解析规则,以及它与send --track、Cloudflare Worker 之间的调用关系。读完本篇,你可以独立完成从追踪配置检查、命令执行到结果排错的完整流程。
命令定位与前置条件
gog gmail track opens用于查询邮件打开记录(Query email opens),是gog gmail track命令组(Email open tracking)下的四个子命令之一。从 internal/cmd/gmail_track.go 可以看到该命令组的完整结构:
gog gmail track setup—— Set up email tracking(部署 Cloudflare Worker)gog gmail track opens—— Query email opens(本命令)gog gmail track status—— Show tracking configuration statusgog gmail track key—— Manage tracking encryption keys
该命令本身只读、不产生任何变更,但它依赖追踪链路已经就绪。在 internal/cmd/gmail_track_opens.go 的Run入口中,命令首先调用loadTrackingConfigForAccount加载当前账号的追踪配置,随后检查cfg.IsConfigured();未就绪时直接报错并提示:
tracking not configured; run 'gog gmail track setup' first从 internal/tracking/config.go 的IsConfigured实现看,配置就绪需要同时满足三个条件:enabled == true、worker_url非空、tracking key 非空。也就是说,必须先执行 gog gmail track setup(例如gog gmail track setup --worker-url https://gog-email-tracker.<acct>.workers.dev),本地配置与密钥(tracking/admin key,默认存放在 keyring 而非 JSON 文件)齐备后,track opens才能工作。
用法与参数
命令基本形式(mail/email为gmail的同义别名):
gog gmail (mail,email) track opens [<tracking-id>] [flags]命令自身的参数只有三个(定义于 internal/cmd/gmail_track_opens.go 的GmailTrackOpensCmd结构体):
| 参数 | 类型 | 说明 |
|---|---|---|
<tracking-id> | 位置参数(可选) | 发送命令返回的追踪 ID;提供时走精确查询路径 |
--to | string | 按收件人邮箱过滤(走管理端点路径) |
--since | string | 按时间过滤,如'24h'、'2024-01-01'(走管理端点路径) |
其余均为 gogcli 全局通用 flag。以下完整继承命令参考页 gog-gmail-track-opens.md(由gog schema --json生成,运行make docs-commands重新生成)的 Flags 表,便于按需查阅:
| Flag | 类型 | 默认值 | 说明 |
|---|---|---|---|
--access-token | string | 直接使用提供的 access token(绕过已存储的 refresh token;token 约 1 小时后过期) | |
-a/--account/--acct | string | 账号邮箱、别名或 auto,用于已认证的 Google API 命令 | |
--client | string | OAuth client 名称(选择已存储的凭据 + token bucket) | |
--color | string | auto | 颜色输出:auto|always|never |
--disable-commands | string | 禁用命令的逗号分隔列表;支持点路径 | |
-n/--dry-run/--dryrun/--noop/--preview | bool | 不执行变更;打印预期动作后成功退出 | |
--enable-commands | string | 启用命令前缀的逗号分隔列表;支持点路径(收窄 CLI 范围) | |
--enable-commands-exact | string | 精确启用命令的逗号分隔列表;支持点路径,父命令不会启用子命令 | |
-y/--force/--assume-yes/--yes | bool | 跳过破坏性命令的确认提示 | |
--gmail-no-send | bool | false | 阻止 Gmail 发送操作(agent 安全开关) |
-h/--help | kong.helpFlag | 显示上下文相关的帮助 | |
--home | string | 覆盖 gogcli 配置/数据/状态/缓存根目录(等价于GOG_HOME) | |
-j/--json/--machine | bool | false | 以 JSON 输出到 stdout(最适合脚本化) |
--no-input/--non-interactive/--noninteractive | bool | 从不交互提示,失败即退出(适用于 CI) | |
-p/--plain/--tsv | bool | false | 输出稳定、可解析的文本到 stdout(TSV;无颜色) |
--quota-project | string | 用于 API 计费的 Google Cloud 项目(以 X-Goog-User-Project 发送;部分 API 在--access-token或 ADC 场景下要求它) | |
--readonly | bool | false | 运行时阻止变更类 API 请求;auth add也会请求只读 OAuth scope |
--results-only | bool | JSON 模式下仅输出主结果(丢弃 envelope 字段如 nextPageToken) | |
--select/--pick/--project | string | JSON 模式下选择逗号分隔的字段(尽力而为;支持点路径)。更多命令建议使用--fields | |
--since | string | 按时间过滤(如'24h'、'2024-01-01') | |
--to | string | 按收件人邮箱过滤 | |
-v/--verbose | bool | 启用详细日志 | |
--version | kong.VersionFlag | 打印版本后退出 | |
--wrap-untrusted | bool | false | JSON/raw 输出中,为拉取的文本字段包裹外部不可信内容标记 |
注意--select的别名--project:这是 kong 参数解析层面的既有别名,与--quota-project的计费项目不是一回事,使用时不要混淆。
两条查询路径的源码解析
track opens的行为完全由是否提供了<tracking-id>位置参数决定,Run 方法 的分支逻辑非常清晰:
// Query by tracking ID if c.TrackingID != "" { return c.queryByTrackingID(ctx, cfg, u) } // Query via admin endpoint return c.queryAdmin(ctx, cfg, u)路径一:按 tracking ID 精确查询
queryByTrackingID向 Worker 的公共查询端点发起GET {worker_url}/q/{tracking_id}(无鉴权头,因为 pixel 链路本身就是无状态的),要求返回 200,否则把响应体一并包进错误信息(tracker returned <code>: <body>)。响应 JSON 被解码为如下结构并输出:
| 输出字段(TSV 模式) | 对应 JSON 字段 | 含义 |
|---|---|---|
tracking_id | tracking_id | 追踪 ID |
recipient | recipient | 收件人邮箱 |
sent_at | sent_at | 发送时间 |
opens_total | total_opens | 总打开次数 |
opens_human | human_opens | 人类打开次数(剔除 bot 后) |
first_human_open | first_human_open.at | 首次人类打开时间 |
first_human_open_location | first_human_open.location | 粗粒度地理位置(城市, 区域格式),缺失时显示unknown |
加-j/--json时,命令直接把 Worker 的原始 JSON 透传输出(经outfmt.WriteJSON封装),适合脚本进一步处理。
路径二:管理端点查询(/opens)
不提供 tracking ID 时走queryAdmin,其前提是本地配置里有admin key(缺失时报错提示重新执行track setup)。该路径构造GET {worker_url}/opens请求,附带两个可选查询参数:
recipient←--tosince←--since(解析后的 RFC3339 时间戳)
请求头携带Authorization: Bearer <admin_key>。错误处理上有两处值得注意的实现细节:401 会被特判为更友好的unauthorized: admin key may be incorrect;其它非 200 状态则连同响应体一起报出。响应中每条打开记录包含tracking_id、recipient、subject_hash、sent_at、opened_at、is_bot与location(城市/区域/国家)。
TSV 模式下的输出按行打印,列为:tracking_id recipient opened_at is_bot subject_hash 地点;没有任何打开记录时输出opens 0。JSON 模式则整体输出{ "opens": [...] }结构。
对照 docs/email-tracking.md 的说明可以确认边界约束:管理端/opens查询默认返回 100 行,单次请求上限 500 行——因此用--since圈定时间窗口是控制结果规模的正确做法。
--since的时间解析规则
--since不是直接透传字符串,而是经过 parseTrackingSince 处理:先经timeparse.ParseSince解析(实现位于 internal/timeparse),支持三种形态:
- 相对时长,如
24h、15m; - 日期
YYYY-MM-DD(如2024-01-01); - RFC3339 时间戳。
解析成功后统一格式化为 RFC3339(含纳秒时取 RFC3339Nano)再作为查询参数发给 Worker。传空值会得到empty --since用法错误,无法解析的值会提示invalid --since ... (use duration like 24h, date YYYY-MM-DD, or RFC3339)。
与上游追踪链路的配合
单独看track opens只是查询动作;要让它有数据可查,上游链路(见 docs/email-tracking.md 的整体描述)需要完整跑通:
- 配置与部署:
gog gmail track setup --worker-url …创建按账号隔离的配置与密钥;加--deploy可由 wrangler 自动创建 D1 并部署 Worker。默认 Worker 名为gog-email-tracker-<account>,--worker-dir默认指向 internal/tracking/worker。 - 发送带追踪邮件:
gog gmail send --to … --subject … --body-html … --track会在 HTML 正文注入 1×1 像素 URL;多收件人场景可加--track-split让每个收件人收到独立消息与独立 tracking ID(这正是track opens <tracking-id>能按收件人精确归因的前提)。 - Worker 记录打开:Worker 收到 pixel 请求后在 D1 写入一条 open 记录并返回透明像素;同一 tracking ID + IP + User-Agent 在一小时窗口内去重,单 IP 每小时最多 100 条有效记录。
- 打开归因:Worker 内嵌 bot 判别逻辑(internal/tracking/worker/src/bot.ts),
GoogleImageProxy视为真人代理打开,Apple MPP 中继、Outlook 预取、投递后 2 秒内的打开、安全扫描器 UA 等会被标记为 bot——这就是track opens输出中opens_human/is_bot字段的来源。
因此当你得到opens_total很大但opens_human为 0 的结果时,大概率是客户端自动预取图片(或 MPP 类隐私代理)所致,属于预期行为而非链路故障。
配置存储与账号隔离
追踪配置按账号(邮箱小写归一化)存储。从 internal/tracking/config.go 的Config结构看,配置包含enabled、worker_url、worker_name、D1 数据库名称/ID、secrets_in_keyring标志、当前 tracking key 与版本号列表等;敏感密钥(tracking key / admin key)在secrets_in_keyring为真、或 JSON 中为空(旧版回退行为)时,从 keyring 动态加载回填,不落明文。测试 internal/cmd/gmail_track_cmd_test.go 进一步验证了运行时布局的隔离性:配置写入运行时 state 目录下的tracking.json,不受环境变量指向的其他目录影响——这对多账号、CI 环境(配合--home/--no-input)下的行为可预期性是关键保障。
排错速查
结合命令参考页与 docs/email-tracking.md 的 Troubleshooting 小节,针对track opens的常见失败与排查动作:
| 现象 | 可能原因与处理 |
|---|---|
tracking not configured; run 'gog gmail track setup' first | 尚未配置;执行track setup(可用gog gmail track status查看当前状态) |
tracking admin key not configured | 未提供 tracking ID 走了/opens路径但本地无 admin key;重跑track setup |
unauthorized: admin key may be incorrect | Worker 侧ADMIN_KEYsecret 与本地不一致;重新wrangler secret put ADMIN_KEY并核对 |
tracker returned 4xx/5xx: … | 查看响应体细节;确认worker_url可达、Worker 已部署 |
invalid --since "…" | 改用24h、YYYY-MM-DD或 RFC3339 格式 |
| 新消息无打开记录 | 确认 HTML 原文中确有注入的 pixel(在邮件客户端查看“原始内容”);部分客户端默认阻止图片,图片加载后才算“打开” |
| 轮换 key 后新邮件无打开 | 用gog gmail track status核对本地当前版本,确认 Worker 已部署对应的TRACKING_KEY_V<N>与TRACKING_CURRENT_KEY_VERSION |
小结
gog gmail track opens本身是一个轻量只读命令,但它是整条“发送端注入 pixel → Cloudflare Worker + D1 记录 → bot 判别与去重 → 终端查询”链路的观测入口:带<tracking-id>时走 Worker 公共查询端点拿到含human_opens与首次打开位置的聚合视图,不带时走带 Bearer 鉴权的/opens管理端点按--to/--since过滤明细。配合-j/--json与--since的时长/日期/RFC3339 三种写法,该命令可以干净地嵌入 shell 脚本与 agent 工作流;而密钥经 keyring 管理、按账号隔离存储、Worker 侧 90 天自动清理的设计,则界定了这套打开追踪在隐私与运维上的边界。
【免费下载链接】gogcliGoogle Workspace in your terminal.项目地址: https://gitcode.com/GitHub_Trending/gogcl/gogcli
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考