# All.Net AuthLite 更新获取 from Crypto.Cipher import AES from Crypto.Util.Padding import pad import httpx from loguru import logger from urllib.parse import parse_qs import configparser as ini def enc(key, iv, data): cipher = AES.new(key, AES.MODE_CBC, iv) encrypted = cipher.encrypt(data) return encrypted def dec(key, iv ,data): de_cipher = AES.new(key, AES.MODE_CBC, iv) decrypted = de_cipher.decrypt(data) return decrypted def getRawDelivery(): key = bytes([47, 63, 106, 111, 43, 34, 76, 38, 92, 67, 114, 57, 40, 61, 107, 71]) iv = bytes.fromhex('00000000000000000000000000000000') content = bytes([0] * 16) + b'title_id=SDGB&title_ver=1.40&client_id=A63E01C2805' header = bytes.fromhex('00000000000000000000000000000000') bytes_data = pad(header + content, 16) encrypted = enc(key, iv, bytes_data) r = httpx.post( 'http://at.sys-allnet.cn/net/delivery/instruction', data = encrypted, headers = { 'User-Agent': "SDGB;Windows/Lite", 'Pragma': 'DFI' } ) resp_data = r.content decrypted = dec(key, resp_data[:16], resp_data) decrypted_str = decrypted[16:].decode('UTF-8').strip() # 过滤所有控制字符 decrypted_str = ''.join([i for i in decrypted_str if 31 < ord(i) < 127]) logger.info(f"RAW Response: {decrypted_str}") return decrypted_str def parseRawDelivery(deliveryStr): """解析 RAW 的 Delivery 字符串,返回其中的有效的 instruction URL 的列表""" parsedResponseDict = {key: value[0] for key, value in parse_qs(deliveryStr).items()} urlList = parsedResponseDict['uri'].split('|') # 过滤掉空字符串和内容为 null 的情况 urlList = [url for url in urlList if url and url != 'null'] logger.info(f"Parsed URL List: {urlList}") validURLs = [] for url in urlList: # 检查是否是 HTTPS 的 URL,以及是否是 txt 文件,否则忽略 if not url.startswith('https://') or not url.endswith('.txt'): logger.warning(f"Invalid URL will be ignored: {url}") continue validURLs.append(url) logger.info(f"Verified Valid URLs: {validURLs}") return validURLs def getUpdateIniFromURL(url): # 发送请求 response = httpx.get(url, headers={ 'User-Agent': 'SDGB;Windows/Lite', 'Pragma': 'DFI' }) logger.info(f"成功自 {url} 获取更新信息") return response.text def parseUpdateIni(iniText): # 解析配置 config = ini.ConfigParser(allow_no_value=True) config.read_string(iniText) logger.info(f"成功解析配置文件,包含的节有:{config.sections()}") # 获取 COMMON 节的配置 common = config['COMMON'] # 初始化消息列表 message = [] # 获取游戏描述并去除引号 game_desc = common['GAME_DESC'].strip('"') # 根据前缀选择消息模板和图标 prefix_icons = { 'PATCH': ('💾 游戏程序更新 ', 'PATCH_'), 'OPTION': ('📚 游戏内容更新 ', 'OPTION_') } icon, prefix = prefix_icons.get(game_desc.split('_')[0], ('📦 游戏更新 ', '')) # 构建消息标题 game_title = game_desc.replace(prefix, '', 1) message.append(f"{icon}{game_title}") # 添加主文件的下载链接 main_file = common['INSTALL1'] main_file_name = main_file.split('/')[-1] message.append(f"下载: \n- [{main_file_name}]({main_file})") # 添加可选文件的下载链接(如果有) if 'OPTIONAL' in config: message.append("其它文件:") optional_files = [f"- [{url.split('/')[-1]}]({url})" for _, url in config.items('OPTIONAL')] message.extend(optional_files) # 添加发布时间信息 release_time = common['RELEASE_TIME'].replace('T', ' ') message.append(f"将于 {release_time} 发布。\n") # 构建最终的消息字符串 final_message = '\n'.join(message) logger.info(f"消息构建完成,最终的消息为:\n{final_message}") return final_message if __name__ == '__main__': urlList = parseRawDelivery(getRawDelivery()) for url in urlList: iniText = getUpdateIniFromURL(url) message = parseUpdateIni(iniText)