Commit 8ad0c188 authored by 苍蓝's avatar 苍蓝

update

parent d23663b1
import os import os
import sys import sys
import urllib.request import urllib.request
from datetime import datetime, timedelta import urllib.error
from typing import Dict, Optional from typing import Optional, List
from pathlib import Path from pathlib import Path
# 配置 # 配置
...@@ -11,102 +11,203 @@ CONFIG = { ...@@ -11,102 +11,203 @@ CONFIG = {
'formal_file': "to-formal.txt", 'formal_file': "to-formal.txt",
'pre_file': "to-pre.txt", 'pre_file': "to-pre.txt",
'url_formal': "https://code.moenext.com/coccvo/card-password-conversion/-/raw/master/to-formal.txt", 'url_formal': "https://code.moenext.com/coccvo/card-password-conversion/-/raw/master/to-formal.txt",
'url_pre': "https://code.moenext.com/coccvo/card-password-conversion/-/raw/master/to-pre.txt" 'url_pre': "https://code.moenext.com/coccvo/card-password-conversion/-/raw/master/to-pre.txt",
'max_retries': 3
} }
def check_and_update_file(filename: str, url: str) -> bool: def get_etag_path(filename: str) -> str:
"""检查并更新文件,如果成功返回True,失败返回False""" """获取存储 ETag 的隐藏文件路径"""
return f"{filename}.etag"
def load_local_etag(filename: str) -> Optional[str]:
"""从本地加载 ETag"""
etag_file = get_etag_path(filename)
if os.path.exists(etag_file):
try: try:
if not os.path.exists(filename): with open(etag_file, 'r', encoding='utf-8') as f:
print(f"{filename} 不存在,开始下载...") return f.read().strip()
return download_file(url, filename) except Exception:
pass
file_mod_time = datetime.fromtimestamp(os.path.getmtime(filename)) return None
current_time = datetime.now()
if (current_time - file_mod_time) > timedelta(days=1): def save_local_etag(filename: str, etag: str):
print(f"更新 {filename}...") """保存 ETag 到本地"""
return download_file(url, filename) etag_file = get_etag_path(filename)
return True try:
with open(etag_file, 'w', encoding='utf-8') as f:
f.write(etag)
except Exception as e: except Exception as e:
print(f"Error: 检查文件更新失败 {e}") print(f"警告:无法保存 ETag 文件 {e}")
return False
def check_and_update_file(filename: str, url: str) -> bool:
"""
检查并更新文件 (使用 ETag 机制,无随机延迟)
"""
local_etag = load_local_etag(filename)
for attempt in range(CONFIG['max_retries']):
try:
req = urllib.request.Request(url)
req.add_header('User-Agent', 'YDK-Update-Tool/1.0')
# 核心:如果有本地 ETag,添加到请求头
if local_etag:
req.add_header('If-None-Match', local_etag)
def download_file(url: str, filename: str) -> bool:
try: try:
urllib.request.urlretrieve(url, filename) with urllib.request.urlopen(req, timeout=10) as response:
# 状态码 200: 文件有更新或首次下载
content = response.read()
new_etag = response.headers.get('ETag', '').strip()
# 保存文件内容
with open(filename, 'wb') as f:
f.write(content)
# 保存新的 ETag
if new_etag:
save_local_etag(filename, new_etag)
print(f"[下载] {filename} 已更新。")
return True
except urllib.error.HTTPError as e:
# 处理 304 Not Modified (文件未变化)
if e.code == 304:
return True return True
else:
# 其他 HTTP 错误
raise e
except Exception as e: except Exception as e:
print(f"Error: 下载对照表失败 {e}") wait_time = (attempt + 1) * 2
print(f"[警告] 下载 {filename} 失败 (尝试 {attempt+1}/{CONFIG['max_retries']}): {e}")
if attempt < CONFIG['max_retries'] - 1:
print(f" 等待 {wait_time} 秒后重试...")
import time
time.sleep(wait_time)
else:
if os.path.exists(filename):
print(f"[提示] 无法连接服务器,继续使用本地旧版 {filename}。")
return True
else:
print(f"[错误] 无法下载初始文件 {filename}。")
return False return False
return False
def load_replacements(formal_file: str, pre_file: str) -> Optional[Dict[str, str]]: def load_replacements(formal_file: str, pre_file: str) -> Optional[dict]:
try:
replacements = {} replacements = {}
# 读取正式对照表 try:
with open(formal_file, 'r', encoding='utf-8') as file: if not os.path.exists(formal_file):
for line in file: return None
with open(formal_file, 'r', encoding='utf-8') as f:
for line in f:
parts = line.strip().split('\t') parts = line.strip().split('\t')
if len(parts) == 2: if len(parts) == 2:
replacements[parts[0]] = parts[1] replacements[parts[0]] = parts[1]
# 读取预发布对照表 if os.path.exists(pre_file):
with open(pre_file, 'r', encoding='utf-8') as file: with open(pre_file, 'r', encoding='utf-8') as f:
for line in file: for line in f:
parts = line.strip().split('\t') parts = line.strip().split('\t')
if len(parts) == 2: if len(parts) == 2:
replacements[parts[1]] = parts[0] replacements[parts[1]] = parts[0]
return replacements return replacements
except Exception as e: except Exception as e:
print(f"Error: 读取对照表失败 {e}") print(f"[错误] 读取对照表出错:{e}")
return None return None
def main() -> int: def main():
# 检查目录 # 1. 检查目录
if not os.path.exists(CONFIG['folder_path']): if not os.path.exists(CONFIG['folder_path']):
print("Error: 请把此文件放到游戏目录中再运行") print(f"[错误] 未找到 '{CONFIG['folder_path']}' 文件夹。")
print(" 请将此脚本放在游戏目录下运行。")
input("\n按回车键退出...")
return 1 return 1
# 更新对照表 # 2. 更新对照表
for file_key, url_key in [('formal_file', 'url_formal'), ('pre_file', 'url_pre')]: files_config = [
if not check_and_update_file(CONFIG[file_key], CONFIG[url_key]): ('formal_file', 'url_formal'),
('pre_file', 'url_pre')
]
critical_failure = False
for file_key, url_key in files_config:
f_name = CONFIG[file_key]
u_url = CONFIG[url_key]
if not os.path.exists(f_name):
if not check_and_update_file(f_name, u_url):
critical_failure = True
else:
check_and_update_file(f_name, u_url)
if critical_failure:
print("\n[错误] 缺少必要的对照表文件且无法下载。")
input("\n按回车键退出...")
return 1 return 1
# 加载替换规则 # 3. 加载替换规则
replacements = load_replacements(CONFIG['formal_file'], CONFIG['pre_file']) replacements = load_replacements(CONFIG['formal_file'], CONFIG['pre_file'])
if not replacements:
return 1
# 处理卡组文件 if not replacements:
print("[警告] 替换规则为空,跳过卡组处理。")
else:
deck_path = Path(CONFIG['folder_path']) deck_path = Path(CONFIG['folder_path'])
for ydk_file in deck_path.rglob('*.ydk'): ydk_files = list(deck_path.rglob('*.ydk'))
if not ydk_files:
print(f"[提示] 在 '{CONFIG['folder_path']}' 目录下未找到 .ydk 文件。")
else:
updated_files: List[str] = []
for ydk in ydk_files:
try: try:
content = ydk_file.read_text(encoding='utf-8') content = ydk.read_text(encoding='utf-8')
new_content = content new_content = content
# 执行替换
for old, new in replacements.items(): for old, new in replacements.items():
if old in new_content:
new_content = new_content.replace(old, new) new_content = new_content.replace(old, new)
# 如果内容发生变化,写入文件并记录
if new_content != content: if new_content != content:
ydk_file.write_text(new_content, encoding='utf-8') ydk.write_text(new_content, encoding='utf-8')
print(f"更新卡组: {ydk_file}") try:
else: relative_path = ydk.relative_to(deck_path)
print(f"卡组无需修改: {ydk_file}") updated_files.append(str(relative_path))
except ValueError:
updated_files.append(str(ydk.name))
except Exception as e: except Exception as e:
print(f"处理文件 {ydk_file} 时出错: {e}") print(f"[错误] 处理文件 {ydk.name} 时异常:{e}")
continue
# 4. 显示修改列表
print("\n" + "="*30)
if updated_files:
print(f"【更新完成】共修改 {len(updated_files)} 个卡组文件:")
for fname in updated_files:
print(f" - {fname}")
else:
print("【更新完成】所有卡组文件均为最新,无需修改。")
print("="*30)
print("卡组更新完成。") print("\n按回车键退出...")
input()
return 0 return 0
if __name__ == "__main__": if __name__ == "__main__":
try: try:
exit_code = main() exit_code = main()
print("\n按回车键退出...") sys.exit(exit_code)
input() except KeyboardInterrupt:
sys.exit(exit_code) # 使用 sys.exit() 替代 exit() print("\n[中断] 用户取消操作。")
sys.exit(130)
except Exception as e: except Exception as e:
print(f"Error: {e}") print(f"\n[严重错误] {e}")
print("\n按回车键退出...") input("\n按回车键退出...")
input()
sys.exit(1) sys.exit(1)
#过期先行卡转换为正式卡 #过期先行卡转换为正式卡
#项目地址:https://code.moenext.com/coccvo/card-password-conversion
100200277 40725446 100200277 40725446
100238201 32872239 100238201 32872239
100243001 20240828 100243001 20240828
......
#服务器未更新正式卡转换为先行卡 #服务器未更新正式卡转换为先行卡
#项目地址:https://code.moenext.com/coccvo/card-password-conversion
100256001 88570003 100256001 88570003
100256002 14965712 100256002 14965712
100256003 41350417 100256003 41350417
...@@ -17,8 +18,6 @@ ...@@ -17,8 +18,6 @@
100256016 74665150 100256016 74665150
100256017 64865 100256017 64865
100256018 37458564 100256018 37458564
100256019
100256020
100256021 32236916 100256021 32236916
100256022 68721020 100256022 68721020
100256023 5125629 100256023 5125629
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment