Skip to content

Commit

Permalink
v0.1.15.3 - отримання курсу валют
Browse files Browse the repository at this point in the history
покращення роботи з локалізацією
Account.balance
  • Loading branch information
sidor0912 committed Dec 16, 2024
1 parent 54d9df2 commit 5e5c66c
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 51 deletions.
117 changes: 74 additions & 43 deletions FunPayAPI/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from typing import TYPE_CHECKING, Literal, Any, Optional, IO

import FunPayAPI.common.enums
from FunPayAPI.common.utils import parse_currency
from FunPayAPI.common.utils import parse_currency, RegularExpressions
from .types import PaymentMethod, CalcResult

if TYPE_CHECKING:
Expand Down Expand Up @@ -69,26 +69,28 @@ def __init__(self, golden_key: str, user_agent: str | None = None,
"""Активные продажи."""
self.active_purchases: int | None = None
"""Активные покупки."""
self.__locale: Literal["ru", "en", "uk"] | None = locale
self.__locale: Literal["ru", "en", "uk"] | None = None
"""Текущий язык аккаунта."""
self.__default_locale: Literal["ru", "en", "uk"] | None = locale
"""Язык аккаунта по умолчанию."""
self.__profile_parse_locale: Literal["ru", "en", "uk"] | None = locale
"""Язык по умолчанию для Account.get_user()"""
self.__chat_parse_locale: Literal["ru", "en", "uk"] | None = locale
self.__chat_parse_locale: Literal["ru", "en", "uk"] | None = None
"""Язык по умолчанию для Account.get_chat()"""
# self.__sales_parse_locale: Literal["ru", "en", "uk"] | None = locale #todo
"""Язык по умолчанию для Account.get_sales()"""
self.__order_parse_locale: Literal["ru", "en", "uk"] | None = locale
self.__order_parse_locale: Literal["ru", "en", "uk"] | None = None
"""Язык по умолчанию для Account.get_order()"""
self.__lots_parse_locale: Literal["ru", "en", "uk"] | None = locale
self.__lots_parse_locale: Literal["ru", "en", "uk"] | None = None
"""Язык по умолчанию для Account.get_subcategory_public_lots()"""
self.__subcategories_parse_locale: Literal["ru", "en", "uk"] | None = locale
self.__subcategories_parse_locale: Literal["ru", "en", "uk"] | None = None
"""Язык по для получения названий разделов."""
self.__set_locale: Literal["ru", "en", "uk"] | None = None
"""Язык, на который будет переведем аккаунт при следующем GET-запросе."""
self.currency: FunPayAPI.types.Currency = FunPayAPI.types.Currency.UNKNOWN
"""Валюта аккаунта"""
self.total_balance: int | None = None
"""Примерный общий баланс аккаунта в валюте аккаунта."""
self.csrf_token: str | None = None
"""CSRF токен."""
self.phpsessid: str | None = None
Expand Down Expand Up @@ -173,20 +175,15 @@ def update_locale(redirect_url: str):
link = normalize_url(api_method, locale)
else:
link = normalize_url(api_method)
if request_method == "get" and self.__set_locale:
if self.__set_locale != self.locale:
link += f'{"&" if "?" in link else "?"}setlocale={self.__set_locale}'
else:
self.__set_locale = None

locale = locale or self.__set_locale
if request_method == "get" and locale and locale != self.locale:
link += f'{"&" if "?" in link else "?"}setlocale={self.__set_locale}'
response = getattr(requests, request_method)(link, headers=headers, data=payload,
timeout=self.requests_timeout,
proxies=self.proxy or {}, allow_redirects=False)
if 'Location' in response.headers:
redirect_url = response.headers['Location']
update_locale(redirect_url)
if self.locale == self.__set_locale:
self.__set_locale = None
response = getattr(requests, request_method)(redirect_url, headers=headers, data=payload,
timeout=self.requests_timeout,
proxies=self.proxy or {})
Expand Down Expand Up @@ -218,16 +215,21 @@ def get(self, update_phpsessid: bool = True) -> Account:
username = parser.find("div", {"class": "user-link-name"})
if not username:
raise exceptions.UnauthorizedError(response)

self.username = username.text
self.app_data = json.loads(parser.find("body").get("data-app-data"))
self.__locale = self.app_data.get("locale")
self.id = self.app_data["userId"]
self.csrf_token = self.app_data["csrf-token"]
self.locale = self.app_data.get("locale")
self._logout_link = parser.find("a", class_="menu-item-logout").get("href")
active_sales = parser.find("span", {"class": "badge badge-trade"})
self.active_sales = int(active_sales.text) if active_sales else 0

balance = parser.find("span", class_="badge badge-balance")
if balance:
balance, currency = balance.text.rsplit(" ", maxsplit=1)
self.total_balance = int(balance.replace(" ", ""))
self.currency = parse_currency(currency)
else:
self.total_balance = 0
active_purchases = parser.find("span", {"class": "badge badge-orders"})
self.active_purchases = int(active_purchases.text) if active_purchases else 0

Expand Down Expand Up @@ -262,9 +264,9 @@ def get_subcategory_public_lots(self, subcategory_type: enums.SubCategoryTypes,
meth = f"lots/{subcategory_id}/" if subcategory_type is enums.SubCategoryTypes.COMMON else f"chips/{subcategory_id}/"
if not locale:
locale = self.__lots_parse_locale
self.locale = locale
response = self.method("get", meth, {"accept": "*/*"}, {}, raise_not_200=True)
self.locale = self.__default_locale
response = self.method("get", meth, {"accept": "*/*"}, {}, raise_not_200=True, locale=locale)
if locale:
self.locale = self.__default_locale
html_response = response.content.decode()
parser = BeautifulSoup(html_response, "lxml")

Expand Down Expand Up @@ -346,9 +348,9 @@ def get_my_subcategory_lots(self, subcategory_id: int,
meth = f"lots/{subcategory_id}/trade"
if not locale:
locale = self.__lots_parse_locale
self.locale = locale
response = self.method("get", meth, {"accept": "*/*"}, {}, raise_not_200=True)
self.locale = self.__default_locale
response = self.method("get", meth, {"accept": "*/*"}, {}, raise_not_200=True, locale=locale)
if locale:
self.locale = self.__default_locale
html_response = response.content.decode()
parser = BeautifulSoup(html_response, "lxml")

Expand Down Expand Up @@ -399,9 +401,7 @@ def get_lot_page(self, lot_id: int, locale: Literal["ru", "en", "uk"] | None = N
headers = {
"accept": "*/*"
}
if locale:
self.locale = locale
response = self.method("get", f"lots/offer?id={lot_id}", headers, {}, raise_not_200=True)
response = self.method("get", f"lots/offer?id={lot_id}", headers, {}, raise_not_200=True, locale=locale)
if locale:
self.locale = self.__default_locale
html_response = response.content.decode()
Expand Down Expand Up @@ -884,12 +884,6 @@ def withdraw(self, currency: enums.Currency, wallet: enums.Wallet, amount: int |
if not self.is_initiated:
raise exceptions.AccountNotInitiatedError()

currencies = {
enums.Currency.RUB: "rub",
enums.Currency.USD: "usd",
enums.Currency.EUR: "eur"
}

wallets = {
enums.Wallet.QIWI: "qiwi",
enums.Wallet.YOUMONEY: "fps",
Expand All @@ -906,7 +900,7 @@ def withdraw(self, currency: enums.Currency, wallet: enums.Wallet, amount: int |
}
payload = {
"csrf_token": self.csrf_token,
"currency_id": currencies[currency],
"currency_id": currency.code,
"ext_currency_id": wallets[wallet],
"wallet": address,
"amount_int": str(amount)
Expand Down Expand Up @@ -1024,9 +1018,9 @@ def get_user(self, user_id: int, locale: Literal["ru", "en", "uk"] | None = None
raise exceptions.AccountNotInitiatedError()
if not locale:
locale = self.__profile_parse_locale
self.locale = locale
response = self.method("get", f"users/{user_id}/", {"accept": "*/*"}, {}, raise_not_200=True)
self.locale = self.__default_locale
response = self.method("get", f"users/{user_id}/", {"accept": "*/*"}, {}, raise_not_200=True, locale=locale)
if locale:
self.locale = self.__default_locale
html_response = response.content.decode()
parser = BeautifulSoup(html_response, "lxml")

Expand Down Expand Up @@ -1103,9 +1097,9 @@ def get_chat(self, chat_id: int, with_history: bool = True,

if not locale:
locale = self.__chat_parse_locale
self.locale = locale
response = self.method("get", f"chat/?node={chat_id}", {"accept": "*/*"}, {}, raise_not_200=True)
self.locale = self.__default_locale
response = self.method("get", f"chat/?node={chat_id}", {"accept": "*/*"}, {}, raise_not_200=True, locale=locale)
if locale:
self.locale = self.__default_locale
html_response = response.content.decode()
parser = BeautifulSoup(html_response, "lxml")
if (name := parser.find("div", {"class": "chat-header"}).find("div", {"class": "media-user-name"}).find(
Expand Down Expand Up @@ -1153,9 +1147,9 @@ def get_order(self, order_id: str, locale: Literal["ru", "en", "uk"] | None = No
}
if not locale:
locale = self.__order_parse_locale
self.locale = locale
response = self.method("get", f"orders/{order_id}/", headers, {}, raise_not_200=True)
self.locale = self.__default_locale
response = self.method("get", f"orders/{order_id}/", headers, {}, raise_not_200=True, locale=locale)
if locale:
self.locale = self.__default_locale
html_response = response.content.decode()
parser = BeautifulSoup(html_response, "lxml")
username = parser.find("div", {"class": "user-link-name"})
Expand Down Expand Up @@ -1676,6 +1670,43 @@ def delete_lot(self, lot_id: int) -> None:
"""
self.save_lot(types.LotFields(lot_id, {"csrf_token": self.csrf_token, "offer_id": lot_id, "deleted": "1"}))

def get_exchange_rate(self, currency: types.Currency) -> tuple[float, types.Currency]:
"""
Получает курс обмена текущей валюты аккаунта на переданную, обновляет валюту аккаунта.
Возвращает X, где X <currency> = 1 <валюта аккаунта> и текущую валюту аккаунта.
:param currency: Валюта, на которую нужно получить курс обмена.
:type currency: :obj:`types.Currency`
:return: Кортеж, содержащий коэффициент обмена и текущую валюту аккаунта.
:rtype: :obj:`tuple[float, types.Currency]`
"""
r = self.method("post", "https://funpay.com/account/switchCurrency",
{"accept": "*/*", "content-type": "application/x-www-form-urlencoded; charset=UTF-8",
"x-requested-with": "XMLHttpRequest"},
{"cy": currency.code, "csrf_token": self.csrf_token, "confirmed": "false"},
raise_not_200=True)
b = json.loads(r.text)
if "url" in b and not b["url"]:
self.currency = currency
return 1, currency
else:
s = BeautifulSoup(b["modal"], "lxml").find("p", class_="lead").text.replace("\xa0", " ")
match = RegularExpressions().EXCHANGE_RATE.fullmatch(s)
assert match is not None
swipe_to = match.group(2)
assert swipe_to.lower() == currency.code
price1 = float(match.group(4))
currency1 = parse_currency(match.group(5))
price2 = float(match.group(7))
currency2 = parse_currency(match.group(8))
now_currency = ({currency1, currency2} - {currency, }).pop()
self.currency = now_currency
if now_currency == currency1:
return price2 / price1, now_currency
else:
return price1 / price2, now_currency

def get_category(self, category_id: int) -> types.Category | None:
"""
Возвращает объект категории (игры).
Expand Down Expand Up @@ -1951,5 +1982,5 @@ def locale(self) -> Literal["ru", "en", "uk"] | None:

@locale.setter
def locale(self, new_locale: Literal["ru", "en", "uk"]):
if self.locale != new_locale and new_locale in ("ru", "en", "uk"):
if self.__locale != new_locale and new_locale in ("ru", "en", "uk"):
self.__set_locale = new_locale
11 changes: 10 additions & 1 deletion FunPayAPI/common/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ class MessageTypes(Enum):
"""Администратор A вернул деньги покупателю X по заказу #Y."""



class OrderStatuses(Enum):
"""
В данном классе перечислены все состояния заказов.
Expand Down Expand Up @@ -130,6 +129,16 @@ def __str__(self):
return "€"
return "¤"

@property
def code(self) -> str:
if self == Currency.USD:
return "usd"
elif self == Currency.RUB:
return "rub"
elif self == Currency.EUR:
return "eur"
raise Exception("Неизвестная валюта.")


class Wallet(Enum):
"""
Expand Down
6 changes: 6 additions & 0 deletions FunPayAPI/common/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,3 +215,9 @@ def __init__(self):
"""
Скомпилированное регулярное выражение, описывающее запись кол-ва товаров в заказе.
"""

self.EXCHANGE_RATE = re.compile(
r"(You will receive payment in|Вы начнёте получать оплату в|Ви почнете одержувати оплату в)\s*(USD|RUB|EUR)\.\s*(Your offers prices will be calculated based on the exchange rate:|Цены ваших предложений будут пересчитаны по курсу|Ціни ваших пропозицій будуть перераховані за курсом)\s*([\d.,]+)\s*(₽|€|\$)\s*(за|for)\s*([\d.,]+)\s*(₽|€|\$)\.")
"""
Скомпилированное регулярное выражение, описывающее фразу о смене валюты.
"""
8 changes: 5 additions & 3 deletions FunPayAPI/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -1183,16 +1183,18 @@ def __init__(self, subcategory_type: SubCategoryTypes, subcategory_id: int, meth
"""Валюта аккаунта"""

@property
def commission_coefficient(self) -> float | None:
def commission_coefficient(self) -> float:
"""Отношение цены с комиссией к цене без комиссии."""
if self.min_price_with_commission:
return self.min_price_with_commission / self.price
else:
res = min(filter(lambda x: x.currency == self.account_currency,
self.methods), key=lambda x: x.price, default=None)
return None if not res else res.price / self.price
if not res:
raise Exception("Невозможно определить коэффициент комиссии.")
return res.price / self.price

@property
def commission_percent(self) -> float | None:
def commission_percent(self) -> float:
"""Процент комиссии."""
return (self.commission_coefficient - 1) * 100
Loading

0 comments on commit 5e5c66c

Please sign in to comment.