diff --git a/.gitignore b/.gitignore index e47fcc0..9af8951 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ /.idea/ /data/ /venv/ +/.ipynb_checkpoints/ +*.pyc +*.log \ No newline at end of file diff --git a/config/config.yml b/config/config.yml index 26333f5..dcd7dc0 100644 --- a/config/config.yml +++ b/config/config.yml @@ -21,4 +21,10 @@ redis: db: 0 masterNode: - address: http://121.37.253.228:12310 \ No newline at end of file + address: http://121.37.253.228:12310 + +proxies: + address: + - http://fga3568:fga3568@219.152.50.19:6588 + - http://fga3568:fga3568@125.74.88.251:6588 + - http://fga3568:fga3568@36.111.202.63:6588 diff --git a/src/initialization/settings.py b/src/initialization/settings.py index fbdf73a..2d91183 100644 --- a/src/initialization/settings.py +++ b/src/initialization/settings.py @@ -42,11 +42,16 @@ class MasterNodeSettings(BaseSettings): address: str = Field(default="127.0.0.1", description="Address") +class ProxySettings(BaseSettings): + address: list[str] = Field(default=[], description="Address") + + class Settings(BaseSettings): server: ServerSettings = Field(default_factory=ServerSettings) redis: RedisSettings = Field(default_factory=RedisSettings) database: DataBaseSettings = Field(default_factory=DataBaseSettings) masterNode: MasterNodeSettings = Field(default_factory=MasterNodeSettings) + proxies: ProxySettings = Field(default_factory=ProxySettings) model_config = SettingsConfigDict(yaml_file="./config/config.yml", extra="ignore") @classmethod diff --git a/src/integrations/itunes/api.py b/src/integrations/itunes/api.py index 0bba998..c743dc8 100644 --- a/src/integrations/itunes/api.py +++ b/src/integrations/itunes/api.py @@ -18,14 +18,17 @@ from src.integrations.itunes.models.redeem import ( from src.integrations.itunes.utils import parse_xml from src.integrations.june.models.login import LoginSignatureModel, ItunesLoginModel from src.integrations.june.models.redeem import AuthenticateModel, ItunesRedeemModel +from src.service.proxy import ProxyService class AppleClient: def __init__(self): self.__session = requests.Session() - self.__session.adapters.update({ - "DEFAULT_RETRIES": 5, - }) + self.__session.adapters.update( + { + "DEFAULT_RETRIES": 5, + } + ) def query_sign_sap_setup(self, signature: LoginSignatureModel, retries=3) -> str: if retries <= 0: @@ -63,7 +66,7 @@ class AppleClient: return response.text def login( - self, sign_map: AuthenticateModel, server_id: str = "", retries: int = 5 + self, sign_map: AuthenticateModel, server_id: str = "", retries: int = 5 ) -> ItunesLoginResponse: headers = { "X-Apple-ActionSignature": sign_map.signature, @@ -90,9 +93,9 @@ class AppleClient: params = {} if server_id != "": url = ( - "https://p" - + server_id - + "-buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/authenticate" + "https://p" + + server_id + + "-buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/authenticate" ) params = {"Pod": server_id, "PRH": server_id} else: @@ -118,14 +121,14 @@ class AppleClient: status = 31 # 账户被禁用 if ( - response_dict_data.get("metrics", {}).get("dialogId") - == "MZFinance.AccountDisabled" + response_dict_data.get("metrics", {}).get("dialogId") + == "MZFinance.AccountDisabled" ): status = 14 # 账户被锁定 if ( - response_dict_data.get("metrics", {}).get("dialogId") - == "MZFinance.DisabledAndFraudLocked" + response_dict_data.get("metrics", {}).get("dialogId") + == "MZFinance.DisabledAndFraudLocked" ): status = 14 # 密码错误 @@ -143,10 +146,10 @@ class AppleClient: ) def redeem( - self, - code: str, - itunes: ItunesLoginModel, - reties=5, + self, + code: str, + itunes: ItunesLoginModel, + reties=5, ) -> RedeemSuccessResponse | RedeemFailResponseModel: if reties <= 0: logger.error("兑换失败,兑换重试次数已用完") @@ -173,7 +176,7 @@ class AppleClient: "Content-Type": "application/x-apple-plist; Charset=UTF-8", "User-Agent": "MacAppStore/2.0 (Macintosh; OS X 12.10) AppleWebKit/600.1.3.41", "Referer": f"https://p{itunes.server_id}-buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/com.apple" - f".jingle.app.finance.DirectAction/redeemCode?cl=iTunes&pg=Music", + f".jingle.app.finance.DirectAction/redeemCode?cl=iTunes&pg=Music", } try: response = self.__session.post( @@ -190,6 +193,7 @@ class AppleClient: has_4gb_limit=False, ).to_xml(), headers=headers, + proxies=ProxyService().get_wrap_proxy(itunes.account_name), ) except Exception as e: logger.warning(f"兑换连接错误,重试:{e}\t{traceback.format_exc()}") @@ -233,22 +237,31 @@ class AppleClient: result = RedeemFailResponseModel.model_validate(response.json()) result.origin_log = response.text if ( - result.errorMessageKey - == "MZCommerce.GiftCertificateAlreadyRedeemed" + result.errorMessageKey + == "MZCommerce.GiftCertificateAlreadyRedeemed" ): # 已经被兑换 result.status = 12 elif result.errorMessageKey == "MZFreeProductCode.NoSuch": # 没有这个卡密 result.status = 11 - elif result.errorMessageKey == "MZCommerce.NatIdYearlyCapExceededException": + elif ( + result.errorMessageKey + == "MZCommerce.NatIdYearlyCapExceededException" + ): # 年限额 result.status = 31 - elif result.errorMessageKey == "MZCommerce.NatIdDailyCapExceededException": + elif ( + result.errorMessageKey + == "MZCommerce.NatIdDailyCapExceededException" + ): # 日限额 result.status = 31 # 国籍问题 - elif result.errorMessageKey == "MZCommerce.GiftCertRedeemStoreFrontMismatch": + elif ( + result.errorMessageKey + == "MZCommerce.GiftCertRedeemStoreFrontMismatch" + ): result.status = 15 else: logger.error(f"失败状态未知:{response.json()}") diff --git a/src/integrations/june/models/login.py b/src/integrations/june/models/login.py index 17ed6e7..d114ef2 100644 --- a/src/integrations/june/models/login.py +++ b/src/integrations/june/models/login.py @@ -74,6 +74,7 @@ class AppleAccountModel(BaseModel): class ItunesLoginModel(BaseModel): + account_name: str = Field(default="") server_id: str = Field(default="") dsis: int = Field(default=0) guid: str = Field(default="") diff --git a/src/service/itunes.py b/src/service/itunes.py index 65cc20c..8bc9d3d 100644 --- a/src/service/itunes.py +++ b/src/service/itunes.py @@ -121,6 +121,7 @@ class ItunesService: result = self.apple_client_service.redeem( code, ItunesLoginModel( + account_name=item.account_name, server_id=item.login_schema.server_id, guid=item.login_schema.guid, dsis=int(item.login_schema.dsis), diff --git a/src/service/proxy.py b/src/service/proxy.py new file mode 100644 index 0000000..906cb10 --- /dev/null +++ b/src/service/proxy.py @@ -0,0 +1,36 @@ +import copy +from time import time +from src.initialization import setting +import random + +# 账户列表 +account_list = {} + + +class ProxyService: + def get_wrap_proxy(self, account_name: str): + proxy = self.get_proxy(account_name) + return { + "http": proxy, + "https": proxy, + } + + def get_proxy(self, account_name: str): + self.set_expire_strategy() + if account_name in account_list: + return account_list[account_name].address + return self.set_proxy(account_name) + + def set_proxy(self, account_name: str): + proxy = random.choice(setting.proxies.address) + account_list[account_name] = { + "address": proxy, + "timeout": time.time() + 60 * 60 * 24, + } + return proxy + + # 设置触发账号过期策略 + def set_expire_strategy(self): + for key, value in copy.deepcopy(account_list).items(): + if value.get("timeout") > time.time(): + account_list.pop(key)