Skip to content

Instantly share code, notes, and snippets.

@yougg
Created June 6, 2026 09:02
Show Gist options
  • Select an option

  • Save yougg/77603db480be1beedbfeb2b21da6dc42 to your computer and use it in GitHub Desktop.

Select an option

Save yougg/77603db480be1beedbfeb2b21da6dc42 to your computer and use it in GitHub Desktop.
Get Genshin Impact wish history URL running in Wine/Proton on Linux, 获取Linux Wine运行的原神的抽卡记录脚本
#!/usr/bin/env bash
# ==============================================================================
# Script Name: get_gacha_url.sh
# Description: Get Genshin Impact wish history URL running in Wine/Proton on Linux
# Environments: Native Wine, Steam Proton, Lutris, Heroic Games Launcher, AAGL, etc.
# Language: English (Comments) / Adaptive (Console Output)
# ==============================================================================
# Initialize localized messages based on system language
if [[ "$LANG" == *"zh_"* ]]; then
msg_check_curl="错误: 未找到 curl 命令,请先安装 curl。"
msg_finding_log="正在寻找原神日志文件..."
msg_log_not_found="无法找到原神日志文件!请确保您至少在 Wine 中运行过一次游戏并打开过抽卡历史记录。"
msg_log_found="找到日志文件: %s"
msg_wineprefix="定位 WINEPREFIX: %s"
msg_parse_failed="无法在日志中解析出游戏目录!请确认已在游戏中打开过抽卡历史记录页面。"
msg_gamedir="解析游戏目录 (Linux): %s"
msg_gamedir_not_exist="游戏目录不存在,请确认路径解析是否正确: %s"
msg_webcaches_not_found="未找到 webCaches 目录,请在游戏中重新打开一次抽卡历史记录页面!"
msg_cache_ver_not_found="未找到有效的缓存版本目录!"
msg_cache_file_not_found="未找到缓存文件 data_2!"
msg_extracting="正在从缓存文件提取抽卡链接..."
msg_no_links="未能从缓存中找到任何抽卡记录链接!请先在游戏中打开一次抽卡历史记录。"
msg_api_host="使用 API 校验主机名: %s"
msg_verifying="共找到 %d 个缓存链接,正在反向逐一验证有效性..."
msg_success="[成功] 找到有效的抽卡记录链接!"
msg_fail="[错误] 所有的缓存链接均已失效!请在游戏中重新打开抽卡历史记录页面后再试。"
msg_link_invalid="链接 %d 已失效,正在检查更早的链接..."
msg_clipboard_wayland="链接已复制到 Wayland 剪贴板。"
msg_clipboard_x11="链接已复制到 X11 剪贴板。"
msg_clipboard_none="提示: 未检测到 xclip, xsel 或 wl-copy,请手动复制上方链接。"
else
msg_check_curl="Error: curl command not found, please install curl first."
msg_finding_log="Looking for Genshin Impact log file..."
msg_log_not_found="Cannot find Genshin Impact log file! Make sure you have run the game in Wine at least once and opened the Wish History."
msg_log_found="Found log file: %s"
msg_wineprefix="Located WINEPREFIX: %s"
msg_parse_failed="Failed to parse game directory from log! Make sure you have opened the Wish History page in game."
msg_gamedir="Parsed game directory (Linux): %s"
msg_gamedir_not_exist="Game directory does not exist, please check if path mapping is correct: %s"
msg_webcaches_not_found="webCaches directory not found, please reopen the Wish History page in game!"
msg_cache_ver_not_found="Valid cache version directory not found!"
msg_cache_file_not_found="Cache file data_2 not found!"
msg_extracting="Extracting wish history links from cache file..."
msg_no_links="No wish history links found in cache! Please open Wish History in game first."
msg_api_host="Using API verification host: %s"
msg_verifying="Found %d cache links, verifying validity backwards..."
msg_success="[Success] Found valid wish history link!"
msg_fail="[Error] All cache links have expired! Please reopen the Wish History page in game and try again."
msg_link_invalid="Link %d expired, trying the next one..."
msg_clipboard_wayland="Link copied to Wayland clipboard."
msg_clipboard_x11="Link copied to X11 clipboard."
msg_clipboard_none="Note: xclip, xsel or wl-copy not found, please copy the link above manually."
fi
# Check basic dependencies
if ! command -v curl >/dev/null 2>&1; then
echo -e "\033[31m${msg_check_curl}\033[0m"
exit 1
fi
# Clipboard helper function
copy_to_clipboard() {
local text="$1"
if command -v wl-copy >/dev/null 2>&1; then
echo -n "$text" | wl-copy
echo -e "\033[32m${msg_clipboard_wayland}\033[0m"
elif command -v xclip >/dev/null 2>&1; then
echo -n "$text" | xclip -selection clipboard
echo -e "\033[32m${msg_clipboard_x11}\033[0m"
elif command -v xsel >/dev/null 2>&1; then
echo -n "$text" | xsel --clipboard --input
echo -e "\033[32m${msg_clipboard_x11}\033[0m"
else
echo -e "\033[33m${msg_clipboard_none}\033[0m"
fi
}
# Find Genshin Impact log file output_log.txt
find_log_file() {
# Scan common wineprefix and launcher directories
local common_paths=(
"$HOME/.local/share/anime-game-launcher/prefix/drive_c/users/$USER/AppData/LocalLow/miHoYo"
"$HOME/.wine/drive_c/users/$USER/AppData/LocalLow/miHoYo"
"$HOME/.wine/drive_c/users/steamuser/AppData/LocalLow/miHoYo"
"$HOME/.local/share/an-anime-game-launcher/wineprefix/drive_c/users/steamuser/AppData/LocalLow/miHoYo"
"$HOME/.var/app/com.gitlab.kresiaz.an_anime_game_launcher/data/wineprefix/drive_c/users/steamuser/AppData/LocalLow/miHoYo"
)
# 1. Look into common directories
for path in "${common_paths[@]}"; do
if [ -d "$path" ]; then
local f
f=$(find "$path" -type f -name "output_log.txt" 2>/dev/null | head -n 1)
if [ -n "$f" ]; then
echo "$f"
return 0
fi
fi
done
# 2. Search Steam compatdata directory
if [ -d "$HOME/.steam/steam/steamapps/compatdata" ]; then
local f
f=$(find "$HOME/.steam/steam/steamapps/compatdata" -maxdepth 6 -type f -path "*/AppData/LocalLow/miHoYo/*/output_log.txt" 2>/dev/null | head -n 1)
if [ -n "$f" ]; then
echo "$f"
return 0
fi
fi
# 3. Search Flatpak containers and other user-local folders
local f
f=$(find "$HOME/.var" "$HOME/.local/share" -maxdepth 8 -type f -path "*/AppData/LocalLow/miHoYo/*/output_log.txt" 2>/dev/null | head -n 1)
if [ -n "$f" ]; then
echo "$f"
return 0
fi
# 4. Fallback: Search User Home directory with limited depth
local f
f=$(find "$HOME" -maxdepth 6 -type f -path "*/AppData/LocalLow/miHoYo/*/output_log.txt" 2>/dev/null | head -n 1)
if [ -n "$f" ]; then
echo "$f"
return 0
fi
return 1
}
echo "$msg_finding_log"
log_file=$(find_log_file)
if [ -z "$log_file" ]; then
echo -e "\033[31m${msg_log_not_found}\033[0m"
exit 1
fi
printf "${msg_log_found}\n" "$log_file"
# Trace WINEPREFIX directory upwards by checking drive_c location
current_dir=$(dirname "$log_file")
wineprefix=""
while [ "$current_dir" != "/" ] && [ -n "$current_dir" ]; do
if [ -d "$current_dir/drive_c" ]; then
wineprefix="$current_dir"
break
fi
current_dir=$(dirname "$current_dir")
done
if [ -z "$wineprefix" ]; then
if [ -n "$WINEPREFIX" ] && [ -d "$WINEPREFIX" ]; then
wineprefix="$WINEPREFIX"
else
wineprefix="$HOME/.wine"
fi
fi
printf "${msg_wineprefix}\n" "$wineprefix"
# Parse game directory Windows path from log
win_path=$(grep -o -E '[A-Za-z]:[/\\][^"'\'']*(GenshinImpact_Data|YuanShen_Data)' "$log_file" | head -n 1 | tr '\\' '/')
if [ -z "$win_path" ]; then
echo -e "\033[31m${msg_parse_failed}\033[0m"
exit 1
fi
# Extract drive letter and relative path
drive_letter=$(echo "$win_path" | cut -d':' -f1 | tr '[:upper:]' '[:lower:]')
rel_path=$(echo "$win_path" | cut -d':' -f2-)
# Convert Windows path to Linux real path
if [ "$drive_letter" = "c" ]; then
linux_gamedir="${wineprefix}/drive_c${rel_path}"
else
# Check Wine dosdevices mapping
if [ -L "${wineprefix}/dosdevices/${drive_letter}:" ]; then
target_dir=$(readlink -f "${wineprefix}/dosdevices/${drive_letter}:")
linux_gamedir="${target_dir}${rel_path}"
else
# Fallback to drive_c relative append
linux_gamedir="${wineprefix}/drive_c${rel_path}"
fi
fi
printf "${msg_gamedir}\n" "$linux_gamedir"
if [ ! -d "$linux_gamedir" ]; then
printf "\033[31m${msg_gamedir_not_exist}\033[0m\n" "$linux_gamedir"
exit 1
fi
# Locate webCaches folder (Case-insensitive check for robustness)
webcache_dir=""
if [ -d "$linux_gamedir/webCaches" ]; then
webcache_dir="$linux_gamedir/webCaches"
elif [ -d "$linux_gamedir/webcaches" ]; then
webcache_dir="$linux_gamedir/webcaches"
else
webcache_dir=$(find "$linux_gamedir" -maxdepth 1 -type d -iname "webcaches" | head -n 1)
fi
if [ -z "$webcache_dir" ] || [ ! -d "$webcache_dir" ]; then
echo -e "\033[31m${msg_webcaches_not_found}\033[0m"
exit 1
fi
# Find the latest modified version directory (ignoring generic cache dirs)
cache_ver_dir=$(find "$webcache_dir" -mindepth 1 -maxdepth 1 -type d | while read -r dir; do
base=$(basename "$dir")
if [[ "$base" =~ ^[0-9]+(\.[0-9]+)+ ]]; then
echo "$(stat -c %Y "$dir" 2>/dev/null || echo 0) $dir"
fi
done | sort -rn | head -n 1 | cut -d' ' -f2-)
if [ -z "$cache_ver_dir" ] || [ ! -d "$cache_ver_dir" ]; then
# Fallback to webcache_dir root if no version subdirectory matches
cache_ver_dir="$webcache_dir"
fi
# Find the cache data file data_2
cachefile=""
if [ -f "$cache_ver_dir/Cache/Cache_Data/data_2" ]; then
cachefile="$cache_ver_dir/Cache/Cache_Data/data_2"
elif [ -f "$cache_ver_dir/cache/cache_data/data_2" ]; then
cachefile="$cache_ver_dir/cache/cache_data/data_2"
else
cachefile=$(find "$cache_ver_dir" -type f -path "*[Cc]ache/[Cc]ache_[Dd]ata/data_2" 2>/dev/null | head -n 1)
if [ -z "$cachefile" ]; then
cachefile=$(find "$cache_ver_dir" -type f -name "data_2" 2>/dev/null | head -n 1)
fi
fi
if [ -z "$cachefile" ] || [ ! -f "$cachefile" ]; then
echo -e "\033[31m${msg_cache_file_not_found}\033[0m"
exit 1
fi
echo "$msg_extracting"
tmp_file="/tmp/genshin_gacha_data_2"
cp "$cachefile" "$tmp_file"
# Extract URLs containing webview_gacha
links=()
while read -r line; do
if [ -n "$line" ]; then
links+=("$line")
fi
done < <(grep -o -a -E 'https://[a-zA-Z0-9_./?=&%-]+game_biz=[a-zA-Z0-9_]+' "$tmp_file" | grep "webview_gacha" | uniq)
# Clean up temp file immediately after reading
rm -f "$tmp_file"
if [ ${#links[@]} -eq 0 ]; then
echo -e "\033[31m${msg_no_links}\033[0m"
exit 1
fi
# Identify default API host based on region
if [[ "$linux_gamedir" == *"YuanShen_Data"* ]]; then
default_api_host="public-operation-hk4e.mihoyo.com" # China Server
else
default_api_host="public-operation-hk4e-sg.hoyoverse.com" # Global Server
fi
# Accept manual override for api host
api_host=${1:-$default_api_host}
if [ "$api_host" = "china" ]; then
api_host="public-operation-hk4e.mihoyo.com"
elif [ "$api_host" = "global" ]; then
api_host="public-operation-hk4e-sg.hoyoverse.com"
fi
printf "${msg_api_host}\n" "$api_host"
printf "${msg_verifying}\n" "${#links[@]}"
wish_history_url=""
link_found=false
# Validate URLs backwards (from newest to oldest)
for ((i=${#links[@]}-1; i>=0; i--)); do
link="${links[i]}"
query_string=$(echo "$link" | cut -d'?' -f2)
cleaned_query=$(echo "$query_string" | sed -E 's/(&|^)(gacha_type|size|lang|limit)=[^&]*//g' | sed 's/^&//')
api_url="https://${api_host}/gacha_info/api/getGachaLog?${cleaned_query}&gacha_type=301&size=5&lang=zh-cn"
response=$(curl -s --max-time 10 "$api_url")
retcode=""
if command -v jq >/dev/null 2>&1; then
retcode=$(echo "$response" | jq -r '.retcode' 2>/dev/null)
fi
if [ -z "$retcode" ]; then
retcode=$(echo "$response" | grep -o '"retcode":[0-9]*' | head -n 1 | cut -d':' -f2)
fi
if [ "$retcode" = "0" ]; then
wish_history_url="$link"
link_found=true
break
fi
printf "${msg_link_invalid}\n" "$(( ${#links[@]} - i ))"
sleep 0.5
done
if [ "$link_found" = "true" ]; then
echo -e "\n\033[32m${msg_success}\033[0m"
echo "--------------------------------------------------------------------------------"
echo "$wish_history_url"
echo "--------------------------------------------------------------------------------"
copy_to_clipboard "$wish_history_url"
else
echo -e "\n\033[31m${msg_fail}\033[0m"
exit 1
fi
@yougg

yougg commented Jun 6, 2026

Copy link
Copy Markdown
Author

Usage example:

./get_gacha_url.sh

Looking for Genshin Impact log file...
Found log file: ~/.local/share/anime-game-launcher/prefix/drive_c/users/yougg/AppData/LocalLow/miHoYo/Genshin Impact/output_log.txt
Located WINEPREFIX: ~/.local/share/anime-game-launcher/prefix
Parsed game directory (Linux): ~/Games/GenshinImpact/Genshin Impact game/GenshinImpact_Data
Extracting wish history links from cache file...
Using API verification host: public-operation-hk4e-sg.hoyoverse.com
Found 6 cache links, verifying validity backwards...

[Success] Found valid wish history link!
--------------------------------------------------------------------------------
https://public-operation-hk4e-sg.hoyoverse.com/gacha_info/api/getGachaLog?win_mode=fullscreen&no_joypad_close=1&authkey_ver=1&sign_type=2&auth_appid=webview_gacha&init_type=301&gacha_id=...&timestamp=1779234622&lang=zh-cn&device_type=pc&game_version=OSRELWin6.6.0_R45246446_S44916582_D45294055&region=os_asia&authkey=...&game_biz=hk4e_global
--------------------------------------------------------------------------------
Link copied to Wayland clipboard.


运行示例:

LANG=zh_CN.UTF-8 ./get_gacha_url.sh

正在寻找原神日志文件...
找到日志文件: ~/.local/share/anime-game-launcher/prefix/drive_c/users/yougg/AppData/LocalLow/miHoYo/Genshin Impact/output_log.txt
定位 WINEPREFIX: ~/.local/share/anime-game-launcher/prefix
解析游戏目录 (Linux): ~/Games/GenshinImpact/Genshin Impact game/GenshinImpact_Data
正在从缓存文件提取抽卡链接...
使用 API 校验主机名: public-operation-hk4e-sg.hoyoverse.com
共找到 6 个缓存链接,正在反向逐一验证有效性...

[成功] 找到有效的抽卡记录链接!
--------------------------------------------------------------------------------
https://public-operation-hk4e-sg.hoyoverse.com/gacha_info/api/getGachaLog?win_mode=fullscreen&no_joypad_close=1&authkey_ver=1&sign_type=2&auth_appid=webview_gacha&init_type=301&gacha_id=...&timestamp=1779234622&lang=zh-cn&device_type=pc&game_version=OSRELWin6.6.0_R45246446_S44916582_D45294055&region=os_asia&authkey=...&game_biz=hk4e_global
--------------------------------------------------------------------------------
链接已复制到 Wayland 剪贴板。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment