-
Notifications
You must be signed in to change notification settings - Fork 375
Expand file tree
/
Copy pathitem_polish_module.py
More file actions
193 lines (155 loc) · 7.3 KB
/
Copy pathitem_polish_module.py
File metadata and controls
193 lines (155 loc) · 7.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
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,
}