Skip to content

Latest commit

 

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

endlog · 终末寻访录

《明日方舟:终末地》抽卡记录查询工具 —— 按卡池分组,算出每个 6 星实际花了多少抽

纯 Go 实现,前端经 go:embed 内嵌,产物是单个可执行文件、无外部运行时依赖; 同一份代码同时打包 Windows exe 与 Android APK。

安装: 下载 endlog.exe 双击运行,或安装 endlog.apk
登录: 手机号 + 短信验证码(不接触密码)

运行

Windows

拿到 endlog.exe双击即可。首次启动会在 exe 同目录生成 config.yaml, 并自动打开浏览器到 http://127.0.0.1:8787

配置与抽卡记录都存在 exe 旁边(config.yamldata/), 所以整个目录可以随便挪、也可以放 U 盘带走 —— 但要连 data/ 一起搬, 只搬 exe 的话历史存档不会跟过去。

命令行参数:

endlog.exe -listen 127.0.0.1:9000   # 端口被占用时换一个
endlog.exe -no-open                 # 不自动打开浏览器
endlog.exe -config D:\my.yaml       # 指定配置文件
endlog.exe -version                 # 打印版本号

Android

安装 endlog.apk,打开即用。界面与功能和 Windows 版完全一致 —— 应用内部就是把同一个 Go 服务跑起来,再用 WebView 显示它的页面。

  • 仅支持 arm64-v8a(目前绝大多数安卓手机都是),Android 7.0 以上
  • 数据存在应用私有目录,卸载即清空;换手机不会自动带走存档
  • 两端的存档目前无法互相同步(想要的话得加导入/导出功能)

安卓上有一个必须处理的坑:Android 没有 /etc/resolv.conf, Go 的纯 Go DNS 解析器找不到 DNS 服务器就会退回本机 53 端口, 于是每个请求都失败在 lookup xxx on [::1]:53: connection refused。 官方解法是开 cgo 用 Bionic 的 getaddrinfo,但那需要 NDK; 这里改由 Java 外壳从 ConnectivityManager 查出系统 DNS,用 -dns 传给 Go, Go 再据此替换 net.DefaultResolver(见 internal/netdns)。 查不到系统 DNS 时会退回内置公共 DNS,也可以在 config.yamldns_servers 手填。

从源码构建

go run ./cmd/endlog                     # 直接运行

bash scripts/build-windows.sh 1.0.0  # → dist/windows/endlog.exe
bash scripts/build-android.sh 1.0.0  # → dist/android/endlog.apk

两个脚本都会先跑 gofmt / go vet / go test,任一不过就中止。

Android 构建不走 Gradle,直接调 aapt2 / javac / d8 / zipalign / apksigner。 原因与两个踩过的坑记在 scripts/build-android.sh 的注释里: build-tools 29 的 d8.bat / apksigner.bat 用了 JDK 9 就移除的 -Djava.ext.dirs, 必须绕过 .bat 直接调 jar;而那一版 d8 处理非静态内部类会 NPE, 所以 MainActivity.java 里的嵌套类必须全部是 static

首次构建会在 build/ 下生成签名密钥。这个 keystore 要自己备份 —— 换密钥签名的 APK 无法覆盖安装,只能卸载重装(那会丢掉手机上的存档)。

登录方式

手机号 + 短信验证码,仅此一种。全程不接触也不保存你的密码。

只有国服支持。国际服官方用邮箱密码 + 图形验证码,本程序未实现, 因此 region: global 目前无法登录(抽卡接口本身是按区域适配好的,缺的只是登录)。

登录要连续走四个接口才能换到抽卡凭据,因此会比一般应用慢几秒。

接口说明

以下端点经两个开源实现交叉核对(bhaoo/endfield-gacha 的 TypeScript 实现与 BoxCatTeam/endfield-cat 的 Rust 实现),两者在域名、appCode 与字段名上完全一致。参数细节另参考 AixLnyt/skport-api-docs

国服域名根为 hypergryph.com,国际服为 gryphline.com,结构相同。

认证链

步骤 端点 请求 得到
1 POST as.<host>/user/auth/v2/token_by_phone_code {phone, code} 账号 token
2 POST as.<host>/user/oauth2/v2/grant {type:1, appCode, token} OAuth token
3 GET binding-api-account-prod.<host>/account/binding/v1/binding_list ?token=&appCode=endfield 角色 uid
4 POST binding-api-account-prod.<host>/account/binding/v1/u8_token_by_uid {uid, token} u8_token

appCode:国服 be36d44aa36bfb5b,国际服 3dacefa138426cfe

抽卡记录

凭 u8_token 访问 ef-webview.<host>不需要 cred/salt 签名流程

GET /api/record/char         ?lang&token&server_id&pool_type[&seq_id]
GET /api/record/weapon/pool  ?lang&token&server_id
GET /api/record/weapon       ?lang&token&server_id&pool_id[&seq_id]

返回统一为 {"code":0,"msg":"","data":{"list":[...],"hasMore":bool}}, 但武器池列表接口的 data 直接是数组

翻页游标 seq_id 取上一页最后一条记录的 seqId

角色卡池必须按 pool_type 逐个查询,共四种:

pool_type 卡池
E_CharacterGachaPoolType_Special 特许寻访
E_CharacterGachaPoolType_Joint 辉光庆典
E_CharacterGachaPoolType_Standard 基础寻访
E_CharacterGachaPoolType_Beginner 启程寻访

单条记录字段:seqIdgachaTsrarityisNewisFreepoolIdpoolName, 名称字段角色为 charName/charId、武器为 weaponName/weaponIdgachaTsrarity 在不同端点上会以数字或数字字符串两种形式出现,程序两种都接受。

server_id:国服 1,国际服 2

上述端点已对国服实测确认可达:请求 GET ef-webview.hypergryph.com/api/record/char?lang=zh-cn&token=…&server_id=1&pool_type=E_CharacterGachaPoolType_Special 返回 HTTP 200 与 {"code":…,"msg":…,"data":…} 外壳,URL 拼装、参数名与响应结构均与实现一致。

目录结构

cmd/endlog/                 入口
internal/config/         配置加载 + 区域定义(域名、appCode、server_id)
internal/gacha/          Record 类型、去重合并、Provider 接口
  ├── endfield/          官方接口实现(角色 4 池 + 武器池,seq_id 游标翻页)
  ├── mockprov/          样例数据源(确定性生成,可反复同步而不产生重复)
  └── remote/            配置驱动的应急数据源 + JSON 路径取值
internal/hypergryph/     账号体系:短信登录与 u8_token 认证链
internal/store/          内存记录集 + JSON 快照(原子写入)
internal/server/         HTTP 路由、会话、接口处理
web/                     前端单页,go:embed 内嵌

设计要点

  • 增量水位按卡池分别记gacha.Watermarks)。这是最容易踩错的地方: 历史卡池早已结束,其记录整体都早于「全局最新时间」。若给所有卡池共用一个全局水位, 每个历史卡池都会在第一条记录上判定「已追上」而停止翻页, 结果是历史卡池的数据永远补不回来。水位键为 类型:查询单元 (角色用 pool_type,武器用 pool_id),由 store.Watermarks() 从已存记录算出。
  • 完整重新同步POST /api/sync{"full":true} 会忽略全部水位, 把所有卡池从头拉一遍,用于补回因同步中断而缺失的数据。页面上对应「完整重新同步」按钮。
  • 时间戳单位自适应:官方 gachaTs 实测是毫秒,而文档与部分开源实现按秒。 写死任一单位都会算出公元五万年,而 time.Time 只能序列化 [0,9999] 年 —— 一条越界记录就能让整份快照连带 HTTP 响应一起失败。 故用 gacha.NormalizeUnixTime 按数值量级判断,并在入库前用 gacha.PlausibleTime 校验。
  • 去重:用 seqId 加类型前缀(char:123 / weapon:456)作为主键, 避免角色与武器序号空间撞号;接口没给 seqId 时按 时间+卡池+名称 生成稳定键。
  • 部分失败不全废:一次完整同步要访问 4 个角色池加若干武器池。单个池失败时, 已拿到的记录照常入库,失败信息作为警告显示,不会让整次同步白跑。
  • 限速:翻页之间按 endfield.delay_ms(默认 700ms)间隔,并有 max_pages 上限。
  • 数据源可换:上层只依赖 gacha.Provider 接口。官方接口若有变动, 可切到 provider: remote,用 config.yaml 里的声明式配置描述新接口,无需改代码重新编译。

配置

config.yaml 每项都有注释。常改的几处:

region: "cn"            # cn 国服 / global 国际服
provider: "endfield"    # endfield 官方接口 / mock 样例数据 / remote 自定义

endfield:
  include_weapon: true  # 关掉可少翻很多页,同步更快
  binding_index: 0      # 一个账号绑了多个角色时选第几个
  delay_ms: 700         # 翻页间隔,别调太小

不想登录先看效果的话,把 provider 改成 mock,任意手机号与验证码都能进, 会载入 320 条样例记录(含角色与武器),用于验证筛选、排序、持久化等功能。

排错

程序出错时会把接口的原始返回一起报出来。常见情况:

  • 访问凭据已失效(40100: Token is invalid) —— 退出登录后重新用短信验证码登录。 凭据有有效期,长时间不用会失效。
  • 收不到短信验证码 —— 核对 auth.send_code_type。这是整套流程里唯一没有交叉验证过的 参数,抓「获取验证码」那条请求看 body 里 type 的数字,改到配置里。
  • 该账号下没有查到终末地角色 —— 确认游戏里已创建角色,且登录的是同一个鹰角账号。
  • 部分卡池未能拉取 —— 通常是限流。等一会儿再点同步,已同步的部分不会重复拉。
  • 某些武器卡池一条记录都没有 —— 点「完整重新同步」。历史卡池的数据若因早先的 同步中断而缺失,普通增量同步不会主动回头补,完整重新同步会把所有卡池从头拉一遍。 同步结果下方会按卡池列出条数,可据此确认哪些池已经补齐。

关于账号安全

  • 只用短信验证码,程序不接触也不保存密码。
  • u8_token 只存在服务端进程内存里,不写磁盘、不下发给浏览器。 浏览器只持有一个本地随机 session cookie。进程退出即失效。
  • 磁盘上只存抽卡记录快照,文件名用 区域-uid,不含手机号。
  • 服务默认只监听 127.0.0.1,不要改成 0.0.0.0
  • 这个工具只用于查询你自己账号的记录。请保持 delay_ms 不要调得太小。

测试

go test ./...

覆盖:官方接口的字段映射与游标翻页(含数字/字符串两种时间戳形式)、部分失败降级、 增量同步的按卡池水位(含历史卡池不被新卡池挡住的回归测试)、去重合并的幂等性、 快照持久化与重启恢复、以及基于样例数据源的完整 HTTP 流程。

官方接口相关的测试通过改写出站 host 指向本地测试服务器, 既验证真实的 URL 拼装,又不会真的访问官方接口。

许可证

GPL-3.0

Copyright (C) 2026 Gordenghost

本程序是自由软件:你可以依据自由软件基金会发布的 GNU 通用公共许可证
(第 3 版或任何更新版本)的条款重新发布和/或修改它。

发布本程序的目的是希望它有用,但不附带任何担保;甚至不包含对
适销性或特定用途适用性的默示担保。详见 GNU 通用公共许可证。

免责声明

  • 本项目是第三方工具,与上海鹰角网络科技有限公司无任何关联,也未获其授权或认可。
  • 它调用的是游戏内「寻访记录」页面所用的同一套接口,这些接口非官方公开文档, 端点与字段随时可能变动或失效。
  • 只用于查询你自己账号的抽卡记录。请勿用于批量抓取他人数据, 并保持 endfield.delay_ms 不要调得过小。
  • 使用本工具的风险由你自行承担。

About

终末寻访录 — 《明日方舟:终末地》抽卡记录查询工具,按卡池算出每个 6 星实际花了多少抽。纯 Go,单文件可执行,Windows 与 Android 双端。

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages