diff --git a/apps/apple/clients/itunes/models/redeem.py b/apps/apple/clients/itunes/models/redeem.py index 4496fda..ecfb6c1 100644 --- a/apps/apple/clients/itunes/models/redeem.py +++ b/apps/apple/clients/itunes/models/redeem.py @@ -31,14 +31,14 @@ class CustomSuccessData(BaseModel): class RedeemedCredit(BaseModel): - movieRentalBalance: int - songBalance: int - videoBalance: int - money: str - totalCredit: str + movieRentalBalance: int = Field(default=0) + songBalance: int = Field(default=0) + videoBalance: int = Field(default=0) + money: str = Field(default="") + totalCredit: str = Field(default="") moneyRaw: float = Field(default=0) - gameBalance: int - tvRentalBalance: int + gameBalance: int = Field(default=0) + tvRentalBalance: int = Field(default=0) class RedeemSuccessResponse(BaseModel): diff --git a/apps/apple/clients/itunes/utils.py b/apps/apple/clients/itunes/utils.py index efaf707..04737b7 100644 --- a/apps/apple/clients/itunes/utils.py +++ b/apps/apple/clients/itunes/utils.py @@ -1,16 +1,19 @@ -from xml.etree import ElementTree +from xml.etree.ElementTree import Element, fromstring def parse_xml(xml_str: str) -> dict: - root = ElementTree.fromstring(xml_str) + root = fromstring(xml_str) dict_data = {} key = "" - for child in root.find("dict"): + dict_element = root.find("dict") + if dict_element is None: + return dict_data + for child in dict_element: if child.tag == "key": - key = child.text + key = child.text or "" else: if child.tag == "integer": - dict_data[key] = int(child.text) + dict_data[key] = int(child.text or "0") elif child.tag == "string": dict_data[key] = child.text if child.text else "" elif child.tag == "true": @@ -24,12 +27,12 @@ def parse_xml(xml_str: str) -> dict: return dict_data -def parse_xml_tree(tree_list: list[ElementTree]): +def parse_xml_tree(tree_list: list[Element]): dict_data = {} key = "" for child in tree_list: if child.tag == "key": - key = child.text + key = child.text or "" else: if child.tag == "string": dict_data[key] = child.text if child.text else "" diff --git a/apps/apple/clients/june/api.py b/apps/apple/clients/june/api.py index f14f6ff..18ee9d7 100644 --- a/apps/apple/clients/june/api.py +++ b/apps/apple/clients/june/api.py @@ -1,3 +1,4 @@ +import asyncio import base64 import hashlib import json @@ -24,11 +25,48 @@ from core.clients.http_client import HTTPClient logger = get_logger(__name__) -class SixClient: +async def try_get_url() -> str: + host_items = ["https://gitee.com/liuyueapp/blogsLiuyue/raw/master/assets/aphos.css", "https://meixi2.oss-us-west-1.aliyuncs.com/host.txt", "https://zjzhuanfa.oss-cn-shenzhen.aliyuncs.com/host.txt"] + target_host_urls = [] + for host in host_items: + client = HTTPClient( + base_url=host, + timeout=2.0, + max_retries=1, + ) + try: + response = await client.get(host) + if response.status_code == 200: + target_host_urls.extend(response.text.splitlines()) + except Exception as e: + logger.error(e) + # 去重 + target_host_urls = list(set(target_host_urls)) + for host in target_host_urls: + client = HTTPClient( + base_url=host, + timeout=5.0, + max_retries=1, + ) + try: + response = await client.get(host+"/AppleClientApi/requestApi") + if response.status_code == 404: + return host + except Exception as e: + print(e) + return "http://43.240.73.119:6113" + + +__base_url = asyncio.run(main=try_get_url()) + +class SixClient: + __base_url = "http://43.240.73.119:6113" + def __init__(self): - self.__base_url = "http://43.240.73.119:6113" + if not self.__base_url: + self.__base_url = asyncio.run(try_get_url()) self._client: Optional[HTTPClient] = None - + async def _get_client(self) -> HTTPClient: """获取或创建HTTP客户端""" if self._client is None: @@ -38,7 +76,7 @@ class SixClient: max_retries=3, ) return self._client - + async def close(self): """关闭客户端连接""" if self._client: @@ -47,10 +85,9 @@ class SixClient: async def _do_post( self, post_data: Any, type_: str, start_now_fun: str = "0", reties: int = 3 - ) -> AppleSixResponseModel | None: + ) -> AppleSixResponseModel: if reties <= 0: - return None - + raise Exception("请求超时") client = await self._get_client() req_count = random.randint(0, 90) + 1 @@ -154,14 +191,14 @@ class SixClient: if response and response.Data: response.Data = json.loads(response.Data) return AppleSixResponseModel[dict].model_validate(response.model_dump()) - return None + raise Exception("远程登录失败") async def get_sign_sap_setup( self, reties: int = 3 - ) -> AppleSixResponseModel[LoginSignatureModel] | None: + ) -> AppleSixResponseModel[LoginSignatureModel]: if reties < 0: - return None - response = await self._do_post( + raise Exception("获取 sign 失败") + response: AppleSixResponseModel[str] = await self._do_post( json.dumps( { "gZip": "1", @@ -174,20 +211,23 @@ class SixClient: start_now_fun="9", ) if not response or not response.Data: - return None - response.Data = json.loads(decode_and_decompress(response.Data)) - if response.Data.get("msg") == "请重试": + raise Exception("获取 sign 失败") + response_data = json.loads(decode_and_decompress(response.Data)) + if response_data.get("msg") == "请重试": logger.info(f"重试六月登录,{response}") time.sleep(1) return await self.get_sign_sap_setup(reties - 1) - response = AppleSixResponseModel[LoginSignatureModel].model_validate( - response.model_dump() + + target_response_data = response.model_dump() + target_response_data["Data"] = response_data + target_response = AppleSixResponseModel[LoginSignatureModel].model_validate( + target_response_data ) # 处理signature编码问题和Base64转码问题 - response.Data.signature = base64.b64decode( - parse.unquote_plus(response.Data.signature) + target_response.Data.signature = base64.b64decode( + parse.unquote_plus(target_response.Data.signature) ).decode() - return response + return target_response async def get_sign_sap_setup_cert( self, @@ -195,10 +235,10 @@ class SixClient: sign: AppleSixResponseModel[LoginSignatureModel], sign_sap_setup: str, reties: int = 3, - ) -> AppleSixResponseModel[AuthenticateModel] | None: + ) -> AppleSixResponseModel[AuthenticateModel]: if reties < 0: - return None - response = await self._do_post( + raise Exception("获取 sign 失败") + response:AppleSixResponseModel[str] = await self._do_post( json.dumps( { "signSap": parse.quote_plus( @@ -220,23 +260,28 @@ class SixClient: start_now_fun="9", ) if not response or not response.Data: - return None + raise Exception("获取 sign 失败") try: - response.Data = json.loads(decode_and_decompress(response.Data)) + response_data = json.loads(decode_and_decompress(response.Data)) except AttributeError as e: - logger.error(f"获取cert失败,{response},错误信息:{traceback.format_exc()}") + logger.error(msg=f"获取cert失败,{response},错误信息:{traceback.format_exc()}") return await self.get_sign_sap_setup_cert(account, sign, sign_sap_setup, reties - 1) - if response.Data.get("msg") == "请重试": + if response_data.get("msg") == "请重试": logger.info(f"重试六月登录,{response}") time.sleep(1) return await self.get_sign_sap_setup_cert( account, sign, sign_sap_setup, reties - 1 ) - response = AppleSixResponseModel[AuthenticateModel].model_validate( + + + new_target_response_data= response.model_dump() + new_target_response_data["Data"] = response_data + target_response_data = AppleSixResponseModel[AuthenticateModel].model_validate( response.model_dump() ) # 解码数据 - response.Data.signature = parse.unquote_plus(response.Data.signature) - response.Data.guid = parse.unquote_plus(response.Data.guid) - response.Data.post = parse.unquote_plus(response.Data.post) - return response + target_response_data.Data.signature = parse.unquote_plus(target_response_data.Data.signature) + target_response_data.Data.guid = parse.unquote_plus(target_response_data.Data.guid) + target_response_data.Data.post = parse.unquote_plus(target_response_data.Data.post) + return target_response_data + diff --git a/apps/apple/clients/june/models/login.py b/apps/apple/clients/june/models/login.py index 17ed6e7..9bef7d3 100644 --- a/apps/apple/clients/june/models/login.py +++ b/apps/apple/clients/june/models/login.py @@ -26,22 +26,22 @@ class LoginSessionInfo(BaseModel): class Cookies(BaseModel): - wosid: str = Field(None, alias="wosid") - woinst: str = Field(None, alias="woinst") + wosid: str | None = Field(None, alias="wosid") + woinst: str | None = Field(None, alias="woinst") ns_mzf_inst: str = Field(..., alias="ns-mzf-inst") - mzf_in: str = Field(None, alias="mzf_in") - mzf_dr: str = Field(None, alias="mzf_dr") - hsaccnt: str = Field(None, alias="hsaccnt") + mzf_in: str | None = Field(None, alias="mzf_in") + mzf_dr: str | None = Field(None, alias="mzf_dr") + hsaccnt: str | None = Field(None, alias="hsaccnt") session_store_id: str = Field(..., alias="session-store-id") X_Dsid: str = Field(..., alias="X-Dsid") mz_at0_135096725: str = Field(..., alias="mz_at0-135096725") - ampsc: str = Field(None, alias="ampsc") + ampsc: str | None = Field(None, alias="ampsc") mz_at_ssl_135096725: str = Field(..., alias="mz_at_ssl-135096725") mz_at_mau_135096725: str = Field(..., alias="mz_at_mau-135096725") - pldfltcid: str = Field(None, alias="pldfltcid") + pldfltcid: str | None = Field(None, alias="pldfltcid") tv_pldfltcid: str = Field(..., alias="tv-pldfltcid") wosid_lite: str = Field(..., alias="wosid-lite") - itspod: str = Field(None, alias="itspod") + itspod: str | None = Field(None, alias="itspod") class RemoteCookieModel(BaseModel): @@ -87,7 +87,7 @@ DataT = TypeVar("DataT") class AppleSixResponseModel(BaseModel, Generic[DataT]): Code: str = Field(default="") Message: str = Field(default="") - Data: DataT = Field(default="") + Data: DataT = Field() serverIndex: int = Field(default=0) extend: str = Field(default="") authenUserInfo: str = Field(default="") diff --git a/apps/apple/clients/june/test_api.py b/apps/apple/clients/june/test_api.py deleted file mode 100644 index 861a740..0000000 --- a/apps/apple/clients/june/test_api.py +++ /dev/null @@ -1,9 +0,0 @@ -from unittest import TestCase - -from src.integrations.june.api import SixClient - - -class TestSixClient(TestCase): - def test_get_sign_sap_setup(self): - result = SixClient().get_sign_sap_setup() - print(result) \ No newline at end of file diff --git a/apps/apple/clients/june/test_utils.py b/apps/apple/clients/june/test_utils.py deleted file mode 100644 index 388e7a9..0000000 --- a/apps/apple/clients/june/test_utils.py +++ /dev/null @@ -1,13 +0,0 @@ -from unittest import TestCase - -from src.integrations.june.utils.utils import ShareCodeUtils, MachineCode - - -class TestShareCodeUtils(TestCase): - def test_code_to_id(self): - print(ShareCodeUtils.code_to_id("2JPA")) - - def test_get_cpu_info(self): - print(MachineCode().get_cpu_info()) - print(MachineCode().get_hd_id()) - print(MachineCode().get_mo_address()) diff --git a/apps/apple/clients/june/utils/linux.py b/apps/apple/clients/june/utils/linux.py deleted file mode 100644 index e69de29..0000000 diff --git a/apps/apple/clients/june/utils/windows.py b/apps/apple/clients/june/utils/windows.py deleted file mode 100644 index e69de29..0000000 diff --git a/core/clients/base.py b/core/clients/base.py index 5c4be9f..4e4ffb7 100644 --- a/core/clients/base.py +++ b/core/clients/base.py @@ -54,7 +54,7 @@ class BaseAPIClient(ABC): self.timeout = timeout self.max_retries = max_retries self.retry_delay = retry_delay - self.retry_backoff = retry_backoff + self.retry_backoff: float = retry_backoff self.trace_enabled = trace_enabled self._client: Optional[httpx.AsyncClient] = None diff --git a/core/clients/http_client.py b/core/clients/http_client.py index 500af2b..5405b07 100644 --- a/core/clients/http_client.py +++ b/core/clients/http_client.py @@ -133,8 +133,12 @@ class HTTPClient(BaseAPIClient): httpx.TimeoutException: On timeout httpx.RequestError: For network errors """ - client = await self._get_client() - url = f"{self.base_url}/{path.lstrip('/')}" + client = await self._get_client() + # 判断path是否是完整的 http 请求 + if not path.startswith("http"): + url = f"{self.base_url}/{path.lstrip('/')}" + else: + url = path # Merge headers request_headers = {**self.default_headers, **(headers or {})} @@ -189,8 +193,8 @@ class HTTPClient(BaseAPIClient): else: span.set_status(Status(StatusCode.OK)) - # Raise for 4xx/5xx - response.raise_for_status() + # # Raise for 4xx/5xx + # response.raise_for_status() return response @@ -218,8 +222,8 @@ class HTTPClient(BaseAPIClient): response.elapsed.total_seconds() ) - # Raise for 4xx/5xx - response.raise_for_status() + # # Raise for 4xx/5xx + # response.raise_for_status() return response