1919import time
2020import re
2121
22+ from requests .adapters import HTTPAdapter
23+ from urllib3 .util .retry import Retry
2224from . import types
2325from .common import exceptions , utils , enums
2426
@@ -104,9 +106,6 @@ def __init__(self, golden_key: str, user_agent: str | None = None,
104106 self .last_update : int | None = None
105107 """Последнее время обновления аккаунта."""
106108
107- self .interlocutor_ids : dict [int , int ] = {}
108- """{id чата: id собеседника}"""
109-
110109 self .__initiated : bool = False
111110
112111 self .__saved_chats : dict [int , types .ChatShortcut ] = {}
@@ -127,6 +126,19 @@ def __init__(self, golden_key: str, user_agent: str | None = None,
127126 """Если сообщение начинается с этого символа, значит оно отправлено ботом."""
128127 self .__old_bot_character = ""
129128 """Старое значение self.__bot_character, для корректной маркировки отправки ботом старых сообщений"""
129+ self .session = requests .Session ()
130+ retry_strategy = Retry (
131+ total = 6 ,
132+ connect = 6 ,
133+ read = 6 ,
134+ redirect = 6 ,
135+ status = 6 ,
136+ backoff_factor = 1 ,
137+ status_forcelist = [500 , 502 , 503 , 504 ],
138+ allowed_methods = {"GET" , "POST" }
139+ )
140+ adapter = HTTPAdapter (max_retries = retry_strategy )
141+ self .session .mount ("https://" , adapter )
130142
131143 def method (self , request_method : Literal ["post" , "get" ], api_method : str , headers : dict , payload : Any ,
132144 exclude_phpsessid : bool = False , raise_not_200 : bool = False ,
@@ -187,20 +199,42 @@ def update_locale(redirect_url: str):
187199 locale = locale or self .__set_locale
188200 if request_method == "get" and locale and locale != self .locale :
189201 link += f'{ "&" if "?" in link else "?" } setlocale={ locale } '
190- for i in range (10 ):
191- response = getattr (requests , request_method )(link , headers = headers , data = payload ,
192- timeout = self .requests_timeout ,
193- proxies = self .proxy or {}, allow_redirects = False )
194- if not (300 <= response .status_code < 400 ) or 'Location' not in response .headers :
202+ kwargs = {"method" : request_method ,
203+ "headers" : headers ,
204+ "timeout" : self .requests_timeout ,
205+ "proxies" : self .proxy or {}}
206+ i = 0
207+ response = None
208+ while i < 10 or response .status_code == 429 :
209+ response = self .session .request (url = link , data = payload , allow_redirects = False , ** kwargs )
210+ if response .status_code == 429 :
211+ self .last_429_err_time = time .time ()
212+ time .sleep (min (2 ** i , 30 ))
213+ continue
214+ elif response .status_code == 400 and isinstance (payload , dict ) and "csrf_token" in payload :
215+ content_type = response .headers .get ("Content-Type" )
216+ if content_type and "application/json" in content_type :
217+ d = response .json ()
218+ if d .get ("error" ) == 1 and d .get ("msg" ) in ("Оновіть сторінку та повторіть спробу." ,
219+ "Обновите страницу и повторите попытку." ,
220+ "Please refresh your page and try again." ):
221+ while payload ["csrf_token" ] == self .csrf_token :
222+ try :
223+ self .get ()
224+ except :
225+ logger .warning ("Произошла ошибка при обновлении данных аккаунта" )
226+ logger .debug ("TRACEBACK" , exc_info = True )
227+ time .sleep (2 )
228+ payload ["csrf_token" ] = self .csrf_token
229+ continue
230+ break
231+ elif not (300 <= response .status_code < 400 ) or 'Location' not in response .headers :
195232 break
196233 link = response .headers ['Location' ]
197234 update_locale (link )
235+ i += 1
198236 else :
199- response = getattr (requests , request_method )(link , headers = headers , data = payload ,
200- timeout = self .requests_timeout ,
201- proxies = self .proxy or {})
202- if response .status_code == 429 :
203- self .last_429_err_time = time .time ()
237+ response = self .session .request (url = link , data = payload , allow_redirects = True , ** kwargs )
204238
205239 if response .status_code == 403 :
206240 raise exceptions .UnauthorizedError (response )
@@ -493,6 +527,52 @@ def get_balance(self, lot_id: int) -> types.Balance:
493527 float (balances ["data-balance-total-eur" ]), float (balances ["data-balance-eur" ]))
494528 return balance
495529
530+ # def get_withdraw_payment_data(self) -> types.WithdrawPaymentData:
531+ # if not self.is_initiated:
532+ # raise exceptions.AccountNotInitiatedError()
533+ # response = self.method("get", f"account/balance", {"accept": "*/*"}, {}, raise_not_200=True)
534+ # html_response = response.content.decode()
535+ # parser = BeautifulSoup(html_response, "lxml")
536+ #
537+ # username = parser.find("div", {"class": "user-link-name"})
538+ # if not username:
539+ # raise exceptions.UnauthorizedError(response)
540+ #
541+ # self.__update_csrf_token(parser)
542+ # data_data = parser.find("div", class_="withdraw-box").get("data-data")
543+ # parsed_data = json.loads(html.unescape(data_data))
544+ # ext_currencies = {}
545+ # for ext_currency_name, ext_currency_data in parsed_data["extCurrencies"].items():
546+ # wallet_info = WithdrawWalletInfo(
547+ # ext_currency=ext_currency_name,
548+ # name=ext_currency_data["name"],
549+ # unit=ext_currency_data["unit"],
550+ # wallet_name=ext_currency_data["walletName"],
551+ # wallets=ext_currency_data["wallets"]
552+ # )
553+ # ext_currencies[ext_currency_name] = wallet_info
554+ #
555+ # currencies = {}
556+ # for currency_name, currency_data in parsed_data["currencies"].items():
557+ # channels = []
558+ # for channel_data in currency_data["channels"]:
559+ # ext_currency = channel_data["extCurrency"]
560+ # wallet_info = ext_currencies.get(ext_currency)
561+ # channel_info = WithdrawMethod(
562+ # name=channel_data["name"],
563+ # ext_currency=ext_currency,
564+ # fee_info=channel_data["feeInfo"],
565+ # wallet_info=wallet_info
566+ # )
567+ # channels.append(channel_info)
568+ # currency = parse_currency(currency_data["unit"])
569+ # currency_info = WithdrawCurrencyInfo(
570+ # currency=currency,
571+ # channels=channels
572+ # )
573+ # currencies[currency] = currency_info
574+ # return WithdrawPaymentData(ext_currencies=ext_currencies, currencies=currencies)
575+
496576 def get_chat_history (self , chat_id : int | str , last_message_id : int = 99999999999999999999999 ,
497577 interlocutor_username : Optional [str ] = None , from_id : int = 0 ) -> list [types .Message ]:
498578 """
@@ -541,12 +621,10 @@ def get_chat_history(self, chat_id: int | str, last_message_id: int = 9999999999
541621 return self .__parse_messages (json_response ["chat" ]["messages" ], chat_id , interlocutor_id ,
542622 interlocutor_username , from_id )
543623
544- def get_chats_histories (self , chats_data : dict [int | str , str | None ],
545- interlocutor_ids : list [int ] | None = None ) -> dict [int , list [types .Message ]]:
624+ def get_chats_histories (self , chats_data : dict [int | str , str | None ]) -> dict [int , list [types .Message ]]:
546625 """
547626 Получает историю сообщений сразу нескольких чатов
548627 (до 50 сообщений на личный чат, до 25 сообщений на публичный чат).
549- Прокидывает в Account.runner информацию о том, какие лоты смотрят cобеседники (interlocutor_ids).
550628
551629 :param chats_data: ID чатов и никнеймы собеседников (None, если никнейм неизвестен)\n
552630 Например: {48392847: "SLLMK", 58392098: "Amongus", 38948728: None}
@@ -562,12 +640,8 @@ def get_chats_histories(self, chats_data: dict[int | str, str | None],
562640 }
563641 chats = [{"type" : "chat_node" , "id" : i , "tag" : "00000000" ,
564642 "data" : {"node" : i , "last_message" : - 1 , "content" : "" }} for i in chats_data ]
565- buyers = [{"type" : "c-p-u" ,
566- "id" : str (buyer ),
567- "tag" : utils .random_tag (),
568- "data" : False } for buyer in interlocutor_ids or []]
569643 payload = {
570- "objects" : json .dumps ([ * chats , * buyers ] ),
644+ "objects" : json .dumps (chats ),
571645 "request" : False ,
572646 "csrf_token" : self .csrf_token
573647 }
@@ -576,10 +650,7 @@ def get_chats_histories(self, chats_data: dict[int | str, str | None],
576650
577651 result = {}
578652 for i in json_response ["objects" ]:
579- if i .get ("type" ) == "c-p-u" :
580- bv = self .parse_buyer_viewing (i )
581- self .runner .buyers_viewing [bv .buyer_id ] = bv
582- elif i .get ("type" ) == "chat_node" :
653+ if i .get ("type" ) == "chat_node" :
583654 if not i .get ("data" ):
584655 result [i .get ("id" )] = []
585656 continue
@@ -1715,7 +1786,8 @@ def get_lot_fields(self, lot_id: int) -> types.LotFields:
17151786 if error_message :
17161787 raise exceptions .LotParsingError (response , error_message .text , lot_id )
17171788 result = {}
1718- result .update ({field ["name" ]: field .get ("value" ) or "" for field in bs .find_all ("input" )})
1789+ result .update (
1790+ {field ["name" ]: field .get ("value" ) or "" for field in bs .find_all ("input" ) if field ["name" ] != "query" })
17191791 result .update ({field ["name" ]: field .text or "" for field in bs .find_all ("textarea" )})
17201792 result .update ({
17211793 field ["name" ]: field .find ("option" , selected = True )["value" ]
@@ -1766,16 +1838,15 @@ def save_offer(self, offer_fields: types.LotFields | types.ChipFields):
17661838 "x-requested-with" : "XMLHttpRequest" ,
17671839 }
17681840 offer_fields .csrf_token = self .csrf_token
1841+ fields = offer_fields .renew_fields ().fields
17691842
17701843 if isinstance (offer_fields , types .LotFields ):
17711844 id_ = offer_fields .lot_id
1772- fields = offer_fields .renew_fields ().fields
1773- fields ["location" ] = "trade"
17741845 api_method = "lots/offerSave"
17751846 else :
17761847 id_ = offer_fields .subcategory_id
1777- fields = offer_fields .renew_fields ().fields
17781848 api_method = "chips/saveOffers"
1849+
17791850 response = self .method ("post" , api_method , headers , fields , raise_not_200 = True )
17801851 json_response = response .json ()
17811852 errors_dict = {}
@@ -1980,7 +2051,7 @@ def __parse_messages(self, json_messages: dict, chat_id: int | str,
19802051 messages = []
19812052 ids = {self .id : self .username , 0 : "FunPay" }
19822053 badges = {}
1983- if interlocutor_id is not None :
2054+ if None not in ( interlocutor_id , interlocutor_username ) :
19842055 ids [interlocutor_id ] = interlocutor_username
19852056
19862057 for i in json_messages :
@@ -1998,9 +2069,12 @@ def __parse_messages(self, json_messages: dict, chat_id: int | str,
19982069 if ids .get (author_id ) is None :
19992070 author = author_div .find ("a" ).text .strip ()
20002071 ids [author_id ] = author
2001- if self .chat_id_private (chat_id ) and author_id == interlocutor_id and not interlocutor_username :
2002- interlocutor_username = author
2003- ids [interlocutor_id ] = interlocutor_username
2072+ if self .chat_id_private (chat_id ):
2073+ if author_id == interlocutor_id and not interlocutor_username :
2074+ interlocutor_username = author
2075+ elif interlocutor_username == author and not interlocutor_id :
2076+ interlocutor_id = author_id
2077+
20042078 by_bot = False
20052079 by_vertex = False
20062080 image_name = None
@@ -2041,6 +2115,7 @@ def __parse_messages(self, json_messages: dict, chat_id: int | str,
20412115 for i in messages :
20422116 i .author = ids .get (i .author_id )
20432117 i .chat_name = interlocutor_username
2118+ i .interlocutor_id = interlocutor_id
20442119 i .badge = badges .get (i .author_id ) if badges .get (i .author_id ) != 0 else None
20452120 parser = BeautifulSoup (i .html , "lxml" )
20462121 if i .badge :
@@ -2109,7 +2184,7 @@ def __update_csrf_token(self, parser: BeautifulSoup):
21092184 logger .debug ("TRACEBACK" , exc_info = True )
21102185
21112186 @staticmethod
2112- def parse_buyer_viewing (json_responce : dict ) -> types .BuyerViewing :
2187+ def __parse_buyer_viewing (json_responce : dict ) -> types .BuyerViewing :
21132188 buyer_id = json_responce .get ("id" )
21142189 if not json_responce ["data" ]:
21152190 return types .BuyerViewing (buyer_id , None , None , None , None )
0 commit comments