Compare commits

..

10 Commits
1.0.8 ... 1.1.4

Author SHA1 Message Date
ikun
3ba8b67f0e fix: bug 2024-08-27 09:48:20 +08:00
ikun
73c92e550e feat: 加入搜索游戏名入库功能
Co-Authored-By: Tibbar <49330075+tibbar213@users.noreply.github.com>
2024-08-26 19:06:05 +08:00
ikun
a2fa038324 feat&&fix: bug!bug!bug! 2024-08-22 22:40:50 +08:00
ikun
ea3aedbab3 fix: 史 2024-08-22 21:48:04 +08:00
ikun
51ccc579f4 feat:配置文件修改 2024-08-22 14:10:30 +08:00
ikun
17e654a68e feat:没什么用懒得发Release 2024-08-21 17:27:50 +08:00
ikun
c4f4fb9e92 feat: 增加一个清单库 2024-08-16 15:11:31 +08:00
ikun
e38af1675c fix: 修改QQ群链接 2024-08-15 19:53:41 +08:00
ikun
50194cf7de 更新 main.py 2024-08-14 19:00:27 +08:00
ikun
7063f2f5dc rm: 移除jsdelivr的CDN服务 2024-08-13 10:25:28 +08:00
2 changed files with 170 additions and 116 deletions

1
.gitignore vendored
View File

@@ -168,3 +168,4 @@ cython_debug/
*.bat
*.xml
*.exe
*.dll

285
main.py
View File

@@ -1,14 +1,12 @@
import os
import vdf
import winreg
import argparse
import aiohttp
import aiofiles
import traceback
import subprocess
import colorlog
import logging
import json
import ujson as json
import time
import sys
import psutil
@@ -38,9 +36,16 @@ def init_log():
# 生成配置文件
def gen_config_file():
default_config = {"Github_Persoal_Token": "", "Custom_Steam_Path": ""}
with open('./config.json', 'w', encoding='utf-8') as f:
json.dump(default_config, f)
default_config ={
"Github_Personal_Token": "",
"Custom_Steam_Path": "",
"QA1": "温馨提示Github_Personal_Token可在Github设置的最底下开发者选项找到详情看教程",
"教程": "https://lyvx-my.sharepoint.com/:w:/g/personal/ikun_ikunshare_com/EWqIqyCElLNLo_CKfLbqix0BWU_O03HLzEHQKHdJYrUz-Q?e=79MZjw"
}
with open("./config.json", "w", encoding="utf-8") as f:
f.write(json.dumps(default_config, indent=2, ensure_ascii=False,
escape_forward_slashes=False))
f.close()
log.info(' 🖱️ 程序可能为第一次启动,请填写配置文件后重新启动程序')
@@ -51,8 +56,8 @@ def load_config():
os.system('pause')
sys.exit()
else:
with open('./config.json', 'r', encoding='utf-8') as f:
config = json.load(f)
with open("./config.json", "r", encoding="utf-8") as f:
config = json.loads(f.read())
return config
@@ -69,19 +74,17 @@ print('\033[1;32;40m | |_| | | | \\ | | |___ | | \\ \\ | |___ / /' + '\033
print('\033[1;32;40m \\_____/ |_| \\_| |_____| |_| \\_\\ |_____| /_/' + '\033[0m')
log.info('作者ikun0014')
log.info('本项目基于wxy1343/ManifestAutoUpdate进行修改采用GPL V3许可证')
log.info('版本1.0.8')
log.info('版本1.1.4')
log.info('项目仓库https://github.com/ikunshare/Onekey')
log.debug('官网ikunshare.com')
log.warning('倒卖本工具的臭傻逼https://space.bilibili.com/3546655638948756好嚣张哦')
log.warning('注意据传Steam新版本对部分解锁工具进行了检测但目前未发现问题如果你被封号可以issue反馈')
log.warning('本项目完全免费如果你在淘宝QQ群内通过购买方式获得赶紧回去骂商家死全家\n交流群组:\n点击链接加入群聊【ikun分享】https://qm.qq.com/q/D9Uiva3RVS\nhttps://t.me/ikunshare_group')
log.warning('本项目完全开源免费如果你在淘宝QQ群内通过购买方式获得赶紧回去骂商家死全家\n交流群组:\n点击链接加入群聊【𝗶𝗸𝘂𝗻分享】https://qm.qq.com/q/d7sWovfAGI\nhttps://t.me/ikunshare_group')
# 通过注册表获取Steam安装路径
def get_steam_path():
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, r'Software\Valve\Steam')
steam_path = Path(winreg.QueryValueEx(key, 'SteamPath')[0])
custom_steam_path = config.get("Custom_Steam_Path", "")
custom_steam_path = config["Custom_Steam_Path"]
if not custom_steam_path == '':
return Path(custom_steam_path)
else:
@@ -99,64 +102,91 @@ def stack_error(exception):
return ''.join(stack_trace)
# 从Steam API直接搜索游戏信息
async def search_game_info(search_term):
async with ClientSession() as session:
url = f'https://steamui.com/loadGames.php?search={search_term}'
async with session.get(url) as r:
if r.status == 200:
data = await r.json()
games = data.get('games', [])
return games
else:
log.error("⚠ 获取游戏信息失败")
return []
# 通过游戏名查找appid
async def find_appid_by_name(game_name):
games = await search_game_info(game_name)
if games:
log.info("🔍 找到以下匹配的游戏:")
for idx, game in enumerate(games, 1):
gamename = game['schinese_name'] if game['schinese_name'] else game['name']
log.info(f"{idx}. {gamename} (AppID: {game['appid']})")
while True:
choice = input("请选择游戏编号:")
if choice.isdigit() and 1 <= int(choice) <= len(games):
selected_game = games[int(choice) - 1]
log.info(f"✅ 选择的游戏: {selected_game['schinese_name']} (AppID: {selected_game['appid']})")
return selected_game['appid'], selected_game['schinese_name']
else:
log.error(f"⚠ 错误的编号:{choice},请重新输入。")
return None, None
# 下载清单
async def get(sha, path):
async def get(sha, path, repo, session):
url_list = [
f'https://gcore.jsdelivr.net/gh/{repo}@{sha}/{path}',
f'https://fastly.jsdelivr.net/gh/{repo}@{sha}/{path}',
f'https://cdn.jsdelivr.net/gh/{repo}@{sha}/{path}',
f'https://raw.dgithub.xyz/{repo}/{sha}/{path}',
f'https://gh.api.99988866.xyz/https://raw.githubusercontent.com/{repo}/{sha}/{path}',
# f'https://gh.api.99988866.xyz/https://raw.githubusercontent.com/{repo}/{sha}/{path}',
f'https://cdn.jsdmirror.com/gh/{repo}@{sha}/{path}',
f'https://jsd.onmicrosoft.cn/gh/{repo}@{sha}/{path}',
f'https://mirror.ghproxy.com/https://raw.githubusercontent.com/{repo}/{sha}/{path}',
f'https://raw.githubusercontent.com/{repo}/{sha}/{path}',
f'https://gh.jiasu.in/https://github.com/{repo}/{sha}/{path}'
f'https://gh.jiasu.in/https://raw.githubusercontent.com/{repo}/{sha}/{path}'
]
retry = 3
async with ClientSession() as session:
while retry:
for url in url_list:
try:
async with session.get(url, ssl=False) as r:
if r.status == 200:
return await r.read()
else:
log.error(f' 🔄 获取失败: {path} - 状态码: {r.status}')
except ClientError():
log.error(f' 🔄 获取失败: {path} - 连接错误')
retry -= 1
log.warning(f' 🔄 重试剩余次数: {retry} - {path}')
log.error(f' 🔄 超过最大重试次数: {path}')
raise Exception(f' 🔄 无法下载: {path}')
while retry:
for url in url_list:
try:
async with session.get(url, ssl=False) as r:
if r.status == 200:
return await r.read()
else:
log.error(f' 🔄 获取失败: {path} - 状态码: {r.status}')
except ClientError:
log.error(f' 🔄 获取失败: {path} - 连接错误')
retry -= 1
log.warning(f' 🔄 重试剩余次数: {retry} - {path}')
log.error(f' 🔄 超过最大重试次数: {path}')
raise Exception(f' 🔄 无法下载: {path}')
# 获取清单信息
async def get_manifest(sha, path, steam_path: Path):
async def get_manifest(sha, path, steam_path: Path, repo, session):
collected_depots = []
try:
if path.endswith('.manifest'):
depot_cache_path = steam_path / 'depotcache'
async with lock:
if not depot_cache_path.exists():
depot_cache_path.mkdir(exist_ok=True)
if not depot_cache_path.exists():
depot_cache_path.mkdir(exist_ok=True)
save_path = depot_cache_path / path
if save_path.exists():
async with lock:
log.warning(f'👋已存在清单: {path}')
log.warning(f'👋已存在清单: {path}')
return collected_depots
content = await get(sha, path)
async with lock:
log.info(f' 🔄 清单下载成功: {path}')
content = await get(sha, path, repo, session)
log.info(f' 🔄 清单下载成功: {path}')
async with aiofiles.open(save_path, 'wb') as f:
await f.write(content)
elif path == 'Key.vdf':
content = await get(sha, path)
async with lock:
log.info(f' 🔄 密钥下载成功: {path}')
content = await get(sha, path, repo, session)
log.info(f' 🔄 密钥下载成功: {path}')
depots_config = vdf.loads(content.decode(encoding='utf-8'))
for depot_id, depot_info in depots_config['depots'].items():
collected_depots.append((depot_id, depot_info['DecryptionKey']))
except KeyboardInterrupt:
raise
except Exception as e:
log.error(f'处理失败: {path} - {stack_error(e)}')
traceback.print_exc()
@@ -203,7 +233,7 @@ async def stool_add(depot_data, app_id):
# 增加GreenLuma解锁相关文件
async def greenluma_add(depot_id_list):
app_list_path = steam_path / 'appcache' / 'appinfo.vdf'
app_list_path = steam_path / 'AppList'
if app_list_path.exists() and app_list_path.is_file():
app_list_path.unlink(missing_ok=True)
if not app_list_path.is_dir():
@@ -230,86 +260,109 @@ async def greenluma_add(depot_id_list):
return True
# 检查进程是否运行
def check_process_running(process_name):
for process in psutil.process_iter(['name']):
if process.info['name'] == process_name:
return True
return False
async def check_github_api_rate_limit(headers, session):
url = 'https://api.github.com/rate_limit'
async with session.get(url, headers=headers, ssl=False) as r:
if not r == None:
r_json = await r.json()
else:
log.error('孩子,你怎么做到的?')
os.system('pause')
if r.status == 200:
rate_limit = r_json['rate']
remaining_requests = rate_limit['remaining']
reset_time = rate_limit['reset']
reset_time_formatted = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(reset_time))
log.info(f' 🔄 剩余请求次数: {remaining_requests}')
else:
log.error('Github请求数检查失败')
if remaining_requests == 0:
log.warning(f' ⚠ GitHub API 请求数已用尽,将在 {reset_time_formatted} 重置, 不想等生成一个填配置文件里')
# 主函数
async def main(app_id):
if not app_id == None:
app_id_list = list(filter(str.isdecimal, app_id.strip().split('-')))
app_id = app_id_list[0]
github_token = config.get("Github_Persoal_Token", "")
headers = {'Authorization': f'Bearer {github_token}'} if github_token else None
url = 'https://api.github.com/rate_limit'
async def main(app_id, game_name):
app_id_list = list(filter(str.isdecimal, app_id.strip().split('-')))
app_id = app_id_list[0]
async with ClientSession() as session:
async with session.get(url, headers=headers, ssl=False) as r:
r_json = await r.json()
remain_limit = r_json['rate']['remaining']
use_limit = r_json['rate']['used']
reset_time = r_json['rate']['reset']
f_reset_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(reset_time))
log.info(f' 🔄 已用Github请求数{use_limit}')
log.info(f' 🔄 剩余Github请求数{remain_limit}')
if r.status == 403:
log.info(f' 🔄 你的Github Api请求数已超限请尝试增加Persoal Token')
log.info(f' 🔄 请求数重置时间:{f_reset_time}')
github_token = config["Github_Personal_Token"]
headers = {'Authorization': f'Bearer {github_token}'} if github_token else None
latest_date = None
selected_repo = None
url = f'https://api.github.com/repos/{repo}/branches/{app_id}'
# 检查Github API限额
await check_github_api_rate_limit(headers, session)
async with ClientSession() as session:
async with session.get(url, headers=headers, ssl=False) as r:
if r.status == 403:
log.info(f' 🔄 你的Github Api请求数已超限请尝试增加Persoal Token')
log.info(f' 🔄 请求数重置时间:{reset_time}')
r_json = await r.json()
if 'commit' in r_json:
sha = r_json['commit']['sha']
url = r_json['commit']['commit']['tree']['url']
date = r_json['commit']['commit']['author']['date']
async with session.get(url, headers=headers, ssl=False) as r2:
r2_json = await r2.json()
if 'tree' in r2_json:
collected_depots = []
for i in r2_json['tree']:
result = await get_manifest(sha, i['path'], steam_path)
collected_depots.extend(result)
if collected_depots:
if isSteamTools:
await stool_add(collected_depots, app_id)
log.info(' ✅ 找到SteamTools已添加解锁文件')
if isGreenLuma:
await greenluma_add([app_id])
depot_config = {'depots': {depot_id: {'DecryptionKey': depot_key} for depot_id, depot_key in collected_depots}}
depotkey_merge(steam_path / 'config' / 'config.vdf', depot_config)
if await greenluma_add([int(i) for i in depot_config['depots'] if i.isdecimal()]):
log.info(' ✅ 找到GreenLuma已添加解锁文件')
log.info(f' ✅ 清单最后更新时间:{date}')
log.info(f' ✅ 入库成功: {app_id}')
return True
log.error(f' ⚠ 清单下载或生成.st失败: {app_id}')
return False
for repo in repos:
url = f'https://api.github.com/repos/{repo}/branches/{app_id}'
try:
async with session.get(url, headers=headers, ssl=False) as r:
r_json = await r.json()
if 'commit' in r_json:
date = r_json['commit']['commit']['author']['date']
if latest_date is None or date > latest_date:
latest_date = date
selected_repo = repo
except Exception as e:
log.error(f' ⚠ 获取分支信息失败: {stack_error(e)}')
traceback.print_exc()
if selected_repo:
log.info(f' 🔄 选择清单仓库:{selected_repo}')
url = f'https://api.github.com/repos/{selected_repo}/branches/{app_id}'
async with session.get(url, headers=headers, ssl=False) as r:
r_json = await r.json()
if 'commit' in r_json:
sha = r_json['commit']['sha']
url = r_json['commit']['commit']['tree']['url']
async with session.get(url, headers=headers, ssl=False) as r2:
r2_json = await r2.json()
if 'tree' in r2_json:
collected_depots = []
for i in r2_json['tree']:
result = await get_manifest(sha, i['path'], steam_path, selected_repo, session)
collected_depots.extend(result)
if collected_depots:
if isSteamTools:
await stool_add(collected_depots, app_id)
log.info(' ✅ 找到SteamTools已添加解锁文件')
if isGreenLuma:
await greenluma_add([app_id])
depot_config = {'depots': {depot_id: {'DecryptionKey': depot_key} for depot_id, depot_key in collected_depots}}
await depotkey_merge(steam_path / 'config' / 'config.vdf', depot_config)
if await greenluma_add([int(i) for i in depot_config['depots'] if i.isdecimal()]):
log.info(' ✅ 找到GreenLuma已添加解锁文件')
log.info(f' ✅ 清单最后更新时间:{date}')
log.info(f' ✅ 入库成功: {app_id}{game_name}')
os.system('pause')
return True
log.error(f' ⚠ 清单下载或生成失败: {app_id}{game_name}')
os.system('pause')
return False
parser = argparse.ArgumentParser()
parser.add_argument('-a', '--app-id')
args = parser.parse_args()
repo = 'ManifestHub/ManifestHub'
repos = [
'ManifestHub/ManifestHub',
'ikun0014/ManifestHub',
'Auiowu/ManifestAutoUpdate',
'tymolu233/ManifestAutoUpdate'
]
if __name__ == '__main__':
try:
log.debug('App ID可以在SteamDB或Steam商店链接页面查看')
asyncio.run(main(args.app_id or input('需要入库的App ID: ')))
user_input = input("请输入游戏AppID或名称:").strip()
appid, game_name = asyncio.run(find_appid_by_name(user_input))
if not appid:
log.error(' ⚠ 未找到匹配的游戏,请尝试其他名称。')
else:
asyncio.run(main(appid, game_name))
except KeyboardInterrupt:
exit()
except Exception as e:
log.error(f' ⚠ 发生错误: {stack_error(e)}')
traceback.print_exc()
if not args.app_id:
if not user_input:
os.system('pause')