mirror of
https://github.com/GuDong2003/xianyu-auto-reply-fix.git
synced 2026-08-28 17:40:45 +08:00
194 lines
7.3 KiB
Python
194 lines
7.3 KiB
Python
import asyncio
|
|
import json
|
|
import random
|
|
import time
|
|
from typing import Any
|
|
|
|
from loguru import logger
|
|
|
|
from utils.xianyu_utils import generate_sign, trans_cookies
|
|
|
|
|
|
class ItemPolishModule:
|
|
"""商品擦亮模块。"""
|
|
|
|
def __init__(self, runtime: Any):
|
|
self.runtime = runtime
|
|
|
|
async def _sync_cookies_from_response(self, response: Any) -> None:
|
|
if 'set-cookie' not in response.headers:
|
|
return
|
|
|
|
new_cookies = {}
|
|
for cookie in response.headers.getall('set-cookie', []):
|
|
if '=' not in cookie:
|
|
continue
|
|
name, value = cookie.split(';')[0].split('=', 1)
|
|
new_cookies[name.strip()] = value.strip()
|
|
|
|
if not new_cookies:
|
|
return
|
|
|
|
self.runtime.cookies.update(new_cookies)
|
|
self.runtime.cookies_str = '; '.join([f"{k}={v}" for k, v in self.runtime.cookies.items()])
|
|
await self.runtime.update_config_cookies()
|
|
|
|
def _build_polish_request(self, api_name: str, item_id: Any) -> tuple[dict[str, str], dict[str, str]]:
|
|
params = {
|
|
'jsv': '2.7.2',
|
|
'appKey': '34839810',
|
|
't': str(int(time.time()) * 1000),
|
|
'sign': '',
|
|
'v': '1.0',
|
|
'type': 'originaljson',
|
|
'accountSite': 'xianyu',
|
|
'dataType': 'json',
|
|
'timeout': '20000',
|
|
'api': api_name,
|
|
'sessionOption': 'AutoLoginOnly',
|
|
'spm_cnt': 'a21ybx.im.0.0',
|
|
'spm_pre': 'a21ybx.collection.menu.1.272b5141NafCNK',
|
|
}
|
|
|
|
cookies = trans_cookies(self.runtime.cookies_str)
|
|
token = cookies.get('_m_h5_tk', '').split('_')[0] if cookies.get('_m_h5_tk') else ''
|
|
data_obj = {'itemId': str(item_id)}
|
|
data_val = json.dumps(data_obj, separators=(',', ':'))
|
|
params['sign'] = generate_sign(params['t'], token, data_val)
|
|
|
|
return params, {'data': data_val}
|
|
|
|
async def polish_item(self, item_id: Any, retry_count: int = 0) -> dict[str, Any]:
|
|
"""擦亮单个商品。"""
|
|
if retry_count >= 4:
|
|
logger.error(f"【{self.runtime.cookie_id}】擦亮商品 {item_id} 失败,重试次数过多")
|
|
return {'success': False, 'item_id': str(item_id), 'error': '重试次数过多'}
|
|
|
|
if not self.runtime.session:
|
|
await self.runtime.create_session()
|
|
|
|
params, payload = self._build_polish_request('mtop.taobao.idle.item.polish', item_id)
|
|
|
|
try:
|
|
async with self.runtime.session.post(
|
|
'https://h5api.m.goofish.com/h5/mtop.taobao.idle.item.polish/1.0/',
|
|
params=params,
|
|
data=payload,
|
|
) as response:
|
|
res_json = await response.json()
|
|
await self._sync_cookies_from_response(response)
|
|
|
|
ret_list = res_json.get('ret', [])
|
|
ret_msg = ret_list[0] if ret_list else ''
|
|
|
|
if 'SUCCESS' in ret_msg or '调用成功' in ret_msg:
|
|
logger.info(f"【{self.runtime.cookie_id}】擦亮商品 {item_id} 成功")
|
|
return {'success': True, 'item_id': str(item_id)}
|
|
|
|
if (
|
|
'FAIL_SYS_TOKEN_EXOIRED' in ret_msg
|
|
or 'FAIL_SYS_TOKEN_EXPIRED' in ret_msg
|
|
or 'token' in ret_msg.lower()
|
|
):
|
|
logger.warning(f"【{self.runtime.cookie_id}】Token失效,准备重试擦亮商品 {item_id}: {ret_msg}")
|
|
await asyncio.sleep(0.5)
|
|
return await self.polish_item(item_id, retry_count + 1)
|
|
|
|
logger.warning(f"【{self.runtime.cookie_id}】擦亮商品 {item_id} 失败: {ret_msg}")
|
|
if retry_count == 0:
|
|
return await self._polish_item_backup(item_id)
|
|
return {'success': False, 'item_id': str(item_id), 'error': ret_msg}
|
|
|
|
except Exception as exc:
|
|
logger.error(f"【{self.runtime.cookie_id}】擦亮商品 {item_id} 异常: {self.runtime._safe_str(exc)}")
|
|
await asyncio.sleep(0.5)
|
|
return await self.polish_item(item_id, retry_count + 1)
|
|
|
|
async def _polish_item_backup(self, item_id: Any) -> dict[str, Any]:
|
|
"""使用备用 API 擦亮商品。"""
|
|
if not self.runtime.session:
|
|
await self.runtime.create_session()
|
|
|
|
params, payload = self._build_polish_request('mtop.idle.item.polish', item_id)
|
|
|
|
try:
|
|
async with self.runtime.session.post(
|
|
'https://h5api.m.goofish.com/h5/mtop.idle.item.polish/1.0/',
|
|
params=params,
|
|
data=payload,
|
|
) as response:
|
|
res_json = await response.json()
|
|
await self._sync_cookies_from_response(response)
|
|
|
|
ret_list = res_json.get('ret', [])
|
|
ret_msg = ret_list[0] if ret_list else ''
|
|
|
|
if 'SUCCESS' in ret_msg or '调用成功' in ret_msg:
|
|
logger.info(f"【{self.runtime.cookie_id}】备用API擦亮商品 {item_id} 成功")
|
|
return {'success': True, 'item_id': str(item_id)}
|
|
|
|
logger.warning(f"【{self.runtime.cookie_id}】备用API擦亮商品 {item_id} 失败: {ret_msg}")
|
|
return {'success': False, 'item_id': str(item_id), 'error': ret_msg}
|
|
|
|
except Exception as exc:
|
|
logger.error(f"【{self.runtime.cookie_id}】备用API擦亮商品 {item_id} 异常: {self.runtime._safe_str(exc)}")
|
|
return {'success': False, 'item_id': str(item_id), 'error': str(exc)}
|
|
|
|
async def polish_all_items(self) -> dict[str, Any]:
|
|
"""擦亮所有在售商品。"""
|
|
logger.info(f"【{self.runtime.cookie_id}】开始擦亮所有商品")
|
|
|
|
all_items_result = await self.runtime.get_all_items()
|
|
if not all_items_result.get('success'):
|
|
return {
|
|
'success': False,
|
|
'message': f"获取商品列表失败: {all_items_result.get('error', '未知错误')}",
|
|
'total': 0,
|
|
'polished': 0,
|
|
'failed': 0,
|
|
'results': [],
|
|
}
|
|
|
|
items = all_items_result.get('items', [])
|
|
if not items:
|
|
return {
|
|
'success': True,
|
|
'message': '没有在售商品需要擦亮',
|
|
'total': 0,
|
|
'polished': 0,
|
|
'failed': 0,
|
|
'results': [],
|
|
}
|
|
|
|
total = len(items)
|
|
polished = 0
|
|
failed = 0
|
|
results = []
|
|
|
|
for index, item in enumerate(items):
|
|
item_id = item.get('id', '')
|
|
if not item_id:
|
|
continue
|
|
|
|
result = await self.polish_item(item_id)
|
|
results.append(result)
|
|
|
|
if result.get('success'):
|
|
polished += 1
|
|
else:
|
|
failed += 1
|
|
|
|
logger.info(f"【{self.runtime.cookie_id}】擦亮进度: {index + 1}/{total}, 成功: {polished}, 失败: {failed}")
|
|
|
|
if index < total - 1:
|
|
await asyncio.sleep(random.uniform(1, 3))
|
|
|
|
logger.info(f"【{self.runtime.cookie_id}】擦亮完成: 总计 {total}, 成功 {polished}, 失败 {failed}")
|
|
return {
|
|
'success': True,
|
|
'total': total,
|
|
'polished': polished,
|
|
'failed': failed,
|
|
'results': results,
|
|
}
|