Compare commits

...

144 Commits

Author SHA1 Message Date
BTMuli
dc03edd30b 🚀 v0.8.8 2025-12-03 20:30:48 +08:00
BTMuli
d18b6bb898 ♻️ 代码重构提取 2025-12-03 20:30:28 +08:00
BTMuli
0f107abde6 🐛 重构管理员权限重启逻辑 2025-12-03 19:58:03 +08:00
BTMuli
46cf40734f 🐛 修复成就数据读取异常 2025-12-03 18:53:35 +08:00
BTMuli
676ef8e8ee 🚀 v0.8.7 2025-12-03 10:24:27 +08:00
BTMuli
5d8ea639b8 🍱 更新成就&卡池数据 2025-12-03 10:22:07 +08:00
BTMuli
bc64e20ebd 🚸 优化字典处理逻辑,确保字典至少包含两个键 2025-12-02 23:49:05 +08:00
BTMuli
98efd557d6 🚸 完善奇偶处理 2025-12-02 23:04:10 +08:00
BTMuli
b0b3120b7b 🚸 完善名片解析处理 2025-12-02 22:55:07 +08:00
BTMuli
c2ea3cf026 🐛 修复左侧列表顺序异常 2025-12-02 22:51:20 +08:00
BTMuli
90d71be17e 🐛 修复mac编译异常 2025-12-02 22:39:04 +08:00
BTMuli
9435622a6d 🍱 更新部分新版本资源 2025-12-02 22:36:31 +08:00
BTMuli
5ac9c24379 🐛 修复macOS编译异常 2025-12-02 22:25:44 +08:00
BTMuli
9e359b9621 ⬆️ 更新依赖 2025-12-02 21:59:24 +08:00
BTMuli
cde9149bbd 🐛 修复mac编译异常 2025-12-02 21:49:59 +08:00
BTMuli
b38c3f9fbe 🐛 修复写入文件异常 2025-12-02 21:29:14 +08:00
BTMuli
92ad548061 🐛 修复 Windows 平台相关的条件编译和文档注释 2025-12-02 21:16:59 +08:00
BTMuli
37cea99bbd 侧边栏添加启动游戏入口 2025-12-02 11:58:13 +08:00
BTMuli
b267599039 🐛 修复重启异常 2025-12-02 11:37:37 +08:00
BTMuli
1d204c8284 🚸 调整回复按钮展示判断 2025-12-02 11:11:54 +08:00
BTMuli
51ce0217f0 💩 release模式下重启不一定成功 2025-12-02 02:03:25 +08:00
BTMuli
fac394be8b 🚨 修复内存分配和句柄关闭错误 2025-12-02 00:37:00 +08:00
BTMuli
a12a12e786 🚨 修复部分异常 2025-12-02 00:37:00 +08:00
BTMuli
2d1890645d 🚨 修复编译器异常,移除多余依赖 2025-12-02 00:37:00 +08:00
BTMuli
38f3301664 完成成就导入 2025-12-02 00:37:00 +08:00
BTMuli
14c47369e7 🌱 尝试检测管理员&以管理员模式重启 2025-12-02 00:37:00 +08:00
BTMuli
93be279cbb 👷 完善构建 2025-12-02 00:37:00 +08:00
BTMuli
aca47f822b 实现成就数据读取 2025-12-02 00:37:00 +08:00
BTMuli
ccb4730c82 🐛 修复pipe broken 2025-12-02 00:37:00 +08:00
BTMuli
670e9deba3 ⬆️ 更新依赖 2025-12-02 00:37:00 +08:00
BTMuli
93cca5f715 🌱 初步建立pipe&成功调用dll 2025-12-02 00:37:00 +08:00
BTMuli
d787b8dc8b ️ 添加条件判断以控制调试和发布构建 2025-12-01 13:00:10 +08:00
BTMuli
6b90dde0ab ♻️ 调整命名 2025-12-01 00:55:51 +08:00
BTMuli
323b951c10 🎨 微调 2025-12-01 00:43:31 +08:00
BTMuli
4c3648481e 🐛 修复自定义表情格式解析异常,增加文本清晰度 2025-12-01 00:33:56 +08:00
BTMuli
afcba5ec1a 💄 调整帖子回复浮窗UI,完善类型 2025-11-30 16:48:02 +08:00
BTMuli
725d62b755 👽️ 完善前瞻识别规则,增加空列表处理 2025-11-29 20:04:31 +08:00
BTMuli
e72d6b1b9f 💄 补充遗漏文本,调整交互逻辑 2025-11-27 17:17:37 +08:00
BTMuli
482c7fb1c9 🚸 移除确认弹窗
close #170
2025-11-27 16:12:01 +08:00
BTMuli
d84d68607b 简化账号切换,逻辑移至侧边栏
close#170
2025-11-27 16:04:16 +08:00
BTMuli
758f0d519f 🌱 登录移至侧栏
#170
2025-11-27 14:41:37 +08:00
BTMuli
72b7dc5405 优化组件响应式处理 2025-11-27 11:38:42 +08:00
BTMuli
50a528d25b 🚸 优化滚动处理,移除不必要的async/await 2025-11-25 21:18:09 +08:00
Copilot
4d937b365b 🚸 优化回复浮窗处理 (#169)
* Initial plan

* Fix secondary reply scroll position issue by adding scroll-strategy="close" to submenu

Co-authored-by: BTMuli <72692909+BTMuli@users.noreply.github.com>

* Add auto-load on scroll for reply and sub-reply lists

Co-authored-by: BTMuli <72692909+BTMuli@users.noreply.github.com>

* Fix sub-reply scroll issues with custom event-based solution

Co-authored-by: BTMuli <72692909+BTMuli@users.noreply.github.com>

* Fix sub-reply initialization to use embedded sub_replies data

Co-authored-by: BTMuli <72692909+BTMuli@users.noreply.github.com>

* 🎨 codeStyle

* Fix duplicate sub-reply data by filtering existing reply IDs

Co-authored-by: BTMuli <72692909+BTMuli@users.noreply.github.com>

* Use persistent Set for existingIds to improve duplicate filtering efficiency

Co-authored-by: BTMuli <72692909+BTMuli@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: BTMuli <72692909+BTMuli@users.noreply.github.com>
Co-authored-by: BTMuli <bt-muli@outlook.com>
2025-11-25 18:26:59 +08:00
BTMuli
1124927c0e 💄 修复UI错位 2025-11-25 16:45:14 +08:00
BTMuli
b3c42428e9 🚸 添加BETA提示 2025-11-25 16:44:57 +08:00
BTMuli
db03f211d4 窄视图 2025-11-25 15:13:21 +08:00
BTMuli
5df5868549 🙈 忽略测试文件 2025-11-23 17:31:38 +08:00
BTMuli
b5e4b013c9 🚸 调整hint 2025-11-22 17:35:08 +08:00
BTMuli
6af43bf957 🧑‍💻 调整构建 2025-11-22 13:48:49 +08:00
BTMuli
256b529b16 🧑‍💻 选择构建,尝试减少层数 2025-11-22 13:35:37 +08:00
BTMuli
c521f3cc26 🚸 优化图表下载交互 2025-11-22 13:00:19 +08:00
BTMuli
dcc0d7d052 💄 增加浅色模式下的可见度 2025-11-22 01:15:25 +08:00
BTMuli
3a542ead17 🐛 修复主题切换异常 2025-11-22 01:08:09 +08:00
BTMuli
dce90b64a6 💄 增加浅色模式下的可见度 2025-11-22 01:07:49 +08:00
BTMuli
2fdb2e7b51 🏷️ 修正类型 2025-11-22 00:14:53 +08:00
Copilot
586b506fca ♻️ 重构祈愿图表 close#166
* Initial plan

* Refactor wish calendar - split charts into separate components with scrollbar support

Co-authored-by: BTMuli <72692909+BTMuli@users.noreply.github.com>

* Address code review feedback - improve error handling and comments

Co-authored-by: BTMuli <72692909+BTMuli@users.noreply.github.com>

* Move chart logic to components and remove v-if guards

Co-authored-by: BTMuli <72692909+BTMuli@users.noreply.github.com>

* Fix UID switching reactivity, add scrollbar spacing, and fix download functionality

Co-authored-by: BTMuli <72692909+BTMuli@users.noreply.github.com>

* Use proper ECharts ComposeOption type declarations

Co-authored-by: BTMuli <72692909+BTMuli@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: BTMuli <72692909+BTMuli@users.noreply.github.com>
2025-11-22 00:11:37 +08:00
BTMuli
45ff02b998 🔧 使用https协议 2025-11-21 23:53:09 +08:00
BTMuli
955a1a3c54 🔥 移除无用调整 T_T 2025-11-21 19:07:46 +08:00
BTMuli
5b5589d213 🐛 修复数据刷新异常
close #163
2025-11-21 19:03:57 +08:00
BTMuli
759804a99a 🚸 导入后刷新页面 2025-11-21 19:03:28 +08:00
BTMuli
310c1c91cf 🧪 尝试替换 2025-11-21 18:22:27 +08:00
BTMuli
954fb1a1e8 🧪 完善类型 2025-11-21 18:15:20 +08:00
BTMuli
17f5a87e31 🧪 尝试允许geetest域名 2025-11-21 17:40:57 +08:00
BTMuli
7a9ef78376 🔨 macos-latest 2025-11-21 17:31:22 +08:00
BTMuli
aa5ef06ffd 🧪 尝试调整极验sdk引用 2025-11-21 17:19:23 +08:00
BTMuli
260e9ce4dd 🐛 修复无法手动关闭验证弹窗 2025-11-21 17:17:49 +08:00
BTMuli
e45ebff0fc 💄 增加浅色模式下的可见度 2025-11-21 17:15:22 +08:00
BTMuli
04c1bd0446 🧑‍💻 微调构建 2025-11-21 16:50:21 +08:00
BTMuli
239196149e 🧪 使用v4 2025-11-21 16:39:52 +08:00
BTMuli
77da679a70 🧪 添加上传 2025-11-21 16:33:19 +08:00
BTMuli
50383c2365 🧪 尝试macos-latest构建arm 2025-11-21 16:24:22 +08:00
BTMuli
a099e4e413 👷 新建debug构建流程 2025-11-21 16:11:57 +08:00
BTMuli
df8af9eecd 👽️ 更新Q群链接 2025-11-21 15:53:42 +08:00
BTMuli
0ba4690085 🚸 调整UI显示 2025-11-21 14:59:43 +08:00
BTMuli
98c911469a Revert "🐛 修复窗口适配异常"
This reverts commit 999ddc708c.
2025-11-20 18:18:48 +08:00
BTMuli
999ddc708c 🐛 修复窗口适配异常 2025-11-20 18:03:40 +08:00
BTMuli
5298ecdd0a 👽️ 支持Gt4验证
close #162
2025-11-20 00:30:12 +08:00
BTMuli
dc18cd75a7 搜索新增“最新”“最热”排序 2025-11-19 22:06:15 +08:00
BTMuli
1f4248bde8 ✏️ 修正文本错误 2025-11-19 16:32:02 +08:00
BTMuli
ae7b4acb88 🚸 执行脚本时不允许切换账号 2025-11-19 14:33:20 +08:00
BTMuli
2ab31d8f5c 🚀 v0.8.6 2025-11-19 14:09:48 +08:00
BTMuli
1af990512d 📝 更新README 2025-11-19 13:55:53 +08:00
BTMuli
d96d451156 👽️ 调整读取格式 2025-11-19 13:50:47 +08:00
BTMuli
f029306ebb 🚸 添加跳转视频链接 2025-11-19 13:40:25 +08:00
BTMuli
d3c5baa0c2 📝 更新资源说明文档 2025-11-19 13:22:01 +08:00
BTMuli
ba0802752c 🔊 完善log 2025-11-19 00:51:49 +08:00
BTMuli
ff94e12ff5 🚸 调整默认文本 2025-11-19 00:07:13 +08:00
BTMuli
0fbf1f7c2a 🚸 添加AIGC相关注释 2025-11-18 23:01:51 +08:00
BTMuli
68809a93c6 支撑导入剧诗数据 2025-11-18 22:51:32 +08:00
BTMuli
0edcadef63 👽️ 移除剧诗概览,支撑导入剧诗数据 2025-11-18 22:42:46 +08:00
BTMuli
9f9c30914f 🔥 移除胡桃深渊统计页面 2025-11-18 22:29:24 +08:00
BTMuli
04cf372798 🎨 路由重定向 2025-11-18 22:27:27 +08:00
BTMuli
6617a26c90 👽️ 移除深渊上传,支撑导入胡桃深渊数据 2025-11-18 22:20:58 +08:00
BTMuli
d244423800 🚸 调整导入浮窗ui,显示导入进度 2025-11-18 22:02:07 +08:00
BTMuli
3366efaadd 🐛 处理拓展解析异常 2025-11-15 20:36:50 +08:00
BTMuli
d74e7a7a31 🥅 处理异常,清除缓存后重启 2025-11-15 14:54:06 +08:00
BTMuli
2d0b409813 🐛 修复图片渲染异常 2025-11-15 14:37:05 +08:00
BTMuli
942068faea 🚀 v0.8.5 2025-11-10 16:34:14 +08:00
BTMuli
0f0f7684d2 🍱 更新下半数据 2025-11-10 16:31:01 +08:00
BTMuli
531cb32f72 🚀 v0.8.4 2025-10-27 19:48:35 +08:00
BTMuli
a368223805 千星奇域页面 2025-10-27 19:42:02 +08:00
BTMuli
6eab6c81f1 🍱 增加千星奇域元数据 2025-10-27 17:26:33 +08:00
BTMuli
68594a2a76 🐛 剔除多余换行 2025-10-27 12:33:09 +08:00
BTMuli
5d5f22d76e 🚸 添加prefix 2025-10-25 23:05:13 +08:00
BTMuli
65e948c34c 添加getRegionRoleInfo事件处理 2025-10-25 21:04:04 +08:00
BTMuli
68dead3d84 🔥 考虑合并祈愿,不单独分页 2025-10-25 19:46:03 +08:00
BTMuli
babc6a9a75 嵌入祈愿详情 2025-10-25 19:45:17 +08:00
BTMuli
6db4ff5ac9 👽️ 移除非必需参数 2025-10-25 18:59:26 +08:00
BTMuli
ce1b6f365e 🏷️ 调整类型注释 2025-10-25 14:07:46 +08:00
BTMuli
b6ed9668ac 🚸 完善类型,添加交互 2025-10-25 13:00:10 +08:00
BTMuli
2a2a190f5f 🐛 修复部分帖子渲染异常 2025-10-25 12:31:03 +08:00
BTMuli
5d03a32362 💄 调整名片样式 2025-10-24 23:35:12 +08:00
BTMuli
33d9ba5c4d 重构帖子解析逻辑,增加新类型解析
*PostID:69886846,69915487
2025-10-24 22:21:03 +08:00
BTMuli
9020214d23 🐛 修复部分帖子解析异常 2025-10-24 20:13:51 +08:00
BTMuli
78c3f79bfd 🧑‍💻 JSON内容复制 2025-10-24 19:58:31 +08:00
BTMuli
ee0fc6dbae 完善投稿活动类型声明,渲染投稿活动&交互
*PostID:69823686
2025-10-24 19:27:49 +08:00
BTMuli
8c51b79558 🐛 修复浮窗显示异常 2025-10-24 18:05:50 +08:00
BTMuli
8c1899637f 🌱 暂时将千星奇域移到祈愿子tab 2025-10-24 16:21:30 +08:00
BTMuli
56df920a7d 嵌入官方公告页面(已登录) 2025-10-24 16:15:05 +08:00
BTMuli
64c6f4ab8f 🚸 兑换码浮窗显示游戏名称 2025-10-24 12:20:15 +08:00
BTMuli
d3902d6e31 🌱 千星奇域抽卡记录页面 2025-10-23 23:56:23 +08:00
BTMuli
01e355b0d6 千星奇域抽卡记录获取 2025-10-23 23:09:58 +08:00
BTMuli
c40b3c6ff0 👽️ 公告添加千星奇域分类 2025-10-23 22:40:30 +08:00
BTMuli
4305967ba9 🚀 v0.8.3 2025-10-22 13:59:10 +08:00
BTMuli
78f454bee5 🍱 更新卡池数据 2025-10-22 13:50:08 +08:00
BTMuli
e9a38e1474 👽️ 奇偶不lock好感卡片 2025-10-22 13:13:22 +08:00
BTMuli
9fb2aa6112 🍱 更新6.1资源 2025-10-22 12:34:43 +08:00
BTMuli
a0554e4355 🚸 首页活动组件(用户)分享图生成 2025-10-17 18:13:34 +08:00
BTMuli
f890165894 💄 微调月谕模式ui 2025-10-17 18:06:38 +08:00
BTMuli
bc22612da7 💄 调整布局 2025-10-15 11:09:42 +08:00
BTMuli
a9ec93b18d 🐛 修复JS脚本执行异常 2025-10-15 00:14:01 +08:00
BTMuli
651cbef0a0 ♻️ 提取剧诗Icon 2025-10-08 12:05:51 +08:00
BTMuli
41a144fec2 🚸 优化图片调整浮窗样式 2025-10-08 11:49:47 +08:00
BTMuli
3f219ebb82 ♻️ 重构gt返回逻辑 2025-10-08 10:36:17 +08:00
BTMuli
43c85afd1e 📝 移除oss认证 2025-10-08 10:08:54 +08:00
BTMuli
48771f57a0 🚸 降低验证触发概率 2025-10-04 12:09:17 +08:00
BTMuli
6e3884df58 💄 添加圣牌图标 2025-10-02 12:26:52 +08:00
BTMuli
7a6a06bb25 🐛 修正链接判断逻辑 2025-10-02 12:18:25 +08:00
BTMuli
eac3691d0b 🐛 修复切换角色导致ck对应异常 2025-10-01 11:10:12 +08:00
BTMuli
3ece987c80 👽️ 适配月谕圣牌模式 2025-10-01 10:37:46 +08:00
BTMuli
145438373b 🐛 修复下载链接异常 2025-09-28 23:23:40 +08:00
BTMuli
b62b0b4902 🐛 重构数据解析,修复HEIC格式图片渲染异常 2025-09-28 23:13:55 +08:00
BTMuli
ed878dea9e 💄 微调样式 2025-09-27 21:40:23 +08:00
261 changed files with 15945 additions and 5118 deletions

View File

@@ -1,42 +0,0 @@
name: 原神游戏资源更新(仅供开发者使用)
description: 版本前瞻后的例行资源更新
title: "[Update] "
labels:
- 资源
body:
- type: checkboxes
attributes:
label: Issue Check
options:
- label: 个人明确了解该模板仅供开发者使用
required: true
- type: input
id: version
attributes:
label: 游戏版本
description: 请填写游戏版本
placeholder: 如 4.6
- type: checkboxes
id: resources
attributes:
label: 包括的资源
options:
- label: 角色&名片,有新角色时选择
required: false
- label: 武器,有新武器时选择
required: false
- label: 成就,有新成就时选择
required: false
- label: 材料,有新材料时选择
required: false
- type: textarea
id: detail
attributes:
label: 详情
description: 对上述内容进行详细说明
- type: textarea
id: additional
attributes:
label: 其他信息
description: 请填写其他信息
placeholder: 请填写其他信息

View File

@@ -23,7 +23,7 @@ jobs:
- platform: macos-latest
args: "--target x86_64-apple-darwin"
target: "macos-intel"
- platform: macos-15-intel
- platform: macos-latest
args: "--target aarch64-apple-darwin"
target: "macos-arm"
runs-on: ${{ matrix.settings.platform }}
@@ -58,7 +58,8 @@ jobs:
run: rustup target add aarch64-apple-darwin
- name: Output toolchain
run: rustup show
- name: Add Offset Conf
run: echo '${{ secrets.YAE_CONF }}' | jq -c . > ./src-tauri/lib/conf.json
- name: setup node
uses: actions/setup-node@v3
with:
@@ -66,7 +67,7 @@ jobs:
- name: setup pnpm
uses: pnpm/action-setup@v2
with:
version: 10.16.1
version: 10.23.0
- name: Install frontend dependencies
run: pnpm install
@@ -81,7 +82,8 @@ jobs:
releaseBody: |
> [!TIP]
> Windows 平台用户建议通过微软应用商店下载macOS 平台仅在此发布Linux 平台暂不支持。
> 如有使用问题可加入 [反馈QQ群](https://h5.qun.qq.com/s/3cgX0hJ4GA)
> 如有使用问题可加入 [反馈QQ群](https://qm.qq.com/q/hUxIfSWluo)
> MacOS 用户参考 [安装指南](https://github.com/BTMuli/TeyvatGuide/blob/master/docs/macos-gatekeeper/README.md)
<a href="https://apps.microsoft.com/store/detail/9NLBNNNBNSJN?launch=true&cid=BTMuli&mode=mini">
<img src="https://get.microsoft.com/images/zh-cn%20dark.svg" alt="download"/>

132
.github/workflows/debug.yml vendored Normal file
View File

@@ -0,0 +1,132 @@
name: Build Debug for Mac
on:
workflow_dispatch:
inputs:
build-debug:
description: "Build debug version"
required: true
default: true
type: boolean
build-release:
description: "Build release version"
required: true
default: false
type: boolean
jobs:
build-debug-mac:
permissions:
contents: write
strategy:
fail-fast: false
matrix:
settings:
- platform: macos-latest
args: "--target x86_64-apple-darwin"
target: "macos-intel"
artifact: "debug-build-macos-intel"
- platform: macos-latest
args: "--target aarch64-apple-darwin"
target: "macos-arm"
artifact: "debug-build-macos-arm"
runs-on: ${{ matrix.settings.platform }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup SSH
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" | tr -d '\r' > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
- name: Add Github RSA
run: |
echo "${{ secrets.KNOWN_GITHUB_RSA }}" >> ~/.ssh/known_hosts
chmod 644 ~/.ssh/known_hosts
- name: Test SSH connection
run: ssh -T git@github.com || true
- name: Rust setup
uses: dtolnay/rust-toolchain@stable
- name: Rust cache
uses: swatinem/rust-cache@v2
with:
workspaces: "./src-tauri -> target"
- name: Add Rust targets(macOS Intel)
if: matrix.settings.target == 'macos-intel'
run: rustup target add x86_64-apple-darwin
- name: Add Rust targets(macOS ARM)
if: matrix.settings.target == 'macos-arm'
run: rustup target add aarch64-apple-darwin
- name: Output toolchain
run: rustup show
- name: Add Offset Conf
run: echo '${{ secrets.YAE_CONF }}' | jq -c . > ./src-tauri/lib/conf.json
- name: setup node
uses: actions/setup-node@v3
with:
node-version: 24.8.0
- name: setup pnpm
uses: pnpm/action-setup@v2
with:
version: 10.23.0
- name: Install frontend dependencies
run: pnpm install
# 获取commit hash后续用这个做文件命名
- name: Get Commit Hash
id: get_commit_hash
run: echo "COMMIT_HASH=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
# Build Debug
- name: Build debug app
if: github.event.inputs.build-debug == 'true'
uses: tauri-apps/tauri-action@dev
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
args: ${{ matrix.settings.args }} --debug
- name: Move Debug Intel
if: matrix.settings.target == 'macos-intel' && github.event.inputs.build-debug == 'true'
run: mv src-tauri/target/x86_64-apple-darwin/debug/bundle/dmg/*.dmg TeyvatGuide_${{ env.COMMIT_HASH }}_intel-debug.dmg
- name: Move Debug ARM
if: matrix.settings.target == 'macos-arm' && github.event.inputs.build-debug == 'true'
run: mv src-tauri/target/aarch64-apple-darwin/debug/bundle/dmg/*.dmg TeyvatGuide_${{ env.COMMIT_HASH }}_arm-debug.dmg
- name: Upload Debug Intel
if: matrix.settings.target == 'macos-intel' && github.event.inputs.build-debug == 'true'
uses: actions/upload-artifact@v4
with:
name: debug-macos-intel
path: TeyvatGuide_${{ env.COMMIT_HASH }}_intel-debug.dmg
- name: Upload Debug ARM
if: matrix.settings.target == 'macos-arm' && github.event.inputs.build-debug == 'true'
uses: actions/upload-artifact@v4
with:
name: debug-macos-arm
path: TeyvatGuide_${{ env.COMMIT_HASH }}_arm-debug.dmg
# Build Release
- name: Build app
if: github.event.inputs.build-release == 'true'
uses: tauri-apps/tauri-action@dev
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
args: ${{ matrix.settings.args }}
- name: Move Release Intel
if: matrix.settings.target == 'macos-intel' && github.event.inputs.build-release == 'true'
run: mv src-tauri/target/x86_64-apple-darwin/release/bundle/dmg/*.dmg TeyvatGuide_${{ env.COMMIT_HASH }}_intel-release.dmg
- name: Move Release ARM
if: matrix.settings.target == 'macos-arm' && github.event.inputs.build-release == 'true'
run: mv src-tauri/target/aarch64-apple-darwin/release/bundle/dmg/*.dmg TeyvatGuide_${{ env.COMMIT_HASH }}_arm-release.dmg
- name: Upload Release Intel
if: matrix.settings.target == 'macos-intel' && github.event.inputs.build-release
uses: actions/upload-artifact@v4
with:
name: release-macos-intel
path: TeyvatGuide_${{ env.COMMIT_HASH }}_intel-release.dmg
- name: Upload Release ARM
if: matrix.settings.target == 'macos-arm' && github.event.inputs.build-release == 'true'
uses: actions/upload-artifact@v4
with:
name: release-macos-arm
path: TeyvatGuide_${{ env.COMMIT_HASH }}_arm-release.dmg

View File

@@ -1,8 +1,9 @@
name: Qodana
on:
push:
branches:
- master
workflow_dispatch:
# push:
# branches:
# - master
jobs:
qodana:

View File

@@ -2,12 +2,85 @@
Author: 目棃
Description: CHANGELOG
Date: 2025-09-09
Update: 2025-09-27
Update: 2025-12-03
---
> 本文档 [`Frontmatter`](https://github.com/BTMuli/MuCli#Frontmatter) 由 [MuCli](https://github.com/BTMuli/Mucli) 自动生成于 `2025-09-09 14:30:56`
>
> 更新于 `2025-09-27 10:18:07`
> 更新于 `2025-12-03 20:02:17`
## [0.8.8](https://github.com/BTMuli/TeyvatGuide/releases/v0.8.8) (2025-12-03)
- 🐛 修复成就数据读取异常
- 🐛 重构管理员权限重启逻辑
## [0.8.7](https://github.com/BTMuli/TeyvatGuide/releases/v0.8.7) (2025-12-03)
- 🍱 更新 6.2 版本资源
- ✨ 帖子搜索支持“最新”“最热”排序
- ✨ 登录支持 Gt4 验证 [`#162`](https://github.com/BTMuli/TeyvatGuide/issues/162)
- ✨ 帖子视图支持窄视图模式,**未完全适配所有组件,可能存在显示异常**
- ✨ 支持通过内置 Yae 自动获取成就数据 [`#142`](https://github.com/BTMuli/TeyvatGuide/issues/142)
- 🐛 修复无法手动关闭极验验证弹窗
- 🐛 修复数据刷新后渲染异常 [`#163`](https://github.com/BTMuli/TeyvatGuide/issues/163)
- 🐛 重构祈愿图表,修复祈愿日历没有下拉条 [`#165`](https://github.com/BTMuli/TeyvatGuide/issues/165)
- 🐛 修复 MacOS 下极验验证浮窗加载异常 [`#164`](https://github.com/BTMuli/TeyvatGuide/issues/164)
- 🐛 重构回复浮窗处理,调整 UI ,修复滚动异常 [`#168`](https://github.com/BTMuli/TeyvatGuide/issues/168)
- 🐛 修复自定义表情格式解析异常,增加文本清晰度
- 🐛 调整回复按钮展示判断,修复特定条件下的数据对应异常
- 🐛 修复角色 Wiki 左侧列表顺序概率异常
- ✏️ 修正通过 Yae 导入成就的文本错误
- ✏️ 修正清除缓存后的提示文本
- 🚸 执行脚本时不允许切换账号
- 🚸 调整外部导入祈愿记录时进度显示逻辑,导入后刷新页面
- 🚸 增加部分 UI 在浅色模式下的可见度
- 🚸 账号相关操作(添加,切换)移至侧栏 [`#170`](https://github.com/BTMuli/TeyvatGuide/issues/170)
- 🚸 侧栏添加启动入口,满足条件时显示
- 🚸 完善角色 Wiki 侧边栏奇偶点击处理
- 👽️ 完善前瞻识别规则,增加空列表处理
- 📝 更新Q群链接
## [0.8.6](https://github.com/BTMuli/TeyvatGuide/releases/v0.8.6) (2025-11-19)
> 关于胡桃数据库导入功能的说明请参考 [导入胡桃数据库](https://app.btmuli.ink/docs/TeyvatGuide/import-hutao-db.html)
- 👽️ 移除剧诗概览,支持导入胡桃剧诗数据
- 👽️ 移除深渊上传,支持导入胡桃深渊数据
- 🔥 移除胡桃深渊统计页面
- 🚸 调整导入祈愿记录浮窗ui显示导入进度
- 🐛 修复图片渲染异常
- 🥅 处理清除缓存异常,清除缓存后重启
- 🚸 帖子详情添加AIGC相关注释
- 🚸 添加跳转视频链接
- 📝 更新相关文档
## [0.8.5](https://github.com/BTMuli/TeyvatGuide/releases/v0.8.5) (2025-11-10)
- 🍱 更新下半数据
## [0.8.4](https://github.com/BTMuli/TeyvatGuide/releases/v0.8.4) (2025-10-27)
- 👽️ 公告添加千星奇域分类
- 🚸 兑换码浮窗显示游戏名称
- ✨ 嵌入官方公告页面(已登录)
- ✨ 嵌入官方祈愿详情(已登录)
- ✨ 完善投稿活动类型声明,渲染投稿活动&交互
- 🐛 修复部分帖子解析异常
- ✨ 重构帖子解析逻辑,增加新类型解析
- 💄 调整名片样式
- ✨ 添加getRegionRoleInfo事件处理
- 🐛 公告解析剔除多余换行
- ✨ 千星奇域祈愿页面草创
## [0.8.3](https://github.com/BTMuli/TeyvatGuide/releases/v0.8.3) (2025-10-22)
- 🍱 更新6.1版本数据
- 👽️ 适配月谕圣牌模式
- 🐛 重构帖子数据解析修复HEIC格式图片渲染异常
- 🐛 修复切换角色导致ck对应异常
- 🚸 优化图片调整浮窗样式
- ♻️ 重构gt返回逻辑
- 💄 调整布局
## [0.8.2](https://github.com/BTMuli/TeyvatGuide/releases/v0.8.2) (2025-09-27)

View File

@@ -2,16 +2,20 @@
Author: 目棃
Description: 说明文档
Date: 2023-03-05
Update: 2025-09-09
Update: 2025-12-03
---
> 本文档 [`Frontmatter`](https://github.com/BTMuli/MuCli#Frontmatter) 由 [MuCli](https://github.com/BTMuli/Mucli) 自动生成于 `2023-03-05 14:41:55`
>
> 更新于 `2025-09-09 14:37:02`
> 更新于 `2025-12-03 10:22:51`
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/BTMuli/TeyvatGuide) ![](https://img.shields.io/github/last-commit/BTMuli/TeyvatGuide) ![](https://img.shields.io/github/commits-since/BTMuli/TeyvatGuide/latest?include_prereleases)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/BTMuli/TeyvatGuide)
![](https://img.shields.io/badge/UIAF-v1.1-orange?style=for-the-badge) ![](https://img.shields.io/badge/UIGF-v3.0-red?style=for-the-badge) ![](https://img.shields.io/badge/UIGF-v4.1-red?style=for-the-badge) ![](https://img.shields.io/github/license/BTMuli/TeyvatGuide?style=for-the-badge)
[![](https://img.shields.io/github/last-commit/BTMuli/TeyvatGuide)](https://github.com/BTMuli/TeyvatGuide/commits) [![](https://img.shields.io/github/commits-since/BTMuli/TeyvatGuide/latest?include_prereleases)](https://github.com/BTMuli/TeyvatGuide/commits)
[![](https://img.shields.io/badge/UIAF-v1.1-orange?style=for-the-badge)](./docs/standards/UIAF.md) [![](https://img.shields.io/badge/UIGF-v3.0-red?style=for-the-badge)](./docs/standards/UIGF3.md) [![](https://img.shields.io/badge/UIGF-v4.1-red?style=for-the-badge)](./docs/standards/UIGF.md)
[![](https://img.shields.io/github/license/BTMuli/TeyvatGuide?style=for-the-badge)](./LICENSE)
<div style="width: 100%; text-align: center; margin: 0 auto;">
<img alt="icon" src="https://s2.loli.net/2023/10/19/Y5DpBQRy3usLHEb.png" />
@@ -49,7 +53,7 @@ Game Tool for Genshin Impact player, supports Windows and macOS.
- [x] 米游社官方帖获取(支持通过 ID 获取)
- [x] 米游社各分区帖子获取(支持通过 ID 获取)
- [x] 米游社话题帖子获取(通过话题点击跳转)
- [x] 成就管理UIAF v1.1),支持 [`Yae`](https://github.com/HolographicHat/Yae) 导入
- [x] 成就管理UIAF v1.1),支持 [`Yae`](https://github.com/HolographicHat/Yae) 导入 & 自动导入内置Yae
- [x] 祈愿管理UIGF v3.0UIGF v4.1
- [x] 留影叙佳期画片查看
- [x] 帖子收藏
@@ -65,13 +69,13 @@ Game Tool for Genshin Impact player, supports Windows and macOS.
- [x] 真境剧诗
- [x] 幽境危战
- [x] 祈愿数据获取(近一年)
- [x] 千星奇域祈愿数据获取(近一年)
- [x] 用户收藏帖子获取
- [x] 用户关注帖子获取
- [x] 一键完成米游币每日任务
- [x] 一键完成游戏签到
- Wiki 功能:
- [x] 深渊数据库Hutao API
- [x] 角色图鉴
- [x] 武器图鉴
- [x] 名片图鉴
@@ -90,7 +94,7 @@ Game Tool for Genshin Impact player, supports Windows and macOS.
## UI 参考 / UI Reference
- [Snap.Hutao](https://github.com/DGP-Studio/Snap.Hutao)
- ~~[Snap.Hutao](https://github.com/DGP-Studio/Snap.Hutao)~~
- [Starward](https://github.com/Scighost/Starward)
- [米游社](https://www.miyoushe.com/ys/)
- [原神](https://yuanshen.com/)
@@ -102,6 +106,7 @@ Game Tool for Genshin Impact player, supports Windows and macOS.
- UIAF[UIAF v1.1](docs/standards/UIAF.md)
- UIGF[UIGF v3.0](docs/standards/UIGF3.md)[UIGF v4.0](docs/standards/UIGF.md)
- [macOS 平台门禁属性导致应用无法打开应用的修复指引](docs/macos-gatekeeper/README.md)
- [如何导入胡桃数据库](https://app.btmuli.ink/docs/TeyvatGuide/import-hutao-db.html)
## 特定项目 / Special Project
@@ -136,7 +141,7 @@ Game Tool for Genshin Impact player, supports Windows and macOS.
本项目在开发过程中参考了诸多相关开源项目,特此鸣谢。
- [UIGF Organization](https://github.com/UIGF-org)
- [Snap.Hutao](https://github.com/DGP-Studio/Snap.Hutao)
- ~~[Snap.Hutao](https://github.com/DGP-Studio/Snap.Hutao)~~
- [StarWard](https://github.com/Scighost/Starward)
- [xunkong](https://github.com/xunkong/xunkong)
- [gs-helper](https://github.com/vikiboss/gs-helper)
@@ -145,9 +150,6 @@ Game Tool for Genshin Impact player, supports Windows and macOS.
- [amos-data](https://github.com/yuehaiteam/amos-data)
- [MihoyoBBSTools](https://github.com/Womsxd/MihoyoBBSTools)
- [nonebot-plugin-mystool](https://github.com/Ljzd-PRO/nonebot-plugin-mystool)
感谢 JetBrains 提供的开源许可证。
![JetBrains logo](https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.png)
- [Yae](https://github.com/HolographicHat/Yae)
[![Star History Chart](https://api.star-history.com/svg?repos=BTMuli/TeyvatGuide&type=Timeline)](https://star-history.com/#BTMuli/TeyvatGuide&Timeline)

View File

@@ -2,12 +2,12 @@
Author: 目棃
Description: 项目资源说明
Date: 2023-03-10
Update: 2025-02-28
Update: 2025-11-19
---
> 本文档 [`Frontmatter`](https://github.com/BTMuli/MuCli#Frontmatter) 由 [MuCli](https://github.com/BTMuli/Mucli) 自动生成于 `2023-03-10 22:05:44`
>
> 更新于 `2025-02-28 09:40:33`
> 更新于 `2025-11-19 12:31:22`
## 说明
@@ -40,8 +40,8 @@ Update: 2025-02-28
相关仓库:
- [TGAssistant](https://github.com/BTMuli/TGAssistant):项目下游仓库,用于处理项目数据。
- [Snap.Metadata](https://github.com/DGP-Studio/Snap.Metadata):胡桃元数据仓库,项目大部分数据来源于此。
- [Snap.Static](https://github.com/DGP-Studio/Snap.Static):胡桃静态资源仓库,项目部分图像资源来源于此。
- ~~[Snap.Metadata](https://github.com/DGP-Studio/Snap.Metadata)~~:胡桃元数据仓库,项目大部分数据来源于此。
- ~~[Snap.Static](https://github.com/DGP-Studio/Snap.Static)~~:胡桃静态资源仓库,项目部分图像资源来源于此。
- [amos-data](https://github.com/yuehaiteam/amos-data):成就数据仓库,成就数据的详细信息来源于此。
## 字体

View File

@@ -1,9 +1,9 @@
{
"name": "teyvatguide",
"version": "0.8.2",
"version": "0.8.8",
"description": "Game Tool for GenshinImpact player",
"private": true,
"packageManager": "pnpm@10.16.1",
"packageManager": "pnpm@10.24.0",
"type": "module",
"scripts": {
"build": "tauri build",
@@ -71,68 +71,66 @@
},
"dependencies": {
"@mdi/font": "7.4.47",
"@tauri-apps/api": "^2.8.0",
"@tauri-apps/plugin-deep-link": "^2.4.3",
"@tauri-apps/plugin-dialog": "^2.4.0",
"@tauri-apps/plugin-fs": "^2.4.2",
"@tauri-apps/plugin-http": "^2.5.2",
"@tauri-apps/plugin-log": "^2.7.0",
"@tauri-apps/plugin-opener": "^2.5.0",
"@tauri-apps/plugin-os": "^2.3.1",
"@tauri-apps/plugin-process": "^2.3.0",
"@tauri-apps/plugin-shell": "^2.3.1",
"@tauri-apps/plugin-sql": "^2.3.0",
"@tauri-apps/api": "^2.9.1",
"@tauri-apps/plugin-deep-link": "^2.4.5",
"@tauri-apps/plugin-dialog": "^2.4.2",
"@tauri-apps/plugin-fs": "^2.4.4",
"@tauri-apps/plugin-http": "github:tauri-apps/tauri-plugin-http",
"@tauri-apps/plugin-log": "^2.7.1",
"@tauri-apps/plugin-opener": "^2.5.2",
"@tauri-apps/plugin-os": "^2.3.2",
"@tauri-apps/plugin-process": "^2.3.1",
"@tauri-apps/plugin-shell": "^2.3.3",
"@tauri-apps/plugin-sql": "^2.3.1",
"ajv": "^8.17.1",
"artplayer": "^5.3.0",
"clipboard": "^2.0.11",
"color-convert": "^3.1.2",
"color-convert": "^3.1.3",
"echarts": "^6.0.0",
"html2canvas": "^1.4.1",
"js-md5": "^0.8.3",
"jsencrypt": "^3.5.4",
"pinia": "^3.0.3",
"pinia-plugin-persistedstate": "^4.5.0",
"pinia": "^3.0.4",
"pinia-plugin-persistedstate": "^4.7.1",
"qrcode.vue": "^3.6.0",
"sass-embedded": "^1.92.1",
"swiper": "^12.0.1",
"sass-embedded": "^1.93.3",
"swiper": "^12.0.3",
"uuid": "^13.0.0",
"vue": "^3.5.21",
"vue-echarts": "^7.0.3",
"vue-json-pretty": "^2.5.0",
"vue-router": "^4.5.1",
"vuetify": "^3.10.0",
"vue": "^3.5.25",
"vue-echarts": "^8.0.1",
"vue-json-pretty": "^2.6.0",
"vue-router": "^4.6.3",
"vuetify": "^3.11.0",
"wcag-color": "^1.1.1",
"xml-js": "^1.6.11"
},
"devDependencies": {
"@btmuli/stylelint-plugin-color": "^0.1.0",
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "^9.35.0",
"@tauri-apps/cli": "2.8.4",
"@eslint/eslintrc": "^3.3.3",
"@eslint/js": "^9.39.1",
"@tauri-apps/cli": "2.9.5",
"@types/color-convert": "^2.0.4",
"@types/fs-extra": "^11.0.4",
"@types/js-md5": "^0.8.0",
"@types/node": "^24.4.0",
"@types/uuid": "^10.0.0",
"@typescript-eslint/parser": "^8.43.0",
"@typescript/native-preview": "7.0.0-dev.20250914.1",
"@vitejs/plugin-vue": "^6.0.1",
"@types/node": "^24.10.1",
"@typescript-eslint/parser": "^8.48.1",
"@typescript/native-preview": "7.0.0-dev.20251202.1",
"@vitejs/plugin-vue": "^6.0.2",
"app-root-path": "^3.1.0",
"concurrently": "^9.2.1",
"eslint": "^9.35.0",
"eslint": "^9.39.1",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-jsonc": "^2.20.1",
"eslint-plugin-jsonc": "^2.21.0",
"eslint-plugin-prettier": "^5.5.4",
"eslint-plugin-vue": "^10.4.0",
"eslint-plugin-yml": "^1.18.0",
"fs-extra": "^11.3.1",
"globals": "^16.4.0",
"eslint-plugin-vue": "^10.6.2",
"eslint-plugin-yml": "^1.19.0",
"fs-extra": "^11.3.2",
"globals": "^16.5.0",
"husky": "^9.1.7",
"jsonc-eslint-parser": "^2.4.0",
"lint-staged": "^16.1.6",
"oxlint": "^1.15.0",
"prettier": "3.6.2",
"stylelint": "^16.24.0",
"jsonc-eslint-parser": "^2.4.1",
"lint-staged": "^16.2.7",
"oxlint": "^1.31.0",
"prettier": "3.7.3",
"stylelint": "^16.26.1",
"stylelint-config-idiomatic-order": "^10.0.0",
"stylelint-config-standard-scss": "^16.0.0",
"stylelint-config-standard-vue": "^1.0.0",
@@ -141,14 +139,14 @@
"stylelint-order": "^7.0.0",
"stylelint-prettier": "^5.0.3",
"stylelint-scss": "^6.12.1",
"tsx": "^4.20.5",
"typescript": "^5.9.2",
"typescript-eslint": "^8.43.0",
"vite": "^7.1.5",
"vite-plugin-vue-devtools": "^8.0.2",
"tsx": "^4.21.0",
"typescript": "^5.9.3",
"typescript-eslint": "^8.48.1",
"vite": "npm:rolldown-vite@^7.2.9",
"vite-plugin-vue-devtools": "^8.0.5",
"vite-plugin-vuetify": "^2.1.2",
"vue-eslint-parser": "^10.2.0",
"vue-tsc": "^3.0.7",
"yaml-eslint-parser": "^1.3.0"
"vue-tsc": "^3.1.5",
"yaml-eslint-parser": "^1.3.1"
}
}

3701
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 836 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@@ -5,3 +5,6 @@
# Generated by Tauri
# will have schema files for capabilities auto-completion
/gen/schemas
# secret
/lib/*.json

1664
src-tauri/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package]
name = "TeyvatGuide"
version = "0.8.2"
version = "0.8.8"
description = "Game Tool for Genshin Impact player"
authors = ["BTMuli <bt-muli@outlook.com>"]
license = "MIT"
@@ -17,18 +17,33 @@ name = "teyvat_guide_lib"
crate-type = ["staticlib", "cdylib", "rlib"]
[build-dependencies]
tauri-build = { version = "2.4.1", features = [] }
tauri-build = { version = "2.5.3", features = [] }
[dependencies]
chrono = "0.4.42"
log = "0.4.28"
serde = { version = "1.0.221", features = ["derive"] }
serde_json = "1.0.144"
tauri = { version = "2.8.5", features = [] }
tauri-utils = "2.7.0"
log = "0.4.29"
prost = "0.14.1"
prost-types = "0.14.1"
serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.145"
tauri = { version = "2.9.4", features = [] }
tauri-utils = "2.8.1"
url = "2.5.7"
walkdir = "2.5.0"
[target.'cfg(windows)'.dependencies.windows-sys]
version = "0.61.2"
features = [
"Win32_System_Diagnostics",
"Win32_System_Diagnostics_Debug",
"Win32_System_Diagnostics_ToolHelp",
"Win32_System_LibraryLoader",
"Win32_System_Memory",
"Win32_System_Pipes",
"Win32_System_Threading",
"Win32_System_WindowsProgramming",
]
# deep link 插件
[dependencies.tauri-plugin-deep-link]
git = "ssh://git@github.com/tauri-apps/plugins-workspace.git"

Binary file not shown.

View File

@@ -8,7 +8,7 @@ mod utils;
use tauri::{AppHandle, Manager, WebviewWindowBuilder};
use tauri_utils::config::WebviewUrl;
static BBS_VERSION: &'static str = "2.93.0";
static BBS_VERSION: &'static str = "2.95.1";
#[tauri::command]
pub async fn create_mhy_client(handle: AppHandle, func: String, url: String) {

View File

@@ -1,6 +1,5 @@
//! @file src/commands.rs
//! @desc 命令模块,负责处理命令
//! @since Beta v0.7.4
//! 命令模块,负责处理命令
//! @since Beta v0.8.8
use tauri::{AppHandle, Emitter, Manager, WebviewWindowBuilder};
use tauri_utils::config::{WebviewUrl, WindowConfig};
@@ -70,3 +69,61 @@ pub async fn get_dir_size(path: String) -> u64 {
}
size
}
// 判断是否是管理员权限
#[tauri::command]
pub fn is_in_admin() -> bool {
#[cfg(not(target_os = "windows"))]
{
return false;
}
#[cfg(target_os = "windows")]
{
use windows_sys::Win32::Foundation::{CloseHandle, HANDLE};
use windows_sys::Win32::Security::{
AllocateAndInitializeSid, CheckTokenMembership, FreeSid, SID_IDENTIFIER_AUTHORITY,
TOKEN_QUERY,
};
use windows_sys::Win32::System::SystemServices::{
DOMAIN_ALIAS_RID_ADMINS, SECURITY_BUILTIN_DOMAIN_RID,
};
use windows_sys::Win32::System::Threading::{GetCurrentProcess, OpenProcessToken};
unsafe {
let mut token_handle: HANDLE = std::ptr::null_mut();
if OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut token_handle) == 0 {
return false;
}
let nt_authority = SID_IDENTIFIER_AUTHORITY { Value: [0, 0, 0, 0, 0, 5] };
let mut admin_group = std::ptr::null_mut();
let success = AllocateAndInitializeSid(
&nt_authority,
2,
SECURITY_BUILTIN_DOMAIN_RID.try_into().unwrap(),
DOMAIN_ALIAS_RID_ADMINS.try_into().unwrap(),
0,
0,
0,
0,
0,
0,
&mut admin_group,
);
if success == 0 {
CloseHandle(token_handle);
return false;
}
let mut is_admin = 0i32;
let result = CheckTokenMembership(std::ptr::null_mut(), admin_group, &mut is_admin);
FreeSid(admin_group);
CloseHandle(token_handle);
result != 0 && is_admin != 0
}
}
}

View File

@@ -1,16 +1,19 @@
//! @file src/lib.rs
//! @desc 主模块,用于启动应用
//! @since Beta v0.7.2
//! 主模块,用于启动应用
//! @since Beta v0.8.8
mod client;
mod commands;
mod plugins;
mod utils;
#[cfg(target_os = "windows")]
mod watchdog;
#[cfg(target_os = "windows")]
mod yae;
use crate::client::create_mhy_client;
use crate::commands::{create_window, execute_js, get_dir_size, init_app};
use crate::commands::{create_window, execute_js, get_dir_size, init_app, is_in_admin};
use crate::plugins::{build_log_plugin, build_si_plugin};
use tauri::{generate_context, generate_handler, Builder, Manager, Window, WindowEvent};
use tauri::{generate_context, generate_handler, Manager, Window, WindowEvent};
// 窗口事件处理
fn window_event_handler(app: &Window, event: &WindowEvent) {
@@ -35,9 +38,35 @@ fn window_event_handler(app: &Window, event: &WindowEvent) {
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
Builder::default()
#[cfg(target_os = "windows")]
{
let args: Vec<String> = std::env::args().collect();
let is_watchdog = args.iter().any(|a| a == "--watchdog");
// 看门狗模式:不初始化 Tauri不加载单例纯等待 + 提权启动
if is_watchdog {
// 解析父进程 PID
let mut ppid: u32 = 0;
for a in &args {
if let Some(rest) = a.strip_prefix("--ppid=") {
if let Ok(v) = rest.parse::<u32>() {
ppid = v;
}
}
}
// 等父进程退出后再 runas 启动管理员实例,传入 --elevated 标志
let _ = watchdog::run_watchdog(ppid, "--elevated");
// 看门狗退出
return;
}
}
// 正常应用实例:加载单例插件,防止多实例
let mut builder = tauri::Builder::default();
// 只有在正常/管理员实例下才加载单例插件;看门狗不加载
builder = builder.plugin(build_si_plugin());
builder
.on_window_event(move |app, event| window_event_handler(app, event))
.plugin(build_si_plugin())
.plugin(tauri_plugin_deep_link::init())
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_fs::init())
@@ -61,7 +90,12 @@ pub fn run() {
create_window,
execute_js,
get_dir_size,
create_mhy_client
create_mhy_client,
is_in_admin,
#[cfg(target_os = "windows")]
yae::call_yae_dll,
#[cfg(target_os = "windows")]
watchdog::run_with_admin
])
.run(generate_context!())
.expect("error while running tauri application");

View File

@@ -1,6 +1,5 @@
//! @file src/plugins.rs
//! @desc 插件模块,用于注册插件
//! @since Beta v0.6.2
//! 插件模块,用于注册插件
//! @since Beta v0.7.8
use crate::utils::get_current_date;
use log::LevelFilter;
@@ -11,7 +10,23 @@ use tauri_plugin_single_instance::init;
// 单例插件
pub fn build_si_plugin<R: Runtime>() -> TauriPlugin<R> {
init(move |app, argv, _cwd| app.emit("active_deep_link", argv).unwrap())
init(move |app, argv, _cwd| {
// 把 argv 转成 Vec<String>
// let args: Vec<String> = argv.iter().map(|s| s.to_string()).collect();
// 如果包含提升约定参数,发出专门事件并短路退出
// if args.iter().any(|a| a == "--elevated") {
// 提升实例通常只负责传参或执行一次性任务,退出避免与主实例冲突
// std::process::exit(0);
// }
// 非提升启动:按原逻辑广播 deep link
if let Err(e) = app.emit("active_deep_link", argv) {
eprintln!("emit active_deep_link failed: {}", e);
}
// 回调必须返回 unit直接结束即可
})
}
// 日志插件

79
src-tauri/src/watchdog.rs Normal file
View File

@@ -0,0 +1,79 @@
//! 重启提权相关处理
//! @since Beta v0.8.7
#![cfg(target_os = "windows")]
use std::ffi::OsStr;
use std::iter::once;
use std::os::windows::ffi::OsStrExt;
use std::ptr::null_mut;
use std::time::Duration;
use tauri::AppHandle;
use windows_sys::Win32::Foundation::{HANDLE, HWND};
use windows_sys::Win32::Storage::FileSystem::SYNCHRONIZE;
use windows_sys::Win32::System::Threading::{OpenProcess, WaitForSingleObject, INFINITE};
use windows_sys::Win32::UI::Shell::ShellExecuteW;
use windows_sys::Win32::UI::WindowsAndMessaging::SW_SHOWNORMAL;
// 带参数启动
fn shell_runas_with_args(args: &str) -> Result<(), String> {
fn to_wide(s: &OsStr) -> Vec<u16> {
s.encode_wide().chain(once(0)).collect()
}
let exe_path = std::env::current_exe().map_err(|e| e.to_string())?;
let exe_wide = to_wide(exe_path.as_os_str());
let args_wide = to_wide(OsStr::new(args));
let cwd_wide =
exe_path.parent().map(|p| to_wide(p.as_os_str())).unwrap_or_else(|| to_wide(OsStr::new("")));
unsafe {
let result = ShellExecuteW(
0 as HWND,
to_wide(OsStr::new("runas")).as_ptr(),
exe_wide.as_ptr(),
args_wide.as_ptr(),
if cwd_wide.len() > 1 { cwd_wide.as_ptr() } else { null_mut() },
SW_SHOWNORMAL,
);
if (result as usize) > 32 {
Ok(())
} else {
Err("Failed to ShellExecuteW runas".into())
}
}
}
// 等待父进程退出(释放单例锁)后,再以管理员身份启动新实例
pub fn run_watchdog(parent_pid: u32, args_to_pass: &str) -> Result<(), String> {
// 打开父进程句柄用于等待
let handle: HANDLE = unsafe { OpenProcess(SYNCHRONIZE, 0, parent_pid) };
if handle == std::ptr::null_mut() {
// 如果拿不到句柄,可能父进程已退出,稍作等待后继续
std::thread::sleep(Duration::from_millis(300));
} else {
unsafe {
WaitForSingleObject(handle, INFINITE);
}
}
// 父进程已退出 → 触发 UAC 提权启动新实例
shell_runas_with_args(args_to_pass)
}
// 以管理员权限重启应用
#[tauri::command]
pub fn run_with_admin(app_handle: AppHandle) -> Result<(), String> {
let parent_pid = std::process::id();
let exe = std::env::current_exe().map_err(|e| e.to_string())?;
let mut cmd = std::process::Command::new(exe);
cmd
.arg("--watchdog")
.arg(format!("--ppid={}", parent_pid))
// 看门狗不加载单例插件(通过参数决定 main 的初始化)
.spawn()
.map_err(|e| format!("spawn watchdog failed: {e}"))?;
// 立即退出:单例锁释放
app_handle.exit(0);
Ok(())
}

191
src-tauri/src/yae/inject.rs Normal file
View File

@@ -0,0 +1,191 @@
//! DLL 注入相关功能
//! @since Beta v0.7.8
#![cfg(target_os = "windows")]
use std::ffi::OsStr;
use std::iter::once;
use std::os::windows::ffi::OsStrExt;
use std::ptr;
use windows_sys::Win32::Foundation::{CloseHandle, FreeLibrary, HANDLE, INVALID_HANDLE_VALUE};
use windows_sys::Win32::Storage::FileSystem::PIPE_ACCESS_DUPLEX;
use windows_sys::Win32::System::Diagnostics::Debug::WriteProcessMemory;
use windows_sys::Win32::System::Diagnostics::ToolHelp::{
CreateToolhelp32Snapshot, Module32FirstW, Module32NextW, MODULEENTRY32W, TH32CS_SNAPMODULE,
};
use windows_sys::Win32::System::LibraryLoader::{
GetModuleHandleA, GetProcAddress, LoadLibraryExW, DONT_RESOLVE_DLL_REFERENCES,
};
use windows_sys::Win32::System::Memory::{VirtualAllocEx, MEM_COMMIT, PAGE_READWRITE};
use windows_sys::Win32::System::Pipes::{
CreateNamedPipeW, PIPE_READMODE_MESSAGE, PIPE_TYPE_MESSAGE, PIPE_UNLIMITED_INSTANCES, PIPE_WAIT,
};
use windows_sys::Win32::System::Threading::{
CreateProcessW, CreateRemoteThread, WaitForSingleObject, INFINITE, PROCESS_INFORMATION,
STARTUPINFOW,
};
/// 转为宽字符串
pub fn to_wide_string(s: &str) -> Vec<u16> {
OsStr::new(s).encode_wide().chain(once(0)).collect()
}
/// 创建命名管道
pub fn create_named_pipe(pipe_name: &str) -> HANDLE {
let full_pipe_name = format!(r"\\.\pipe\{}", pipe_name);
let wide: Vec<u16> = to_wide_string(&full_pipe_name);
unsafe {
let handle = CreateNamedPipeW(
wide.as_ptr(),
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
512,
512,
0,
ptr::null_mut(),
);
if handle == INVALID_HANDLE_VALUE {
panic!("CreateNamedPipeW failed");
}
handle
}
}
/// 启动目标进程附加cwd
pub fn spawn_process(path: &str) -> PROCESS_INFORMATION {
let wide_path: Vec<u16> = to_wide_string(path);
let cwd = std::path::Path::new(path).parent().unwrap().to_str().unwrap();
let wide_cwd: Vec<u16> = to_wide_string(cwd);
unsafe {
let mut si: STARTUPINFOW = std::mem::zeroed();
si.cb = std::mem::size_of::<STARTUPINFOW>() as u32;
let mut pi: PROCESS_INFORMATION = std::mem::zeroed();
let success = CreateProcessW(
wide_path.as_ptr(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
0,
0,
ptr::null_mut(),
wide_cwd.as_ptr(),
&mut si,
&mut pi,
);
if success == 0 {
panic!("CreateProcessW failed");
}
pi
}
}
/// 注入 DLL
pub fn inject_dll(pi: &PROCESS_INFORMATION, dll_path: &str) {
let dll_utf16: Vec<u16> = to_wide_string(dll_path);
let size = dll_utf16.len() * 2;
unsafe {
let addr = VirtualAllocEx(pi.hProcess, ptr::null_mut(), size, MEM_COMMIT, PAGE_READWRITE);
if addr.is_null() {
panic!("VirtualAllocEx failed");
}
let success =
WriteProcessMemory(pi.hProcess, addr, dll_utf16.as_ptr() as *const _, size, ptr::null_mut());
if success == 0 {
panic!("WriteProcessMemory failed");
}
let k32 = GetModuleHandleA(b"kernel32.dll\0".as_ptr());
if k32 == std::ptr::null_mut() {
panic!("GetModuleHandleA failed");
}
let loadlib = GetProcAddress(k32, b"LoadLibraryW\0".as_ptr());
if loadlib.is_none() {
panic!("GetProcAddress failed");
}
let thread = CreateRemoteThread(
pi.hProcess,
ptr::null_mut(),
0,
Some(std::mem::transmute(loadlib)),
addr,
0,
ptr::null_mut(),
);
if thread.is_null() {
panic!("CreateRemoteThread failed");
}
WaitForSingleObject(thread, INFINITE);
}
}
/// 枚举模块,找到 DLL 基址
pub fn find_module_base(pid: u32, dll_name: &str) -> Option<usize> {
unsafe {
let snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
if snapshot == INVALID_HANDLE_VALUE {
return None;
}
let mut me32 =
MODULEENTRY32W { dwSize: std::mem::size_of::<MODULEENTRY32W>() as u32, ..Default::default() };
if Module32FirstW(snapshot, &mut me32) != 0 {
loop {
let name = String::from_utf16_lossy(&me32.szModule);
if name.contains(dll_name) {
CloseHandle(snapshot);
return Some(me32.modBaseAddr as usize);
}
if Module32NextW(snapshot, &mut me32) == 0 {
break;
}
}
}
CloseHandle(snapshot);
}
None
}
/// 执行 YaeMain
pub fn call_yaemain(pi: &PROCESS_INFORMATION, base: usize, dll_path: &str) {
let dll_path_wide: Vec<u16> = to_wide_string(dll_path);
unsafe {
let local =
LoadLibraryExW(dll_path_wide.as_ptr(), std::ptr::null_mut(), DONT_RESOLVE_DLL_REFERENCES);
if local == std::ptr::null_mut() {
panic!("LoadLibraryExW failed");
}
let proc = GetProcAddress(local, b"YaeMain\0".as_ptr()).expect("无法找到 YaeMain");
// Option<unsafe extern "system" fn() -> isize>
let proc_addr = proc as *const () as usize;
let rva = proc_addr - local as usize;
println!("YaeMain RVA: {:#x}", rva);
FreeLibrary(local);
let remote_yaemain = (base + rva) as *mut std::ffi::c_void;
// 在远程进程里调用 YaeMain(hModule)
CreateRemoteThread(
pi.hProcess,
std::ptr::null_mut(),
0,
Some(std::mem::transmute(remote_yaemain)),
base as *mut _,
0,
std::ptr::null_mut(),
);
}
}

204
src-tauri/src/yae/mod.rs Normal file
View File

@@ -0,0 +1,204 @@
//! Yae 相关处理
//! @since Beta v0.8.7
#![cfg(target_os = "windows")]
pub mod inject;
pub mod proto;
use inject::{call_yaemain, create_named_pipe, find_module_base, inject_dll, spawn_process};
use proto::parse_achi_list;
use serde_json::Value;
use std::fs::File;
use std::io::{self, Read, Write};
use std::os::windows::io::{FromRawHandle, RawHandle};
use std::sync::Arc;
use tauri::{AppHandle, Emitter, Manager};
use windows_sys::Win32::Foundation::CloseHandle;
use windows_sys::Win32::System::Pipes::ConnectNamedPipe;
// 读取配置值
fn read_rva(key: &str) -> i32 {
let path = format!("nativeConfig.methodRva.chinese.{}", key);
read_conf(&path)
}
// 读取配置文件
pub fn read_conf(path: &str) -> i32 {
// 编译时嵌入 JSON 文件值都是32位整数
let data = include_str!("../../lib/conf.json");
let json: Value = serde_json::from_str(data).expect("Invalid JSON");
// 按 '.' 分割 key
let mut current = &json;
for key in path.split('.') {
match current.get(key) {
Some(value) => current = value,
None => return 0, // 如果找不到 key返回默认值 0
}
}
current.as_i64().unwrap_or(0) as i32
}
fn read_u32_le<R: Read>(r: &mut R) -> io::Result<u32> {
let mut buf = [0u8; 4];
match r.read_exact(&mut buf) {
Ok(_) => Ok(u32::from_le_bytes(buf)),
Err(e) => Err(e),
}
}
fn read_f64_le<R: Read>(r: &mut R) -> io::Result<f64> {
let mut buf = [0u8; 8];
match r.read_exact(&mut buf) {
Ok(_) => Ok(f64::from_le_bytes(buf)),
Err(e) => Err(e),
}
}
fn read_exact_vec<R: Read>(r: &mut R, len: usize) -> io::Result<Vec<u8>> {
let mut v = vec![0u8; len];
match r.read_exact(&mut v) {
Ok(_) => Ok(v),
Err(e) => Err(e),
}
}
/// 调用 dll
#[tauri::command]
pub fn call_yae_dll(app_handle: AppHandle, game_path: String) -> Result<(), String> {
let dll_path = app_handle.path().resource_dir().unwrap().join("resources/YaeAchievementLib.dll");
dbg!(&dll_path);
// 0. 创建 YaeAchievementPipe 的 命名管道,获取句柄
dbg!("开始启动 YaeAchievementPipe 命名管道");
let _pipe_handle = create_named_pipe("YaeAchievementPipe");
// 1. 启动游戏进程
let pi = spawn_process(&game_path);
dbg!("游戏进程启动完成");
// 2. 注入 DLL
inject_dll(&pi, dll_path.to_str().unwrap());
dbg!("DLL 注入完成");
// 3. 找到 DLL 基址
let base = find_module_base(pi.dwProcessId, "YaeAchievementLib.dll").expect("找不到 DLL 基址");
dbg!("找到 DLL 基址: {:X}", base);
// 4. 调用 YaeMain
call_yaemain(&pi, base, dll_path.to_str().unwrap());
dbg!("YaeMain 调用完成");
// 根据句柄等待命名管道连接
let retry_count = 50;
for _ in 0..retry_count {
let result = unsafe { ConnectNamedPipe(_pipe_handle, std::ptr::null_mut()) };
if result != 0 {
dbg!("命名管道连接成功");
break;
} else {
std::thread::sleep(std::time::Duration::from_secs(1));
}
}
// 5. 读取命名管道数据,循环读取,根据接受字节进行通信
let file = unsafe { File::from_raw_handle(_pipe_handle as RawHandle) };
let file = Arc::new(file);
// No raw HANDLE captured in the closure. Only Arc<File>.
std::thread::spawn({
let file = file.clone();
move || {
let mut file = file.try_clone().expect("Failed to clone pipe file");
let mut cmd = [0u8; 1];
loop {
match file.read_exact(&mut cmd) {
// 输出命令字节
Ok(_) => {
println!("收到命令: {}", cmd[0]);
match cmd[0] {
0x01 => {
match read_u32_le(&mut file) {
Ok(len) => {
// 再读数据
match read_exact_vec(&mut file, len as usize) {
Ok(data) => {
println!("长度: {}", len);
// 解码成 AchievementInfo
match parse_achi_list(&data) {
Ok(list) => {
println!("解码成功,成就列表长度: {}", list.len());
let json = serde_json::to_string_pretty(&list).unwrap();
let _ = app_handle.emit("yae_achi_list", json);
}
Err(e) => println!("解析失败: {:?}", e),
}
}
Err(e) => println!("读取数据失败: {:?}", e),
}
}
Err(e) => println!("读取长度失败: {:?}", e),
}
}
0x02 => {
println!("PlayerStoreNotify");
// 读取剩余数据
match read_u32_le(&mut file) {
Ok(len) => match read_exact_vec(&mut file, len as usize) {
Ok(_data) => println!("长度: {}", len),
Err(e) => println!("读取数据失败: {:?}", e),
},
Err(e) => println!("读取长度失败: {:?}", e),
}
}
0x03 => {
println!("PlayerPropNotify");
// 读取剩余数据
match read_u32_le(&mut file) {
Ok(prop_type) => match read_f64_le(&mut file) {
Ok(value) => println!("Prop 类型: {}, 值: {}", prop_type, value),
Err(e) => println!("读取值失败: {:?}", e),
},
Err(e) => println!("读取类型失败: {:?}", e),
}
}
0xFC => {
let _ = file.write_all(&read_conf("nativeConfig.achievementCmdId").to_le_bytes());
let _ = file.write_all(&read_conf("nativeConfig.storeCmdId").to_le_bytes());
}
0xFD => {
for key in [
"doCmd",
"updateNormalProp",
"newString",
"findGameObject",
"eventSystemUpdate",
"simulatePointerClick",
"toInt32",
"tcpStatePtr",
"sharedInfoPtr",
"decompress",
] {
let _ = file.write_all(&read_rva(key).to_le_bytes());
}
}
0xFF => {
let _ = file.write_all(&[1]);
break;
}
_ => println!("收到未知命令: {}", cmd[0]),
}
}
Err(e) => {
println!("读取失败: {:?}", e);
break;
}
}
}
}
});
unsafe {
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
Ok(())
}

170
src-tauri/src/yae/proto.rs Normal file
View File

@@ -0,0 +1,170 @@
//! Yae 成就信息的 Protobuf 定义
//! @since Beta v0.7.9
#![cfg(target_os = "windows")]
use crate::yae::read_conf;
use prost::encoding::{decode_key, WireType};
use prost::DecodeError;
use prost::Message;
use serde::Serialize;
use std::collections::HashMap;
use std::io::{Cursor, Read};
#[allow(dead_code)]
#[derive(Clone, PartialEq, Message, Serialize)]
pub struct AchievementProtoFieldInfo {
#[prost(uint32, tag = "1")]
pub id: u32,
#[prost(uint32, tag = "2")]
pub status: u32,
#[prost(uint32, tag = "3")]
pub total_progress: u32,
#[prost(uint32, tag = "4")]
pub current_progress: u32,
#[prost(uint32, tag = "5")]
pub finish_timestamp: u32,
}
#[allow(dead_code)]
#[derive(Clone, PartialEq, Message, Serialize)]
pub struct AchievementItem {
#[prost(uint32, tag = "1")]
pub pre: u32,
#[prost(uint32, tag = "2")]
pub group: u32,
#[prost(string, tag = "3")]
pub name: String,
#[prost(string, tag = "4")]
pub description: String,
}
#[allow(dead_code)]
#[derive(Clone, PartialEq, Message, Serialize)]
pub struct MethodRvaConfig {
#[prost(uint32, tag = "1")]
pub do_cmd: u32,
#[prost(uint32, tag = "3")]
pub update_normal_prop: u32,
#[prost(uint32, tag = "4")]
pub new_string: u32,
#[prost(uint32, tag = "5")]
pub find_game_object: u32,
#[prost(uint32, tag = "6")]
pub event_system_update: u32,
#[prost(uint32, tag = "7")]
pub simulate_pointer_click: u32,
#[prost(uint32, tag = "8")]
pub to_int32: u32,
#[prost(uint32, tag = "9")]
pub tcp_state_ptr: u32,
#[prost(uint32, tag = "10")]
pub shared_info_ptr: u32,
#[prost(uint32, tag = "11")]
pub decompress: u32,
}
#[allow(dead_code)]
#[derive(Clone, PartialEq, Message, Serialize)]
pub struct NativeLibConfig {
#[prost(uint32, tag = "1")]
pub store_cmd_id: u32,
#[prost(uint32, tag = "2")]
pub achievement_cmd_id: u32,
#[prost(map = "uint32, message", tag = "10")]
pub method_rva: HashMap<u32, MethodRvaConfig>,
}
#[allow(dead_code)]
#[derive(Clone, PartialEq, Message, Serialize)]
pub struct AchievementInfo {
#[prost(string, tag = "1")]
pub version: String,
#[prost(map = "uint32, string", tag = "2")]
pub group: HashMap<u32, String>,
#[prost(map = "uint32, message", tag = "3")]
pub items: HashMap<u32, AchievementItem>,
#[prost(message, tag = "4")]
pub pb_info: Option<AchievementProtoFieldInfo>,
#[prost(message, tag = "5")]
pub native_config: Option<NativeLibConfig>,
}
#[derive(Debug, Default, Serialize)]
pub struct UiafAchiItem {
pub id: u32,
pub current: u32,
pub timestamp: u32,
pub status: u32,
}
pub fn parse_achi_list(bytes: &[u8]) -> Result<Vec<UiafAchiItem>, DecodeError> {
let mut cursor = Cursor::new(bytes);
let mut dicts: Vec<HashMap<u32, u32>> = Vec::new();
while let Ok((_, wire_type)) = decode_key(&mut cursor) {
if wire_type == WireType::LengthDelimited {
let len = prost::encoding::decode_varint(&mut cursor)? as usize;
let mut buf = vec![0u8; len];
if cursor.read_exact(&mut buf).is_err() {
continue;
}
let mut inner = Cursor::new(&buf);
let mut dict = HashMap::new();
while let Ok((tag, wire_type)) = decode_key(&mut inner) {
if wire_type != WireType::Varint {
break;
}
let value = prost::encoding::decode_varint(&mut inner)? as u32;
dict.insert(tag, value);
}
// 输出 dict
println!("{:?}", dict);
// dict 至少需要两个 key
if dict.len() > 2 {
dicts.push(dict)
}
}
}
let _id = read_conf("id") as u32;
let _status = read_conf("status") as u32;
let _cur = read_conf("currentProgress") as u32;
let _ts = read_conf("finishTimestamp") as u32;
let achievements = dicts
.into_iter()
.map(|d| UiafAchiItem {
id: d.get(&_id).copied().unwrap_or(0),
status: d.get(&_status).copied().unwrap_or(0),
current: d.get(&_cur).copied().unwrap_or(0),
timestamp: d.get(&_ts).copied().unwrap_or(0),
})
.filter(|a| a.status != 0)
.collect();
Ok(achievements)
}

View File

@@ -2,7 +2,7 @@
"$schema": "https://schema.tauri.app/config/2",
"productName": "TeyvatGuide",
"identifier": "TeyvatGuide",
"version": "0.8.2",
"version": "0.8.8",
"build": {
"beforeDevCommand": "pnpm vite:dev",
"beforeBuildCommand": "pnpm vite:build",
@@ -30,7 +30,8 @@
],
"targets": ["msi", "app", "dmg"],
"windows": { "wix": { "language": "zh-CN" } },
"macOS": {}
"macOS": {},
"resources": { "lib/YaeAchievementLib.dll": "resources/YaeAchievementLib.dll" }
},
"app": {
"withGlobalTauri": true,

View File

@@ -1,3 +1,4 @@
<!--主界面 -->
<template>
<v-app v-model:theme="vuetifyTheme">
<TSidebar v-if="isMain" />
@@ -164,7 +165,7 @@ async function getDeepLink(): Promise<UnlistenFn> {
const windowGet = new webviewWindow.WebviewWindow("TeyvatGuide");
if (await windowGet.isMinimized()) await windowGet.unminimize();
await windowGet.setFocus();
const payload = parseDeepLink(e.payload);
const payload = await parseDeepLink(e.payload);
if (payload === false) {
showSnackbar.error("无效的 deep link", 3000);
await TGLogger.Error(`[App][getDeepLink] 无效的 deep link ${JSON.stringify(e.payload)}`);
@@ -175,14 +176,14 @@ async function getDeepLink(): Promise<UnlistenFn> {
});
}
function parseDeepLink(payload: string | string[]): string | false {
async function parseDeepLink(payload: string | string[]): Promise<string | false> {
try {
if (typeof payload === "string") return payload;
if (payload.length < 2) return "teyvatguide://";
return payload[1];
} catch (e) {
if (e instanceof Error) {
TGLogger.Error(`[App][parseDeepLink] ${e.name}: ${e.message}`);
await TGLogger.Error(`[App][parseDeepLink] ${e.name}: ${e.message}`);
} else console.error(e);
return false;
}

View File

@@ -1,3 +1,4 @@
<!-- 版块小组件菜单 -->
<template>
<div class="tgn-container">
<div v-for="navItem in nav" :key="navItem.id" class="tgn-nav" @click="toNav(navItem)">
@@ -5,13 +6,14 @@
<span>{{ navItem.name }}</span>
</div>
<div v-if="hasNav" class="tgn-nav">
<v-icon size="25" @click="tryGetCode" title="查看兑换码">mdi-code-tags-check</v-icon>
<v-icon size="25" @click="tryGetCode" title="查看兑换码" color="var(--tgc-od-orange)">
mdi-code-tags-check
</v-icon>
</div>
<ToLivecode v-model="showOverlay" :data="codeData" v-model:actId="actId" />
<ToLivecode v-model="showOverlay" :gid="model" :data="codeData" :actId="actId" />
</div>
</template>
<script lang="ts" setup>
import TMiImg from "@comp/app/t-mi-img.vue";
import showDialog from "@comp/func/dialog.js";
import showSnackbar from "@comp/func/snackbar.js";
import ApiHubReq from "@req/apiHubReq.js";
@@ -25,18 +27,19 @@ import { createPost } from "@utils/TGWindow.js";
import { storeToRefs } from "pinia";
import { computed, onMounted, ref, shallowRef, watch } from "vue";
import TMiImg from "./t-mi-img.vue";
import ToLivecode from "./to-livecode.vue";
const { isLogin } = storeToRefs(useAppStore());
const model = defineModel<number>({ default: 2 });
const { isLogin } = storeToRefs(useAppStore());
const actId = ref<string>();
const showOverlay = ref<boolean>(false);
const nav = shallowRef<TGApp.BBS.Navigator.Navigator[]>([]);
const codeData = shallowRef<TGApp.BBS.Navigator.CodeData[]>([]);
const showOverlay = ref<boolean>(false);
const actId = ref<string>();
const hasNav = computed<TGApp.BBS.Navigator.Navigator | undefined>(() => {
const liveNames = ["前瞻直播", "前瞻节目", "直播兑换码"];
const liveNames = ["前瞻直播", "前瞻节目", "直播兑换码", "特别节目"];
const find = nav.value.find((item) => liveNames.includes(item.name));
if (find) return find;
return nav.value.find((item) => item.name.includes("前瞻"));
@@ -49,34 +52,55 @@ watch(
async () => await loadNav(),
);
/**
* 加载组件数据
* @returns {Promise<void>}
*/
async function loadNav(): Promise<void> {
nav.value = await ApiHubReq.home(model.value);
try {
nav.value = await ApiHubReq.home(model.value);
console.debug(`[TGameNav][loadNav] 组件数据:`, nav.value);
} catch (e) {
await TGLogger.Error(`[TGameNav][loadNav] 加载组件数据失败:${e}`);
}
}
/**
* 获取兑换码
* @returns {Promise<void>}
*/
async function tryGetCode(): Promise<void> {
if (!hasNav.value) return;
const actIdFind = new URL(hasNav.value.app_path).searchParams.get("act_id");
if (!actIdFind) {
showSnackbar.warn("未找到活动ID");
await TGLogger.Warn(`[TGameNav][tryGetCode] 未找到活动ID链接${hasNav.value.app_path}`);
return;
}
actId.value = actIdFind;
const res = await OtherApi.code(actIdFind);
if (!Array.isArray(res)) {
showSnackbar.warn(`[${res.retcode}] ${res.message}`);
await TGLogger.Warn(`[TGameNav][tryGetCode] 获取兑换码失败:${JSON.stringify(res)}`);
return;
}
codeData.value = res;
console.debug(`[TGameNave][tryGetCode] 兑换码数据:`, codeData.value);
showSnackbar.success("获取兑换码成功");
await TGLogger.Info(JSON.stringify(res));
showOverlay.value = true;
}
/**
* 跳转到活动页面
* @param {TGApp.BBS.Navigator.Navigator} item 导航项
* @returns {Promise<void>}
*/
async function toNav(item: TGApp.BBS.Navigator.Navigator): Promise<void> {
if (!isLogin.value) {
showSnackbar.warn("请先登录");
return;
}
console.debug(`[TGameNav][toNav] 跳转到活动页面:`, item);
await TGLogger.Info(`[TGameNav][toNav] 打开网页活动 ${item.name}`);
await TGLogger.Info(`[TGameNav][toNav] ${item.app_path}`);
const link = new URL(item.app_path);
@@ -110,7 +134,11 @@ async function toNav(item: TGApp.BBS.Navigator.Navigator): Promise<void> {
else await TGClient.open("web_act", item.app_path);
}
// 处理 protocol
/**
* 处理米游社论坛链接
* @param {URL} link 链接
* @returns {Promise<void>}
*/
async function toBBS(link: URL): Promise<void> {
if (link.protocol == "mihoyobbs:") {
if (link.hostname === "article") {
@@ -148,25 +176,26 @@ async function toBBS(link: URL): Promise<void> {
border-radius: 4px;
color: var(--tgc-white-1);
cursor: pointer;
img {
width: 28px;
height: 28px;
}
span {
display: none;
margin-left: 4px;
color: var(--common-text-title);
font-family: var(--font-title);
font-size: 16px;
}
&:hover span {
display: block;
}
}
.dark .tgn-nav {
@include github-styles.github-card("dark");
}
.tgn-nav img {
width: 28px;
height: 28px;
}
.tgn-nav span {
display: none;
color: var(--common-text-title);
font-family: var(--font-title);
font-size: 16px;
}
.tgn-nav:hover span {
display: block;
}
</style>

View File

@@ -0,0 +1,95 @@
<!-- 改变帖子视图 -->
<template>
<div
class="tpw2-box"
:class="postViewWide ? '' : 'active'"
:title="postViewWide ? '切换为窄屏视图' : '切换为宽屏视图'"
data-html2canvas-ignore
>
<div class="tpw2-btn" @click="switchPostWidth()">
<v-icon size="20">
{{ postViewWide ? "mdi-arrow-collapse-horizontal" : "mdi-arrow-expand-horizontal" }}
</v-icon>
</div>
<div class="tpw2-beta-hint" title="测试功能,可能存在适配问题">β</div>
</div>
</template>
<script lang="ts" setup>
import useAppStore from "@store/app.js";
import { storeToRefs } from "pinia";
const { postViewWide } = storeToRefs(useAppStore());
function switchPostWidth(): void {
postViewWide.value = !postViewWide.value;
}
</script>
<style lang="scss" scoped>
@use "@styles/github.styles.scss" as github-styles;
.tpw2-box {
@include github-styles.github-card;
position: fixed;
top: 112px;
left: 16px;
display: flex;
width: 36px;
height: 36px;
box-sizing: border-box;
align-items: center;
justify-content: center;
border-radius: 50%;
cursor: pointer;
&.active {
background: var(--tgc-btn-1);
box-shadow: 1px 3px 6px var(--common-shadow-2);
color: var(--btn-text);
}
&:hover:not(.active) {
background: var(--common-shadow-1);
}
}
.dark .tpw2-box {
border: 1px solid var(--common-shadow-1);
box-shadow: 1px 3px 6px var(--common-shadow-t-2);
&:not(.active) {
@include github-styles.github-card("dark");
&:hover {
background: var(--common-shadow-6);
}
}
}
.tpw2-btn {
position: relative;
z-index: 1;
display: flex;
width: 20px;
height: 20px;
align-items: center;
justify-content: center;
}
.tpw2-beta-hint {
position: absolute;
right: -5px;
bottom: -5px;
display: flex;
width: 16px;
height: 16px;
align-items: center;
justify-content: center;
border-radius: 50%;
background: var(--tgc-od-green);
box-shadow: 1px 3px 6px var(--common-shadow-2);
color: var(--tgc-blue-1);
font-size: 10px;
font-weight: bold;
}
</style>

View File

@@ -112,7 +112,10 @@
}"
v-else
>
{{ props.modelValue.post.post_id }}
<span>{{ props.modelValue.post.post_id }}</span>
<template v-if="isDevEnv">
<span data-html2canvas-ignore>[{{ props.modelValue.post.view_type }}]</span>
</template>
</div>
</div>
</template>
@@ -154,7 +157,8 @@ export type RenderCard = {
topics: Array<TGApp.BBS.Post.Topic>;
reasons: Array<TGApp.BBS.Post.RecommendTags>;
};
// @ts-expect-error The import.meta meta-property is not allowed in files which will build into CommonJS output.
const isDevEnv = import.meta.env.MODE === "development";
const stats: Readonly<Array<RenderStatus>> = [
{ stat: 0, label: "未知", color: "var(--tgc-od-red)" },
{ stat: 1, label: "进行中", color: "var(--tgc-od-green)" },

Some files were not shown because too many files have changed in this diff Show More