Files
Sipro-Stocks/marketplaces/wildberries.py

99 lines
3.8 KiB
Python

import asyncio
import json
import logging
from typing import Union
import jwt
import utils
from database import Marketplace
from limiter import BatchLimiter
from marketplaces.base import BaseMarketplaceApi
class WildberriesMarketplaceApi(BaseMarketplaceApi):
def __init__(self, marketplace: Marketplace):
self.marketplace = marketplace
auth_data = json.loads(marketplace.auth_data)
token = auth_data.get('token')
self.is_valid = True
try:
decoded_token = jwt.decode(token, algorithms=["HS256"], options={"verify_signature": False})
except Exception:
logging.error(f"Couldn't decode token for {marketplace.id}")
self.is_valid = False
return
self.limiter_key = str(marketplace.company_id) + str(decoded_token.get('sid'))
self.headers = {
'Authorization': token,
'Content-Type': 'application/json',
'User-Agent': 'wbas_seller.denco.store3547'
}
def get_headers(self):
return self.headers
@property
def api_url(self):
return 'https://marketplace-api.wildberries.ru'
def _filter_chunk_with_conflict(self, chunk: dict, response: list):
if not isinstance(response, list):
return chunk
filter_skus = []
for error in response:
for sku in error.get('data', []):
filter_skus.append(sku['sku'])
return list(filter(lambda x: x['sku'] not in filter_skus, chunk))
async def update_stocks(self, data: Union[list, dict]):
if type(data) is not list:
return
if not self.is_valid:
logging.warning(f'Skipping marketplace [{self.marketplace.id}] because of invalid token')
return
max_stocks = 1000
chunks = list(utils.chunk_list(data, max_stocks))
if not chunks:
return
self.init_session()
limiter = BatchLimiter()
max_retries = 10
while chunks:
current_retry = 0
chunk = chunks.pop()
while current_retry <= max_retries:
try:
await limiter.acquire_wildberries(self.limiter_key)
request_data = {'stocks': chunk}
response = await self._method('PUT', f'/api/v3/stocks/{self.marketplace.warehouse_id}',
data=request_data)
current_retry += 1
if (response.status not in [204, 409, 429]):
response = await response.json()
error_message = response.get('message')
error_code = response.get('code')
logging.warning(
f'Error occurred when sending stocks to [{self.marketplace.id}]: {error_message} ({error_code})')
break
if response.status == 429:
logging.warning(f'WB rate limit exceeded for marketplace [{self.marketplace.id}]')
await asyncio.sleep(1)
continue
if response.status == 409:
response_data = await response.json()
logging.warning(
f'Conflict occurred when sending stocks to [{self.marketplace.id}]')
await asyncio.sleep(1)
chunk = self._filter_chunk_with_conflict(chunk, response_data)
continue
await asyncio.sleep(0.2)
break
except Exception as e:
logging.error(
f'Exception occurred while sending stocks to marketplace ID [{self.marketplace.id}]: {str(e)}')
break
await self.session.close()