fpx-engine 0.7.2__tar.gz → 0.7.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/PKG-INFO +1 -1
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/_api/_client.py +22 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/_parsers/__init__.py +2 -1
- fpx_engine-0.7.4/fpx/_parsers/_category.py +39 -0
- fpx_engine-0.7.4/fpx/classes/account/subclasses/addons.py +64 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/classes/account/subclasses/category.py +55 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/classes/runner/subclasses/router.py +19 -2
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/models/account.py +22 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx_engine.egg-info/PKG-INFO +1 -1
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx_engine.egg-info/SOURCES.txt +1 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/pyproject.toml +1 -1
- fpx_engine-0.7.2/fpx/classes/account/subclasses/addons.py +0 -29
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/LICENSE +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/README.md +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/__init__.py +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/_parsers/_base.py +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/_parsers/_chats.py +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/_parsers/_lots.py +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/_parsers/_orders.py +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/_parsers/_profile.py +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/classes/account/account.py +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/classes/account/subclasses/chat.py +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/classes/account/subclasses/editor.py +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/classes/account/subclasses/lot.py +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/classes/account/subclasses/order.py +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/classes/account/subclasses/profile.py +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/classes/account/subclasses/review.py +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/classes/runner/runner.py +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/classes/runner/subclasses/_category.py +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/classes/runner/subclasses/_chat.py +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/classes/runner/subclasses/_order.py +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/classes/runner/subclasses/_review.py +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/fsm/__init__.py +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/fsm/fsm.py +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/main.py +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/middlewares/_request_engine.py +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/models/__init__.py +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/models/chat.py +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/models/lots.py +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/py.typed +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/services/__init__.py +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/utils/dependencies.py +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/utils/errors.py +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/utils/storage/__init__.py +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/utils/storage/base.py +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/utils/storage/file.py +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/utils/storage/memory.py +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx/utils/storage/redis.py +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx_engine.egg-info/dependency_links.txt +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx_engine.egg-info/requires.txt +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/fpx_engine.egg-info/top_level.txt +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/setup.cfg +0 -0
- {fpx_engine-0.7.2 → fpx_engine-0.7.4}/tests/test_happy_path.py +0 -0
|
@@ -187,3 +187,25 @@ class FunPayClient:
|
|
|
187
187
|
headers=headers
|
|
188
188
|
)
|
|
189
189
|
return r.json()
|
|
190
|
+
|
|
191
|
+
async def find_category(self, target):
|
|
192
|
+
headers = {
|
|
193
|
+
"X-Requested-With": "XMLHttpRequest"
|
|
194
|
+
}
|
|
195
|
+
r = await self.client.request(
|
|
196
|
+
'POST', '/games/promoFilter',
|
|
197
|
+
data={'query': target},
|
|
198
|
+
headers=headers
|
|
199
|
+
)
|
|
200
|
+
return r.json()
|
|
201
|
+
|
|
202
|
+
async def calc_category_price(self, price, node_id):
|
|
203
|
+
headers = {
|
|
204
|
+
"X-Requested-With": "XMLHttpRequest"
|
|
205
|
+
}
|
|
206
|
+
r = await self.client.request(
|
|
207
|
+
'POST', '/lots/calc',
|
|
208
|
+
data={'nodeId': node_id, 'price': price},
|
|
209
|
+
headers=headers
|
|
210
|
+
)
|
|
211
|
+
return r.json()
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
from ._category import CategoryParser
|
|
1
2
|
from ._chats import ChatParser
|
|
2
3
|
from ._lots import LotParser
|
|
3
4
|
from ._orders import OrderParser
|
|
4
5
|
from ._profile import ProfileParser
|
|
5
6
|
|
|
6
7
|
|
|
7
|
-
class FpxParser(ChatParser, LotParser, OrderParser, ProfileParser):
|
|
8
|
+
class FpxParser(ChatParser, LotParser, OrderParser, ProfileParser, CategoryParser):
|
|
8
9
|
'''
|
|
9
10
|
Главный парсер fpx
|
|
10
11
|
Собирает внутри себя методы из отдельных модулей.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from bs4 import BeautifulSoup
|
|
4
|
+
|
|
5
|
+
from fpx.models.account import Game, GameSubCategory, GameTitle
|
|
6
|
+
|
|
7
|
+
from ._base import BaseParser
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger("fpx.category_parser")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CategoryParser(BaseParser):
|
|
13
|
+
|
|
14
|
+
@classmethod
|
|
15
|
+
def parse_all_categories(cls, html_content):
|
|
16
|
+
'''Парсит главную страницу фп на лоты'''
|
|
17
|
+
soup = BeautifulSoup(html_content, 'html.parser')
|
|
18
|
+
items = soup.find_all('div', class_='promo-game-item')
|
|
19
|
+
result = []
|
|
20
|
+
for item in items:
|
|
21
|
+
game_title = item.find('div', class_='game-title')
|
|
22
|
+
game_name = game_title.get_text(strip=True)
|
|
23
|
+
data_id = game_title.get('data-id')
|
|
24
|
+
subcategory_str = item.find('ul', class_='list-inline')
|
|
25
|
+
subcategory_list = subcategory_str.find_all('li')
|
|
26
|
+
subcategories = []
|
|
27
|
+
for s in subcategory_list:
|
|
28
|
+
a = s.find('a')
|
|
29
|
+
href = a.get('href')
|
|
30
|
+
category_id = href.split('/')[-2]
|
|
31
|
+
name = s.get_text()
|
|
32
|
+
subcategories.append(GameSubCategory(id=category_id, sub_name=name))
|
|
33
|
+
title = GameTitle(id=data_id, name=game_name)
|
|
34
|
+
result.append(Game(title=title, subcategories=subcategories))
|
|
35
|
+
return result
|
|
36
|
+
|
|
37
|
+
@classmethod
|
|
38
|
+
def parse_category_browser(cls, html_content):
|
|
39
|
+
BeautifulSoup(html_content, 'html.parser')
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
|
|
2
|
+
from fpx.models.account import Calc
|
|
3
|
+
from fpx.utils import errors as fpx_err
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class AddonsManager:
|
|
7
|
+
def __init__(self, account):
|
|
8
|
+
self._account = account
|
|
9
|
+
|
|
10
|
+
async def get_game_id(self, category_id: str):
|
|
11
|
+
"""
|
|
12
|
+
Получает game_id.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
category_id (str | int): ID подкатегории.
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
str | int: ID игры.
|
|
19
|
+
|
|
20
|
+
Raises:
|
|
21
|
+
FpxGetGameIDError: Ошибка запроса ID игры
|
|
22
|
+
"""
|
|
23
|
+
try:
|
|
24
|
+
stage = 'запроса данных категории с FunPay'
|
|
25
|
+
html = await self._account._client.lot_menu_by_category(category_id)
|
|
26
|
+
stage = 'парсинга данных'
|
|
27
|
+
data = self._account._parser.parse_lot_menu(html)
|
|
28
|
+
except Exception as e:
|
|
29
|
+
raise fpx_err.FpxGetGameIDError(f'При выполнении {stage} произошла ошибка: {e}')
|
|
30
|
+
return data
|
|
31
|
+
|
|
32
|
+
async def calc_category_price(self, price, node_id):
|
|
33
|
+
'''
|
|
34
|
+
Считает цену в категории с включенной комиссией.
|
|
35
|
+
Args:
|
|
36
|
+
price (str | float | int): Твоя цена до комиссии.
|
|
37
|
+
node_id (str | int): ID категории
|
|
38
|
+
Returns:
|
|
39
|
+
list[Calc]: Список объектов. В каждой итерации содержит:
|
|
40
|
+
- type_name (str): Название типа оплаты(банковская карта, сбп)
|
|
41
|
+
- price (str): Цена с комиссией
|
|
42
|
+
- unit (str): Символ валюты(типа еврики, рубли, доллары $$$)
|
|
43
|
+
- pos (str): Позиция. В 99% не нужный параметр,
|
|
44
|
+
если вам будет нужно, вы уже должны знать
|
|
45
|
+
об этом
|
|
46
|
+
Raises:
|
|
47
|
+
FpxGetGameIDError: Ошибка запроса данных
|
|
48
|
+
'''
|
|
49
|
+
try:
|
|
50
|
+
data = await self._account._client.calc_category_price(price, node_id)
|
|
51
|
+
price_list = data['methods']
|
|
52
|
+
except Exception as e:
|
|
53
|
+
raise fpx_err.FpxRequestError(f'При сборе всех категорий произошла ошибка: {e}')
|
|
54
|
+
calc_list = []
|
|
55
|
+
for price in price_list:
|
|
56
|
+
calc_list.append(
|
|
57
|
+
Calc(
|
|
58
|
+
type_name=price['name'],
|
|
59
|
+
price=price['price'],
|
|
60
|
+
unit=price['unit'],
|
|
61
|
+
pos=price['pos']
|
|
62
|
+
)
|
|
63
|
+
)
|
|
64
|
+
return calc_list
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
+
from fpx.models.account import Game
|
|
3
4
|
from fpx.models.lots import CategoryLastLot
|
|
4
5
|
from fpx.utils import errors as fpx_err
|
|
5
6
|
|
|
@@ -65,3 +66,57 @@ class CategoryManager:
|
|
|
65
66
|
for el in data:
|
|
66
67
|
result.append(CategoryLastLot(category_id=chip_category_id, **el))
|
|
67
68
|
return result
|
|
69
|
+
|
|
70
|
+
async def get_all_categories(self) -> list[Game]:
|
|
71
|
+
'''
|
|
72
|
+
Собирает все категории с фанпей.
|
|
73
|
+
Returns:
|
|
74
|
+
list[Game]: Список объектов, в каждой итерации
|
|
75
|
+
содержит в себе:
|
|
76
|
+
- title (GameTitle):
|
|
77
|
+
Который содержит в себе:
|
|
78
|
+
- id (int): ID категории
|
|
79
|
+
- name (str): Название категории
|
|
80
|
+
|
|
81
|
+
- subcategories (GameSubCategory):
|
|
82
|
+
- id (int): ID подкатегории
|
|
83
|
+
- sub_name (str): Название подкатегории
|
|
84
|
+
Raises:
|
|
85
|
+
FpxRequestError: Ошибка запроса данных
|
|
86
|
+
'''
|
|
87
|
+
try:
|
|
88
|
+
html = await self._account._client.get_main_menu()
|
|
89
|
+
data = self._account._parser.parse_all_categories(html)
|
|
90
|
+
except Exception as e:
|
|
91
|
+
raise fpx_err.FpxRequestError(f'При сборе всех категорий произошла ошибка: {e}')
|
|
92
|
+
return data
|
|
93
|
+
|
|
94
|
+
async def find_category(self, target):
|
|
95
|
+
'''
|
|
96
|
+
Использует встроенный поиск фанпей,
|
|
97
|
+
ищет категории по совпадениям, допустимы
|
|
98
|
+
довольно критичные погрешности.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
target (str): Слово, по которому идет поиск
|
|
102
|
+
Returns:
|
|
103
|
+
list[Game]: Список объектов, в каждой итерации
|
|
104
|
+
содержит в себе:
|
|
105
|
+
- title (GameTitle):
|
|
106
|
+
Который содержит в себе:
|
|
107
|
+
- id (int): ID категории
|
|
108
|
+
- name (str): Название категории
|
|
109
|
+
|
|
110
|
+
- subcategories (GameSubCategory):
|
|
111
|
+
- id (int): ID подкатегории
|
|
112
|
+
- sub_name (str): Название подкатегории
|
|
113
|
+
Raises:
|
|
114
|
+
FpxRequestError: Ошибка запроса данных
|
|
115
|
+
|
|
116
|
+
'''
|
|
117
|
+
try:
|
|
118
|
+
html = await self._account._client.find_category(target)
|
|
119
|
+
data = self._account._parser.parse_all_categories(html['html'])
|
|
120
|
+
except Exception as e:
|
|
121
|
+
raise fpx_err.FpxRequestError(f'При сборе всех категорий произошла ошибка: {e}')
|
|
122
|
+
return data
|
|
@@ -38,10 +38,12 @@ class Router:
|
|
|
38
38
|
|
|
39
39
|
async def invoke(self, h_func, event, state_ctx=None, args=None):
|
|
40
40
|
'''Вызывает хендлер'''
|
|
41
|
+
generators_to_close = []
|
|
41
42
|
async def endpoint(ev):
|
|
42
43
|
sig = inspect.signature(h_func)
|
|
43
44
|
kwargs = {}
|
|
44
45
|
arg_index = 0
|
|
46
|
+
nonlocal generators_to_close
|
|
45
47
|
for param_name, param in sig.parameters.items():
|
|
46
48
|
if param.annotation is not inspect.Parameter.empty and isinstance(ev, param.annotation):
|
|
47
49
|
kwargs[param_name] = ev
|
|
@@ -52,7 +54,15 @@ class Router:
|
|
|
52
54
|
if isinstance(param.default, Dependency):
|
|
53
55
|
dep_func = param.default.dependency
|
|
54
56
|
dep_sig = inspect.signature(dep_func)
|
|
55
|
-
if
|
|
57
|
+
if inspect.isasyncgenfunction(dep_func):
|
|
58
|
+
gen = dep_func(ev)
|
|
59
|
+
try:
|
|
60
|
+
resolved_val = await anext(gen)
|
|
61
|
+
kwargs[param_name] = resolved_val
|
|
62
|
+
generators_to_close.append(gen)
|
|
63
|
+
except StopAsyncIteration:
|
|
64
|
+
pass
|
|
65
|
+
elif len(dep_sig.parameters) == 0:
|
|
56
66
|
if asyncio.iscoroutinefunction(dep_func):
|
|
57
67
|
kwargs[param_name] = await dep_func(ev)
|
|
58
68
|
else:
|
|
@@ -75,7 +85,14 @@ class Router:
|
|
|
75
85
|
return await mw(ev, next_)
|
|
76
86
|
return call
|
|
77
87
|
call_next = await make_next()
|
|
78
|
-
|
|
88
|
+
try:
|
|
89
|
+
await call_next(event)
|
|
90
|
+
finally:
|
|
91
|
+
for gen in generators_to_close:
|
|
92
|
+
try:
|
|
93
|
+
await gen.aclose()
|
|
94
|
+
except Exception:
|
|
95
|
+
pass
|
|
79
96
|
|
|
80
97
|
def include_router(self, router):
|
|
81
98
|
'''Метод для подключения плагинов и сторонних роутеров'''
|
|
@@ -13,6 +13,13 @@ class Balance:
|
|
|
13
13
|
usd: float=0.0
|
|
14
14
|
eur: float=0.0
|
|
15
15
|
|
|
16
|
+
@dataclass
|
|
17
|
+
class Calc:
|
|
18
|
+
type_name: str
|
|
19
|
+
price: str
|
|
20
|
+
unit: str
|
|
21
|
+
pos: str
|
|
22
|
+
|
|
16
23
|
@dataclass
|
|
17
24
|
class CurReview:
|
|
18
25
|
text: str
|
|
@@ -103,3 +110,18 @@ class Review:
|
|
|
103
110
|
text: str
|
|
104
111
|
stars: int
|
|
105
112
|
answer: str
|
|
113
|
+
|
|
114
|
+
@dataclass
|
|
115
|
+
class GameTitle:
|
|
116
|
+
id: int
|
|
117
|
+
name: str
|
|
118
|
+
|
|
119
|
+
@dataclass
|
|
120
|
+
class GameSubCategory:
|
|
121
|
+
id: int
|
|
122
|
+
sub_name: str
|
|
123
|
+
|
|
124
|
+
@dataclass
|
|
125
|
+
class Game:
|
|
126
|
+
title: GameTitle
|
|
127
|
+
subcategories: list[GameSubCategory] = field(default_factory=list)
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
from fpx.utils import errors as fpx_err
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class AddonsManager:
|
|
6
|
-
def __init__(self, account):
|
|
7
|
-
self._account = account
|
|
8
|
-
|
|
9
|
-
async def get_game_id(self, category_id: str):
|
|
10
|
-
"""
|
|
11
|
-
Получает game_id.
|
|
12
|
-
|
|
13
|
-
Args:
|
|
14
|
-
category_id (str | int): ID подкатегории.
|
|
15
|
-
|
|
16
|
-
Returns:
|
|
17
|
-
str | int: ID игры.
|
|
18
|
-
|
|
19
|
-
Raises:
|
|
20
|
-
FpxGetGameIDError: Ошибка запроса ID игры
|
|
21
|
-
"""
|
|
22
|
-
try:
|
|
23
|
-
stage = 'запроса данных категории с FunPay'
|
|
24
|
-
html = await self._account._client.lot_menu_by_category(category_id)
|
|
25
|
-
stage = 'парсинга данных'
|
|
26
|
-
data = self._account._parser.parse_lot_menu(html)
|
|
27
|
-
except Exception as e:
|
|
28
|
-
raise fpx_err.FpxGetGameIDError(f'При выполнении {stage} произошла ошибка: {e}')
|
|
29
|
-
return data
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|