feat(service): 添加代理服务并更新相关功能

- 新增 ProxyService 类,用于管理代理服务器
- 在 iTunes 服务中集成代理服务
- 更新 .gitignore 文件,添加新忽略项
- 在配置文件中添加代理服务器地址
- 更新相关模块以支持代理服务
This commit is contained in:
danial
2024-10-31 00:36:33 +08:00
parent 06b1481eec
commit 371fe49d1a
7 changed files with 87 additions and 22 deletions

3
.gitignore vendored
View File

@@ -1,3 +1,6 @@
/.idea/ /.idea/
/data/ /data/
/venv/ /venv/
/.ipynb_checkpoints/
*.pyc
*.log

View File

@@ -21,4 +21,10 @@ redis:
db: 0 db: 0
masterNode: masterNode:
address: http://121.37.253.228:12310 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

View File

@@ -42,11 +42,16 @@ class MasterNodeSettings(BaseSettings):
address: str = Field(default="127.0.0.1", description="Address") address: str = Field(default="127.0.0.1", description="Address")
class ProxySettings(BaseSettings):
address: list[str] = Field(default=[], description="Address")
class Settings(BaseSettings): class Settings(BaseSettings):
server: ServerSettings = Field(default_factory=ServerSettings) server: ServerSettings = Field(default_factory=ServerSettings)
redis: RedisSettings = Field(default_factory=RedisSettings) redis: RedisSettings = Field(default_factory=RedisSettings)
database: DataBaseSettings = Field(default_factory=DataBaseSettings) database: DataBaseSettings = Field(default_factory=DataBaseSettings)
masterNode: MasterNodeSettings = Field(default_factory=MasterNodeSettings) masterNode: MasterNodeSettings = Field(default_factory=MasterNodeSettings)
proxies: ProxySettings = Field(default_factory=ProxySettings)
model_config = SettingsConfigDict(yaml_file="./config/config.yml", extra="ignore") model_config = SettingsConfigDict(yaml_file="./config/config.yml", extra="ignore")
@classmethod @classmethod

View File

@@ -18,14 +18,17 @@ from src.integrations.itunes.models.redeem import (
from src.integrations.itunes.utils import parse_xml from src.integrations.itunes.utils import parse_xml
from src.integrations.june.models.login import LoginSignatureModel, ItunesLoginModel from src.integrations.june.models.login import LoginSignatureModel, ItunesLoginModel
from src.integrations.june.models.redeem import AuthenticateModel, ItunesRedeemModel from src.integrations.june.models.redeem import AuthenticateModel, ItunesRedeemModel
from src.service.proxy import ProxyService
class AppleClient: class AppleClient:
def __init__(self): def __init__(self):
self.__session = requests.Session() self.__session = requests.Session()
self.__session.adapters.update({ self.__session.adapters.update(
"DEFAULT_RETRIES": 5, {
}) "DEFAULT_RETRIES": 5,
}
)
def query_sign_sap_setup(self, signature: LoginSignatureModel, retries=3) -> str: def query_sign_sap_setup(self, signature: LoginSignatureModel, retries=3) -> str:
if retries <= 0: if retries <= 0:
@@ -63,7 +66,7 @@ class AppleClient:
return response.text return response.text
def login( def login(
self, sign_map: AuthenticateModel, server_id: str = "", retries: int = 5 self, sign_map: AuthenticateModel, server_id: str = "", retries: int = 5
) -> ItunesLoginResponse: ) -> ItunesLoginResponse:
headers = { headers = {
"X-Apple-ActionSignature": sign_map.signature, "X-Apple-ActionSignature": sign_map.signature,
@@ -90,9 +93,9 @@ class AppleClient:
params = {} params = {}
if server_id != "": if server_id != "":
url = ( url = (
"https://p" "https://p"
+ server_id + server_id
+ "-buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/authenticate" + "-buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/authenticate"
) )
params = {"Pod": server_id, "PRH": server_id} params = {"Pod": server_id, "PRH": server_id}
else: else:
@@ -118,14 +121,14 @@ class AppleClient:
status = 31 status = 31
# 账户被禁用 # 账户被禁用
if ( if (
response_dict_data.get("metrics", {}).get("dialogId") response_dict_data.get("metrics", {}).get("dialogId")
== "MZFinance.AccountDisabled" == "MZFinance.AccountDisabled"
): ):
status = 14 status = 14
# 账户被锁定 # 账户被锁定
if ( if (
response_dict_data.get("metrics", {}).get("dialogId") response_dict_data.get("metrics", {}).get("dialogId")
== "MZFinance.DisabledAndFraudLocked" == "MZFinance.DisabledAndFraudLocked"
): ):
status = 14 status = 14
# 密码错误 # 密码错误
@@ -143,10 +146,10 @@ class AppleClient:
) )
def redeem( def redeem(
self, self,
code: str, code: str,
itunes: ItunesLoginModel, itunes: ItunesLoginModel,
reties=5, reties=5,
) -> RedeemSuccessResponse | RedeemFailResponseModel: ) -> RedeemSuccessResponse | RedeemFailResponseModel:
if reties <= 0: if reties <= 0:
logger.error("兑换失败,兑换重试次数已用完") logger.error("兑换失败,兑换重试次数已用完")
@@ -173,7 +176,7 @@ class AppleClient:
"Content-Type": "application/x-apple-plist; Charset=UTF-8", "Content-Type": "application/x-apple-plist; Charset=UTF-8",
"User-Agent": "MacAppStore/2.0 (Macintosh; OS X 12.10) AppleWebKit/600.1.3.41", "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" "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: try:
response = self.__session.post( response = self.__session.post(
@@ -190,6 +193,7 @@ class AppleClient:
has_4gb_limit=False, has_4gb_limit=False,
).to_xml(), ).to_xml(),
headers=headers, headers=headers,
proxies=ProxyService().get_wrap_proxy(itunes.account_name),
) )
except Exception as e: except Exception as e:
logger.warning(f"兑换连接错误,重试:{e}\t{traceback.format_exc()}") logger.warning(f"兑换连接错误,重试:{e}\t{traceback.format_exc()}")
@@ -233,22 +237,31 @@ class AppleClient:
result = RedeemFailResponseModel.model_validate(response.json()) result = RedeemFailResponseModel.model_validate(response.json())
result.origin_log = response.text result.origin_log = response.text
if ( if (
result.errorMessageKey result.errorMessageKey
== "MZCommerce.GiftCertificateAlreadyRedeemed" == "MZCommerce.GiftCertificateAlreadyRedeemed"
): ):
# 已经被兑换 # 已经被兑换
result.status = 12 result.status = 12
elif result.errorMessageKey == "MZFreeProductCode.NoSuch": elif result.errorMessageKey == "MZFreeProductCode.NoSuch":
# 没有这个卡密 # 没有这个卡密
result.status = 11 result.status = 11
elif result.errorMessageKey == "MZCommerce.NatIdYearlyCapExceededException": elif (
result.errorMessageKey
== "MZCommerce.NatIdYearlyCapExceededException"
):
# 年限额 # 年限额
result.status = 31 result.status = 31
elif result.errorMessageKey == "MZCommerce.NatIdDailyCapExceededException": elif (
result.errorMessageKey
== "MZCommerce.NatIdDailyCapExceededException"
):
# 日限额 # 日限额
result.status = 31 result.status = 31
# 国籍问题 # 国籍问题
elif result.errorMessageKey == "MZCommerce.GiftCertRedeemStoreFrontMismatch": elif (
result.errorMessageKey
== "MZCommerce.GiftCertRedeemStoreFrontMismatch"
):
result.status = 15 result.status = 15
else: else:
logger.error(f"失败状态未知:{response.json()}") logger.error(f"失败状态未知:{response.json()}")

View File

@@ -74,6 +74,7 @@ class AppleAccountModel(BaseModel):
class ItunesLoginModel(BaseModel): class ItunesLoginModel(BaseModel):
account_name: str = Field(default="")
server_id: str = Field(default="") server_id: str = Field(default="")
dsis: int = Field(default=0) dsis: int = Field(default=0)
guid: str = Field(default="") guid: str = Field(default="")

View File

@@ -121,6 +121,7 @@ class ItunesService:
result = self.apple_client_service.redeem( result = self.apple_client_service.redeem(
code, code,
ItunesLoginModel( ItunesLoginModel(
account_name=item.account_name,
server_id=item.login_schema.server_id, server_id=item.login_schema.server_id,
guid=item.login_schema.guid, guid=item.login_schema.guid,
dsis=int(item.login_schema.dsis), dsis=int(item.login_schema.dsis),

36
src/service/proxy.py Normal file
View File

@@ -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)