Skip to content

Commit 10e3ee1

Browse files
committed
v0.1.16.7 - requests.Session, payload["csrf_token"], upload_backup, install-fpc.sh #17 fix
1 parent 8aceddf commit 10e3ee1

File tree

13 files changed

+219
-129
lines changed

13 files changed

+219
-129
lines changed

FunPayAPI/account.py

Lines changed: 109 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import time
2020
import re
2121

22+
from requests.adapters import HTTPAdapter
23+
from urllib3.util.retry import Retry
2224
from . import types
2325
from .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

Comments
 (0)