[U] add ru support.

This commit is contained in:
2026-04-15 14:12:01 +08:00
parent 1bc74cbd1b
commit 0da69910cf
14 changed files with 263 additions and 39 deletions

View File

@@ -6,6 +6,7 @@
},
"cdn": {
"base_url": "https://128-ft-cdn-aws-tx.arksgame.com/ab",
"ru_base_url": "https://128-ft-cdn-ali-tx.arksgame.com/ru",
"timeout": 10,
"retry_count": 3,
"cloudflare": {

116
decryptConfig.py Normal file
View File

@@ -0,0 +1,116 @@
#!/usr/bin/env python3
"""
Config Decryptor Tool
读取加密后的配置文件Base64 + DES-CBC还原为原始 JSON 配置。
"""
import json
import os
import sys
from pathlib import Path
from rich.console import Console
from rich.panel import Panel
from rich.syntax import Syntax
# Add the src directory to the path so we can import the crypto module
sys.path.insert(0, str(Path(__file__).parent / "src"))
from config_man.utils.crypto import decrypt
# Initialize rich console
console = Console()
def load_encrypted_file(file_path):
"""Load encrypted file content as a single string (strip whitespace)."""
try:
with open(file_path, "r", encoding="utf-8") as f:
content = f.read().strip()
if not content:
console.print(f"[red]错误: 文件 {file_path} 为空[/red]")
return None
return content
except FileNotFoundError:
console.print(f"[red]错误: 文件 {file_path} 不存在[/red]")
return None
except Exception as e:
console.print(f"[red]错误: 无法读取文件 {file_path}: {str(e)}[/red]")
return None
def decrypt_to_config(encrypted_content):
"""Decrypt and parse JSON into a Python object."""
try:
plain = decrypt(encrypted_content)
return json.loads(plain)
except json.JSONDecodeError as e:
console.print(f"[red]错误: 解密后内容不是有效 JSON: {str(e)}[/red]")
return None
except Exception as e:
console.print(f"[red]错误: 解密失败: {str(e)}[/red]")
return None
def save_raw_config(config_data, file_path):
"""Save decrypted config as formatted JSON."""
try:
with open(file_path, "w", encoding="utf-8") as f:
json.dump(config_data, f, ensure_ascii=False, indent=2)
console.print(f"[green]✅ 解密后的配置文件已保存到: {file_path}[/green]")
return True
except Exception as e:
console.print(f"[red]错误: 无法保存文件 {file_path}: {str(e)}[/red]")
return False
def main():
console.print(Panel("🔓 Config Decryptor Tool", expand=False, border_style="bold blue"))
while True:
input_file = input("请输入要解密的配置文件路径 (如: encrypted_config.json): ").strip()
if input_file and os.path.exists(input_file):
break
console.print("[red]❌ 文件不存在,请重新输入[/red]")
console.print(f"[blue]正在读取加密文件: {input_file}[/blue]")
encrypted_blob = load_encrypted_file(input_file)
if encrypted_blob is None:
return 1
console.print("[blue]正在解密…[/blue]")
config_data = decrypt_to_config(encrypted_blob)
if config_data is None:
return 1
console.print("[green]✅ 解密成功[/green]")
console.print(Panel("解密后的配置内容", title="📄 原始配置", border_style="cyan"))
console.print(Syntax(json.dumps(config_data, ensure_ascii=False, indent=2), "json", theme="monokai"))
base = os.path.basename(input_file)
if base.startswith("encrypted_"):
default_output = "decrypted_" + base[len("encrypted_"):]
else:
default_output = "decrypted_" + base
output_file = input(f"\n请输入输出文件路径 (默认: {default_output}): ").strip()
if not output_file:
output_file = default_output
console.print(f"[blue]正在保存: {output_file}[/blue]")
if save_raw_config(config_data, output_file):
console.print(Panel("🎉 配置文件解密完成!", expand=False, border_style="bold green"))
return 0
return 1
if __name__ == "__main__":
try:
sys.exit(main())
except KeyboardInterrupt:
print("\n\n👋 已取消操作")
sys.exit(0)
except Exception as e:
print(f"\n❌ 程序执行出错: {str(e)}")
sys.exit(1)

View File

@@ -24,9 +24,10 @@ def main():
# 测试不同版本的CDN URL构建
versions = ["0.29", "0.30"]
platforms = ["android", "ios"]
platforms = ["android", "ios", "ru"]
print(f"CDN基础URL: {config_manager.cdn_base_url}")
print(f"CDN基础URL(默认): {config_manager.cdn_config.get('base_url', '')}")
print(f"CDN基础URL(ru): {config_manager.cdn_config.get('ru_base_url', '')}")
print(f"存储路径: {config_manager.storage_path}")
print()
@@ -41,7 +42,8 @@ def main():
# 显示当前配置
print(f"当前CDN配置:")
print(f"CDN基础URL: {config_manager.cdn_base_url}")
print(f"CDN基础URL(默认): {config_manager.cdn_config.get('base_url', '')}")
print(f"CDN基础URL(ru): {config_manager.cdn_config.get('ru_base_url', '')}")
print(f"CDN超时: {config_manager.cdn_config.get('timeout', 10)}")
print(f"CDN重试次数: {config_manager.cdn_config.get('retry_count', 3)}")

View File

@@ -24,7 +24,7 @@ def main():
# 测试不同版本的路径构建
versions = ["0.29", "0.30"]
platforms = ["android", "ios"]
platforms = ["android", "ios", "ru"]
print(f"存储配置:")
print(f" rclone_remote: {config_manager.storage_config.get('rclone_remote', 'remote')}")

View File

@@ -24,7 +24,7 @@ def main():
# 测试不同版本的路径构建
versions = ["0.29", "0.30"]
platforms = ["android", "ios"]
platforms = ["android", "ios", "ru"]
for version in versions:
for platform in platforms:

View File

@@ -27,7 +27,7 @@ def cli():
def view(version, json_format, storage_path, cdn_url, mock):
"""查看配置文件
对比显示对象存储和CDN中的配置内容支持AndroidiOS平台。
对比显示对象存储和CDN中的配置内容支持 AndroidiOS 和 RU 平台。
示例:
config-man view --version 0.30
@@ -59,7 +59,7 @@ def view(version, json_format, storage_path, cdn_url, mock):
@cli.command()
@click.option('--platform', required=True, type=click.Choice(['android', 'ios']),
@click.option('--platform', required=True, type=click.Choice(['android', 'ios', 'ru']),
help='平台类型')
@click.option('--target-version', required=True, help='目标版本号')
@click.option('--update-version', required=True, help='更新版本号')
@@ -74,6 +74,7 @@ def suggest_update(platform, target_version, update_version, storage_path, cdn_u
示例:
config-man suggest-update --platform android --target-version 0.29 --update-version 0.30
config-man suggest-update --platform ios --target-version 0.29 --update-version 0.30 --mock
config-man suggest-update --platform ru --target-version 0.29 --update-version 0.30 --mock
"""
try:
# 如果使用模拟环境启用rclone模拟
@@ -100,7 +101,7 @@ def suggest_update(platform, target_version, update_version, storage_path, cdn_u
@cli.command()
@click.option('--platform', required=True, type=click.Choice(['android', 'ios']),
@click.option('--platform', required=True, type=click.Choice(['android', 'ios', 'ru']),
help='平台类型')
@click.option('--target-version', required=True, help='目标版本号')
@click.option('--update-version', required=True, help='更新版本号')
@@ -115,6 +116,7 @@ def force_update(platform, target_version, update_version, storage_path, cdn_url
示例:
config-man force-update --platform ios --target-version 0.29 --update-version 0.30
config-man force-update --platform android --target-version 0.29 --update-version 0.30 --mock
config-man force-update --platform ru --target-version 0.29 --update-version 0.30 --mock
"""
try:
# 如果使用模拟环境启用rclone模拟

View File

@@ -13,10 +13,33 @@ class ConfigManager:
def __init__(self, storage_path: str = None, cdn_base_url: str = None):
# 使用配置文件中的值如果参数为None
self.storage_path = storage_path or config.get_storage_path()
self.cdn_base_url = cdn_base_url or config.get_cdn_base_url()
self.cdn_url_override = cdn_base_url
self.storage_config = config.get_storage_config()
self.cdn_config = config.get_cdn_config()
def _storage_prefix_for_platform(self, platform: str) -> str:
"""根据平台获取对象存储路径前缀"""
if platform == "ru":
return "ru"
return self.storage_path
def _config_filename_for_platform(self, platform: str) -> str:
"""根据平台获取配置文件名"""
if platform == "ru":
return "androidconfig.json"
return f"{platform}config.json"
def _cdn_base_for_platform(self, platform: str) -> str:
"""根据平台获取CDN基础URL"""
if self.cdn_url_override is not None:
return self.cdn_url_override
if platform == "ru":
return self.cdn_config.get(
"ru_base_url", "https://128-ft-cdn-ali-tx.arksgame.com/ru"
)
return config.get_cdn_base_url()
def version_to_path(self, version: str) -> str:
"""将版本号转换为存储路径格式"""
# 0.29 -> 0_29
@@ -25,23 +48,26 @@ class ConfigManager:
def get_config_path(self, version: str, platform: str) -> str:
"""获取配置文件在对象存储中的路径"""
version_path = self.version_to_path(version)
filename = f"{platform}config.json"
return f"{self.storage_path}/{version_path}/{filename}"
filename = self._config_filename_for_platform(platform)
prefix = self._storage_prefix_for_platform(platform)
return f"{prefix}/{version_path}/{filename}"
def get_rclone_path(self, version: str, platform: str) -> str:
"""获取rclone完整路径包含远程名称"""
version_path = self.version_to_path(version)
filename = f"{platform}config.json"
filename = self._config_filename_for_platform(platform)
prefix = self._storage_prefix_for_platform(platform)
rclone_remote = self.storage_config.get('rclone_remote', 'remote')
return f"{rclone_remote}:{self.storage_path}/{version_path}/{filename}"
return f"{rclone_remote}:{prefix}/{version_path}/{filename}"
def get_cdn_url(self, version: str, platform: str) -> str:
"""获取配置文件在CDN中的URL"""
if not self.cdn_base_url:
cdn_base_url = self._cdn_base_for_platform(platform)
if not cdn_base_url:
return ""
version_path = self.version_to_path(version)
filename = f"{platform}config.json"
return f"{self.cdn_base_url}/{version_path}/{filename}"
filename = self._config_filename_for_platform(platform)
return f"{cdn_base_url}/{version_path}/{filename}"
def download_from_storage(self, version: str, platform: str) -> Optional[str]:
"""从对象存储下载并解密配置文件"""

View File

@@ -14,13 +14,22 @@ class DisplayManager:
self.console = Console()
self.display_config = config.get_display_config()
def _platform_label(self, platform: str) -> str:
"""平台显示名称"""
mapping = {
"android": "Android",
"ios": "iOS",
"ru": "RU",
}
return mapping.get(platform, platform)
def display_config_comparison(self, version: str, platform: str,
storage_config: Dict, cdn_config: Dict,
differences: Dict):
"""显示配置对比结果"""
# 创建标题
title = f"版本: {version} | 平台: {platform.title()}"
title = f"版本: {version} | 平台: {self._platform_label(platform)}"
self.console.print(f"\n[bold blue]{title}[/bold blue]")
# 创建表格
@@ -51,7 +60,7 @@ class DisplayManager:
storage_config: Dict, cdn_config: Dict):
"""以JSON格式显示配置"""
title = f"版本: {version} | 平台: {platform.title()}"
title = f"版本: {version} | 平台: {self._platform_label(platform)}"
self.console.print(f"\n[bold blue]{title}[/bold blue]")
# 创建对比JSON
@@ -79,7 +88,7 @@ class DisplayManager:
self.console.print(f"\n[bold green]配置查看总结[/bold green]")
self.console.print(f"版本: {version}")
for platform in ["android", "ios"]:
for platform in ["android", "ios", "ru"]:
if platform in results:
result = results[platform]
if result["success"]:
@@ -91,13 +100,13 @@ class DisplayManager:
status = "✅ 正常" if differences == 0 else f"⚠️ {differences} 项不同"
self.console.print(
f" {platform.title()}: {status} "
f" {self._platform_label(platform)}: {status} "
f"(存储: {storage_keys}项, CDN: {cdn_keys}项)"
)
else:
self.console.print(f" {platform.title()}: ❌ 失败")
self.console.print(f" {self._platform_label(platform)}: ❌ 失败")
else:
self.console.print(f" {platform.title()}: ⚠️ 未检查")
self.console.print(f" {self._platform_label(platform)}: ⚠️ 未检查")
def display_error(self, message: str):
"""显示错误信息"""

View File

@@ -72,8 +72,8 @@ class ForceUpdateCommand:
"""验证输入参数"""
# 验证平台
if platform not in ["android", "ios"]:
self.display_manager.display_error(f"无效的平台: {platform},支持 android 或 ios")
if platform not in ["android", "ios", "ru"]:
self.display_manager.display_error(f"无效的平台: {platform},支持 android、ios 或 ru")
return False
# 验证版本号格式

View File

@@ -72,8 +72,8 @@ class SuggestUpdateCommand:
"""验证输入参数"""
# 验证平台
if platform not in ["android", "ios"]:
self.display_manager.display_error(f"无效的平台: {platform},支持 android 或 ios")
if platform not in ["android", "ios", "ru"]:
self.display_manager.display_error(f"无效的平台: {platform},支持 android、ios 或 ru")
return False
# 验证版本号格式

View File

@@ -23,8 +23,8 @@ class ViewCommand:
results = {}
success = True
# 检查AndroidiOS平台
for platform in ["android", "ios"]:
# 检查AndroidiOS和俄罗斯平台
for platform in ["android", "ios", "ru"]:
result = self._check_platform_config(version, platform)
results[platform] = result
@@ -110,7 +110,7 @@ class ViewCommand:
def _display_table_results(self, version: str, results: Dict):
"""以表格格式显示结果"""
for platform in ["android", "ios"]:
for platform in ["android", "ios", "ru"]:
if platform in results:
result = results[platform]
if result["success"]:
@@ -122,12 +122,12 @@ class ViewCommand:
)
else:
self.display_manager.display_error(
f"{platform.title()} 配置检查失败: {result['error']}"
f"{self.display_manager._platform_label(platform)} 配置检查失败: {result['error']}"
)
def _display_json_results(self, version: str, results: Dict):
"""以JSON格式显示结果"""
for platform in ["android", "ios"]:
for platform in ["android", "ios", "ru"]:
if platform in results:
result = results[platform]
if result["success"]:
@@ -138,5 +138,5 @@ class ViewCommand:
)
else:
self.display_manager.display_error(
f"{platform.title()} 配置检查失败: {result['error']}"
f"{self.display_manager._platform_label(platform)} 配置检查失败: {result['error']}"
)

View File

@@ -48,6 +48,7 @@ class Config:
},
"cdn": {
"base_url": "",
"ru_base_url": "https://128-ft-cdn-ali-tx.arksgame.com/ru",
"timeout": 10,
"retry_count": 3,
"cloudflare": {
@@ -100,6 +101,7 @@ class Config:
"CONFIG_MAN_STORAGE_RCLONE_REMOTE": ("storage", "rclone_remote"),
"CONFIG_MAN_STORAGE_TIMEOUT": ("storage", "timeout"),
"CONFIG_MAN_CDN_BASE_URL": ("cdn", "base_url"),
"CONFIG_MAN_CDN_RU_BASE_URL": ("cdn", "ru_base_url"),
"CONFIG_MAN_CDN_TIMEOUT": ("cdn", "timeout"),
"CONFIG_MAN_CDN_RETRY_COUNT": ("cdn", "retry_count"),
"CONFIG_MAN_CDN_CLOUDFLARE_API_TOKEN": ("cdn", "cloudflare", "api_token"),

View File

@@ -50,6 +50,19 @@ class MockRclone:
"RTMHmacSecret": "57047697437f4f2c97a835e8d9a53358",
"FuncUrl": "https://leaderboardcreate.azurewebsites.net",
"FuncKey": "R5kU45fNBRd52Eqp3tEqfpZqrbFw53uSSEo7wraUSqIfAzFuRmLm_w=="
},
"ru": {
"EventApiURL": "https://n3backend.azurewebsites.net/",
"Ver": "0.29.1ru123",
"PlayFabTitle": "B066F",
"Google_Play_URL": "https://play.google.com/store/apps/details?id=com.arkgame.ft",
"APP_Store_URL": "https://apps.apple.com/us/app/id6505145935",
"HeartBeat": "60",
"RTMPid": "80000586",
"RTMServerEndpoint": "rtm-intl-frontgate.ilivedata.com:13321",
"RTMHmacSecret": "57047697437f4f2c97a835e8d9a53358",
"FuncUrl": "https://leaderboardcreate.azurewebsites.net",
"FuncKey": "R5kU45fNBRd52Eqp3tEqfpZqrbFw53uSSEo7wraUSqIfAzFuRmLm_w=="
}
},
"0.30": {
@@ -80,6 +93,20 @@ class MockRclone:
"FuncUrl": "https://leaderboardcreate.azurewebsites.net",
"FuncKey": "R5kU45fNBRd52Eqp3tEqfpZqrbFw53uSSEo7wraUSqIfAzFuRmLm_w==",
"NewFeature": "enabled"
},
"ru": {
"EventApiURL": "https://n3backend.azurewebsites.net/",
"Ver": "0.30.2ru789",
"PlayFabTitle": "B066F",
"Google_Play_URL": "https://play.google.com/store/apps/details?id=com.arkgame.ft",
"APP_Store_URL": "https://apps.apple.com/us/app/id6505145935",
"HeartBeat": "60",
"RTMPid": "80000586",
"RTMServerEndpoint": "rtm-intl-frontgate.ilivedata.com:13321",
"RTMHmacSecret": "57047697437f4f2c97a835e8d9a53358",
"FuncUrl": "https://leaderboardcreate.azurewebsites.net",
"FuncKey": "R5kU45fNBRd52Eqp3tEqfpZqrbFw53uSSEo7wraUSqIfAzFuRmLm_w==",
"NewFeature": "enabled"
}
}
}
@@ -89,12 +116,18 @@ class MockRclone:
# 创建测试目录结构
for version, platforms in test_configs.items():
version_path = f"{self.test_data_path}/{storage_path}/{version.replace('.', '_')}"
os.makedirs(version_path, exist_ok=True)
version_token = version.replace('.', '_')
for platform, config_data in platforms.items():
if platform == "ru":
platform_dir = f"{self.test_data_path}/ru/{version_token}"
filename = "androidconfig.json"
else:
platform_dir = f"{self.test_data_path}/{storage_path}/{version_token}"
filename = f"{platform}config.json"
filepath = os.path.join(version_path, filename)
os.makedirs(platform_dir, exist_ok=True)
filepath = os.path.join(platform_dir, filename)
# 加密配置并保存
config_json = json.dumps(config_data, indent=2, ensure_ascii=False)

View File

@@ -38,6 +38,19 @@ def create_test_configs():
"RTMHmacSecret": "57047697437f4f2c97a835e8d9a53358",
"FuncUrl": "https://leaderboardcreate.azurewebsites.net",
"FuncKey": "R5kU45fNBRd52Eqp3tEqfpZqrbFw53uSSEo7wraUSqIfAzFuRmLm_w=="
},
"ru": {
"EventApiURL": "https://n3backend.azurewebsites.net/",
"Ver": "0.29.1ru123",
"PlayFabTitle": "B066F",
"Google_Play_URL": "https://play.google.com/store/apps/details?id=com.arkgame.ft",
"APP_Store_URL": "https://apps.apple.com/us/app/id6505145935",
"HeartBeat": "60",
"RTMPid": "80000586",
"RTMServerEndpoint": "rtm-intl-frontgate.ilivedata.com:13321",
"RTMHmacSecret": "57047697437f4f2c97a835e8d9a53358",
"FuncUrl": "https://leaderboardcreate.azurewebsites.net",
"FuncKey": "R5kU45fNBRd52Eqp3tEqfpZqrbFw53uSSEo7wraUSqIfAzFuRmLm_w=="
}
},
"0.30": {
@@ -68,17 +81,37 @@ def create_test_configs():
"FuncUrl": "https://leaderboardcreate.azurewebsites.net",
"FuncKey": "R5kU45fNBRd52Eqp3tEqfpZqrbFw53uSSEo7wraUSqIfAzFuRmLm_w==",
"NewFeature": "enabled"
},
"ru": {
"EventApiURL": "https://n3backend.azurewebsites.net/",
"Ver": "0.30.2ru789",
"PlayFabTitle": "B066F",
"Google_Play_URL": "https://play.google.com/store/apps/details?id=com.arkgame.ft",
"APP_Store_URL": "https://apps.apple.com/us/app/id6505145935",
"HeartBeat": "60",
"RTMPid": "80000586",
"RTMServerEndpoint": "rtm-intl-frontgate.ilivedata.com:13321",
"RTMHmacSecret": "57047697437f4f2c97a835e8d9a53358",
"FuncUrl": "https://leaderboardcreate.azurewebsites.net",
"FuncKey": "R5kU45fNBRd52Eqp3tEqfpZqrbFw53uSSEo7wraUSqIfAzFuRmLm_w==",
"NewFeature": "enabled"
}
}
}
# 创建测试目录结构
for version, platforms in test_configs.items():
version_path = f"test_data/ab/{version.replace('.', '_')}"
os.makedirs(version_path, exist_ok=True)
version_token = version.replace('.', '_')
for platform, config in platforms.items():
if platform == "ru":
version_path = f"test_data/ru/{version_token}"
filename = "androidconfig.json"
else:
version_path = f"test_data/ab/{version_token}"
filename = f"{platform}config.json"
os.makedirs(version_path, exist_ok=True)
filepath = os.path.join(version_path, filename)
# 加密配置并保存