funpaybotengine 0.3.6__tar.gz → 0.4.1__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.
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/PKG-INFO +5 -5
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/dispatching/events/base.py +25 -5
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/dispatching/events/builtin_events.py +27 -27
- funpaybotengine-0.4.1/funpaybotengine/runner/config.py +116 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/runner/runner.py +40 -10
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/types/messages.py +0 -1
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/types/orders.py +19 -12
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/pyproject.toml +5 -5
- funpaybotengine-0.4.1/temp.py +0 -0
- funpaybotengine-0.3.6/comments.json +0 -1
- funpaybotengine-0.3.6/funpaybotengine/runner/config.py +0 -49
- funpaybotengine-0.3.6/html.html +0 -209
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/.github/CODEOWNERS +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/.gitignore +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/.pre-commit-config.yaml +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/LICENSE +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/Makefile +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/README.md +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/examples/basic.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/examples/temp.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/examples/with_redis_storage.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/__init__.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/base.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/client/__init__.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/client/bot.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/client/default_hooks.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/client/session/__init__.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/client/session/aiohttp_session.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/client/session/base.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/client/session/http_methods.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/dispatching/__init__.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/dispatching/events/__init__.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/dispatching/filters/__init__.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/dispatching/filters/base.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/dispatching/filters/message_filters.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/dispatching/filters/order_filters.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/dispatching/handlers/__init__.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/dispatching/handlers/handler_manager.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/dispatching/routers/__init__.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/dispatching/routers/base.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/dispatching/routers/dispatcher.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/exceptions/__init__.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/exceptions/action_exceptions.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/exceptions/base.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/exceptions/bot_exceptions.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/exceptions/message_check_errors.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/exceptions/runner_exceptions.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/exceptions/session_exceptions.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/loggers.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/__init__.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/base.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/calc_chips.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/calc_lots.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/check_banned.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/delete_review.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/get_2fa_status.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/get_chat_history.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/get_chat_page.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/get_main_page.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/get_my_chips_page.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/get_my_offers_page.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/get_offer_fields.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/get_offer_page.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/get_order_page.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/get_profile_page.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/get_purchases.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/get_reviews.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/get_sales.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/get_settings_page.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/get_subcategory_page.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/get_telegram_connect_url.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/get_transactions.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/get_transactions_page.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/logout.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/mute_chat.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/raise_offers.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/refund.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/review.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/runner_request.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/save_offer_fields.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/set_offers_hidden.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/update_notice_channel.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/upload_avatar.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/methods/upload_image.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/py.typed +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/runner/__init__.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/runner/event_collector.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/storage/__init__.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/storage/base.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/storage/inmemory.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/storage/redis.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/types/__init__.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/types/base.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/types/calc.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/types/categories.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/types/chat.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/types/common.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/types/common_page_elements.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/types/enums.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/types/finances.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/types/offers.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/types/pages/__init__.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/types/pages/base.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/types/pages/chat_page.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/types/pages/main_page.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/types/pages/my_chips_page.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/types/pages/my_offers_page.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/types/pages/offer_page.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/types/pages/order_page.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/types/pages/profile_page.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/types/pages/settings_page.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/types/pages/subcategory_page.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/types/pages/transactions_page.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/types/requests/__init__.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/types/requests/runner.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/types/reviews.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/types/settings.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/types/updates.py +0 -0
- {funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: funpaybotengine
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.1
|
|
4
4
|
Summary: Framework for creating FunPay bots.
|
|
5
5
|
Project-URL: Telegram, https://t.me/funpay_hub
|
|
6
6
|
Project-URL: Issues, https://github.com/funpayhub/funpaybotengine/issues
|
|
@@ -18,11 +18,11 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.14
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.15
|
|
20
20
|
Requires-Python: >=3.10
|
|
21
|
-
Requires-Dist: aiohttp-socks
|
|
22
|
-
Requires-Dist: aiohttp
|
|
23
|
-
Requires-Dist: eventry<0.
|
|
21
|
+
Requires-Dist: aiohttp-socks>=0.11.0
|
|
22
|
+
Requires-Dist: aiohttp<3.14,>=3.13.3
|
|
23
|
+
Requires-Dist: eventry<1.0.0,>=0.3.6
|
|
24
24
|
Requires-Dist: funpayparsers<1.0.0,>=0.5.14
|
|
25
|
-
Requires-Dist: pydantic
|
|
25
|
+
Requires-Dist: pydantic<2.13,>=2.12
|
|
26
26
|
Provides-Extra: dev
|
|
27
27
|
Requires-Dist: mypy>=1.10.1; extra == 'dev'
|
|
28
28
|
Requires-Dist: pre-commit>=4.5.1; extra == 'dev'
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
__all__ = (
|
|
4
|
+
__all__ = (
|
|
5
|
+
'Event',
|
|
6
|
+
'RunnerEvent',
|
|
7
|
+
'BotEngineEvent',
|
|
8
|
+
'ExceptionEvent',
|
|
9
|
+
'BotUnauthenticatedEvent',
|
|
10
|
+
'BotAuthenticatedEvent',
|
|
11
|
+
)
|
|
5
12
|
|
|
6
13
|
from typing import Any, Generic, TypeVar
|
|
7
14
|
from types import MappingProxyType
|
|
@@ -16,7 +23,7 @@ EventObject = TypeVar('EventObject')
|
|
|
16
23
|
|
|
17
24
|
|
|
18
25
|
# Not inheriting from ExtendedEvent, cz __iter__ conflict is pydantic BaseModel.
|
|
19
|
-
class Event(EventryEvent, BindableObject, Generic[EventObject],
|
|
26
|
+
class Event(EventryEvent, BindableObject, Generic[EventObject], event_name='event'):
|
|
20
27
|
model_config = {
|
|
21
28
|
'arbitrary_types_allowed': True,
|
|
22
29
|
}
|
|
@@ -66,7 +73,7 @@ class Event(EventryEvent, BindableObject, Generic[EventObject], name='event'):
|
|
|
66
73
|
return MappingProxyType(self._data)
|
|
67
74
|
|
|
68
75
|
|
|
69
|
-
class RunnerEvent(Event[EventObject],
|
|
76
|
+
class RunnerEvent(Event[EventObject], event_name='runner'):
|
|
70
77
|
tag: str | None = Field(frozen=True)
|
|
71
78
|
|
|
72
79
|
@property
|
|
@@ -76,10 +83,10 @@ class RunnerEvent(Event[EventObject], name='runner'):
|
|
|
76
83
|
return injection
|
|
77
84
|
|
|
78
85
|
|
|
79
|
-
class BotEngineEvent(Event[EventObject],
|
|
86
|
+
class BotEngineEvent(Event[EventObject], event_name='funpaybotengine'): ...
|
|
80
87
|
|
|
81
88
|
|
|
82
|
-
class ExceptionEvent(BotEngineEvent[Exception],
|
|
89
|
+
class ExceptionEvent(BotEngineEvent[Exception], event_name='error'):
|
|
83
90
|
event: Event[Any] = Field(frozen=True)
|
|
84
91
|
|
|
85
92
|
@property
|
|
@@ -87,3 +94,16 @@ class ExceptionEvent(BotEngineEvent[Exception], name='error'):
|
|
|
87
94
|
injection = super().event_context_injection
|
|
88
95
|
injection.update({'on_event': self.event, 'exception': self.object})
|
|
89
96
|
return injection
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class BotUnauthenticatedEvent(BotEngineEvent[float], event_name='unauthorized'):
|
|
100
|
+
delay: float
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def event_context_injection(self) -> dict[str, Any]:
|
|
104
|
+
injection = super().event_context_injection
|
|
105
|
+
injection.update({'delay': self.delay})
|
|
106
|
+
return injection
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class BotAuthenticatedEvent(BotEngineEvent[None], event_name='authorized'): ...
|
{funpaybotengine-0.3.6 → funpaybotengine-0.4.1}/funpaybotengine/dispatching/events/builtin_events.py
RENAMED
|
@@ -43,7 +43,7 @@ from .base import RunnerEvent
|
|
|
43
43
|
from ...types.pages import OrderPage
|
|
44
44
|
|
|
45
45
|
|
|
46
|
-
class ChatChangedEvent(RunnerEvent[PrivateChatPreview],
|
|
46
|
+
class ChatChangedEvent(RunnerEvent[PrivateChatPreview], event_name='chat_changed'):
|
|
47
47
|
previous: PrivateChatPreview | None = None
|
|
48
48
|
|
|
49
49
|
@property
|
|
@@ -57,7 +57,7 @@ class ChatChangedEvent(RunnerEvent[PrivateChatPreview], name='chat_changed'):
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
|
|
60
|
-
class NewMessageEvent(RunnerEvent[Message],
|
|
60
|
+
class NewMessageEvent(RunnerEvent[Message], event_name='new_message'):
|
|
61
61
|
@property
|
|
62
62
|
def message(self) -> Message:
|
|
63
63
|
return self.object
|
|
@@ -69,7 +69,7 @@ class NewMessageEvent(RunnerEvent[Message], name='new_message'):
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
|
|
72
|
-
class FromMessageEvent(NewMessageEvent,
|
|
72
|
+
class FromMessageEvent(NewMessageEvent, event_name='__from_message__'):
|
|
73
73
|
related_new_message_event: NewMessageEvent
|
|
74
74
|
|
|
75
75
|
@property
|
|
@@ -79,14 +79,14 @@ class FromMessageEvent(NewMessageEvent, name='__from_message__'):
|
|
|
79
79
|
return val
|
|
80
80
|
|
|
81
81
|
|
|
82
|
-
class OrderEvent(FromMessageEvent,
|
|
82
|
+
class OrderEvent(FromMessageEvent, event_name='__order_event__'):
|
|
83
83
|
_order_preview: OrderPreview | None = PrivateAttr(default=None)
|
|
84
84
|
|
|
85
85
|
async def get_order_preview(self, update: bool = False) -> OrderPreview:
|
|
86
86
|
raise NotImplementedError
|
|
87
87
|
|
|
88
88
|
|
|
89
|
-
class SaleEvent(OrderEvent,
|
|
89
|
+
class SaleEvent(OrderEvent, event_name='__sale_event__'):
|
|
90
90
|
async def get_order_preview(self, update: bool = False) -> OrderPreview:
|
|
91
91
|
if self._order_preview is not None and not update:
|
|
92
92
|
return self._order_preview
|
|
@@ -95,7 +95,7 @@ class SaleEvent(OrderEvent, name='__sale_event__'):
|
|
|
95
95
|
return orders.orders[0]
|
|
96
96
|
|
|
97
97
|
|
|
98
|
-
class PurchaseEvent(OrderEvent,
|
|
98
|
+
class PurchaseEvent(OrderEvent, event_name='__purchase_event__'):
|
|
99
99
|
async def get_order_preview(self, update: bool = False) -> OrderPreview:
|
|
100
100
|
if self._order_preview is not None and not update:
|
|
101
101
|
return self._order_preview
|
|
@@ -106,55 +106,55 @@ class PurchaseEvent(OrderEvent, name='__purchase_event__'):
|
|
|
106
106
|
return orders.orders[0]
|
|
107
107
|
|
|
108
108
|
|
|
109
|
-
class NewSaleEvent(SaleEvent,
|
|
109
|
+
class NewSaleEvent(SaleEvent, event_name='new_sale'):
|
|
110
110
|
related_auto_message_events: list[NewMessageEvent] = Field(default_factory=list)
|
|
111
111
|
|
|
112
112
|
|
|
113
|
-
class SaleStatusChangedEvent(SaleEvent,
|
|
113
|
+
class SaleStatusChangedEvent(SaleEvent, event_name='sale_status_changed'):
|
|
114
114
|
previous: OrderPreview | None = None
|
|
115
115
|
|
|
116
116
|
|
|
117
|
-
class SaleClosedEvent(SaleStatusChangedEvent,
|
|
117
|
+
class SaleClosedEvent(SaleStatusChangedEvent, event_name='sale_closed'): ...
|
|
118
118
|
|
|
119
119
|
|
|
120
|
-
class SaleClosedByAdminEvent(SaleClosedEvent,
|
|
120
|
+
class SaleClosedByAdminEvent(SaleClosedEvent, event_name='sale_closed_by_admin'): ...
|
|
121
121
|
|
|
122
122
|
|
|
123
|
-
class SaleRefundedEvent(SaleStatusChangedEvent,
|
|
123
|
+
class SaleRefundedEvent(SaleStatusChangedEvent, event_name='sale_refunded'): ...
|
|
124
124
|
|
|
125
125
|
|
|
126
|
-
class SalePartiallyRefundedEvent(SaleRefundedEvent,
|
|
126
|
+
class SalePartiallyRefundedEvent(SaleRefundedEvent, event_name='sale_partially_refunded'): ...
|
|
127
127
|
|
|
128
128
|
|
|
129
|
-
class SaleReopenedEvent(SaleStatusChangedEvent,
|
|
129
|
+
class SaleReopenedEvent(SaleStatusChangedEvent, event_name='sale_reopened'): ...
|
|
130
130
|
|
|
131
131
|
|
|
132
|
-
class NewPurchaseEvent(PurchaseEvent,
|
|
132
|
+
class NewPurchaseEvent(PurchaseEvent, event_name='new_purchase'):
|
|
133
133
|
related_auto_message_events: list[NewMessageEvent] = Field(default_factory=list)
|
|
134
134
|
|
|
135
135
|
|
|
136
|
-
class PurchaseStatusChangedEvent(PurchaseEvent,
|
|
136
|
+
class PurchaseStatusChangedEvent(PurchaseEvent, event_name='purchase_status_changed'):
|
|
137
137
|
previous: OrderPreview | None = None
|
|
138
138
|
|
|
139
139
|
|
|
140
|
-
class PurchaseClosedEvent(PurchaseStatusChangedEvent,
|
|
140
|
+
class PurchaseClosedEvent(PurchaseStatusChangedEvent, event_name='purchase_closed'): ...
|
|
141
141
|
|
|
142
142
|
|
|
143
|
-
class PurchaseClosedByAdminEvent(PurchaseClosedEvent,
|
|
143
|
+
class PurchaseClosedByAdminEvent(PurchaseClosedEvent, event_name='purchase_closed_by_admin'): ...
|
|
144
144
|
|
|
145
145
|
|
|
146
|
-
class PurchaseRefundedEvent(PurchaseStatusChangedEvent,
|
|
146
|
+
class PurchaseRefundedEvent(PurchaseStatusChangedEvent, event_name='purchase_refunded'): ...
|
|
147
147
|
|
|
148
148
|
|
|
149
149
|
class PurchasePartiallyRefundedEvent(
|
|
150
|
-
PurchaseRefundedEvent,
|
|
150
|
+
PurchaseRefundedEvent, event_name='purchase_partially_refunded'
|
|
151
151
|
): ...
|
|
152
152
|
|
|
153
153
|
|
|
154
|
-
class PurchaseReopenedEvent(PurchaseStatusChangedEvent,
|
|
154
|
+
class PurchaseReopenedEvent(PurchaseStatusChangedEvent, event_name='purchase_reopened'): ...
|
|
155
155
|
|
|
156
156
|
|
|
157
|
-
class ReviewEvent(FromMessageEvent,
|
|
157
|
+
class ReviewEvent(FromMessageEvent, event_name='__review_event__'):
|
|
158
158
|
_order_page: OrderPage | None = PrivateAttr(default=None)
|
|
159
159
|
|
|
160
160
|
async def get_order_page(self, update: bool = False) -> OrderPage:
|
|
@@ -169,19 +169,19 @@ class ReviewEvent(FromMessageEvent, name='__review_event__'):
|
|
|
169
169
|
return order_page.review
|
|
170
170
|
|
|
171
171
|
|
|
172
|
-
class NewReviewEvent(ReviewEvent,
|
|
172
|
+
class NewReviewEvent(ReviewEvent, event_name='new_review'): ...
|
|
173
173
|
|
|
174
174
|
|
|
175
|
-
class ReviewChangedEvent(ReviewEvent,
|
|
175
|
+
class ReviewChangedEvent(ReviewEvent, event_name='review_changed'): ...
|
|
176
176
|
|
|
177
177
|
|
|
178
|
-
class ReviewDeletedEvent(ReviewEvent,
|
|
178
|
+
class ReviewDeletedEvent(ReviewEvent, event_name='review_deleted'): ...
|
|
179
179
|
|
|
180
180
|
|
|
181
|
-
class NewReviewResponseEvent(ReviewEvent,
|
|
181
|
+
class NewReviewResponseEvent(ReviewEvent, event_name='new_review_response'): ...
|
|
182
182
|
|
|
183
183
|
|
|
184
|
-
class ReviewResponseChangedEvent(ReviewEvent,
|
|
184
|
+
class ReviewResponseChangedEvent(ReviewEvent, event_name='review_response_changed'): ...
|
|
185
185
|
|
|
186
186
|
|
|
187
|
-
class ReviewResponseDeletedEvent(ReviewEvent,
|
|
187
|
+
class ReviewResponseDeletedEvent(ReviewEvent, event_name='review_response_deleted'): ...
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
__all__ = ('RunnerConfig', 'BackoffConfig', 'Backoff')
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
import asyncio
|
|
8
|
+
from typing import Literal
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(frozen=True)
|
|
13
|
+
class BackoffConfig:
|
|
14
|
+
min_delay: float = 6.0
|
|
15
|
+
max_delay: float = 18.0
|
|
16
|
+
factor: float = 1.3
|
|
17
|
+
|
|
18
|
+
def __post_init__(self) -> None:
|
|
19
|
+
if self.max_delay <= self.min_delay:
|
|
20
|
+
raise ValueError('`max_delay` must be greater than `min_delay`.')
|
|
21
|
+
if self.factor <= 1:
|
|
22
|
+
raise ValueError('`factor` must be greater than 1')
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class RunnerConfig:
|
|
27
|
+
interval: int | float = 4.0
|
|
28
|
+
"""
|
|
29
|
+
Interval between updates requests.
|
|
30
|
+
Must be more than ``0``.
|
|
31
|
+
|
|
32
|
+
Defaults to ``4.0``.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
discover_sales: bool = True
|
|
36
|
+
"""Whether to discover new sales or not."""
|
|
37
|
+
|
|
38
|
+
discover_purchases: bool = True
|
|
39
|
+
"""Whether to discover new purchases or not."""
|
|
40
|
+
|
|
41
|
+
keep_unread: bool = False
|
|
42
|
+
"""
|
|
43
|
+
Whether to preserve the unread status of chats when getting updates.
|
|
44
|
+
|
|
45
|
+
If ``False`` (default), a batch method is used: up to 10 chats can be requested in a single
|
|
46
|
+
request, but they will be marked as read.
|
|
47
|
+
|
|
48
|
+
If ``True``, only one chat can be fetched per request to keep it unread.
|
|
49
|
+
With high incoming message volume this leads to many requests and may cause
|
|
50
|
+
``429 Too Many Requests`` errors.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
on_unauthenticated_error_policy: Literal['ignore', 'event', 'stop'] = 'ignore'
|
|
54
|
+
"""
|
|
55
|
+
What to do when an `BotUnauthenticatedError` occurred during fetching updates process?
|
|
56
|
+
|
|
57
|
+
- ``ignore``: ignore the error and continue fetching updates.
|
|
58
|
+
- ``event``: yield an error event and continue fetching updates.
|
|
59
|
+
- ``stop``: stop fetching updates, exit a function.
|
|
60
|
+
|
|
61
|
+
Defaults to ``ignore``.
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
backoff_config: BackoffConfig = BackoffConfig()
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class Backoff:
|
|
68
|
+
def __init__(self, config: BackoffConfig) -> None:
|
|
69
|
+
self.config = config
|
|
70
|
+
self._next_delay = config.min_delay
|
|
71
|
+
self._current_delay = 0.0
|
|
72
|
+
self._counter = 0
|
|
73
|
+
|
|
74
|
+
def __iter__(self) -> Backoff:
|
|
75
|
+
return self
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def min_delay(self) -> float:
|
|
79
|
+
return self.config.min_delay
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def max_delay(self) -> float:
|
|
83
|
+
return self.config.max_delay
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def factor(self) -> float:
|
|
87
|
+
return self.config.factor
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def next_delay(self) -> float:
|
|
91
|
+
return self._next_delay
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def current_delay(self) -> float:
|
|
95
|
+
return self._current_delay
|
|
96
|
+
|
|
97
|
+
@property
|
|
98
|
+
def counter(self) -> int:
|
|
99
|
+
return self._counter
|
|
100
|
+
|
|
101
|
+
async def asleep(self) -> None:
|
|
102
|
+
await asyncio.sleep(next(self))
|
|
103
|
+
|
|
104
|
+
def _calculate_next(self, value: float) -> float:
|
|
105
|
+
return min(value * self.factor, self.max_delay)
|
|
106
|
+
|
|
107
|
+
def __next__(self) -> float:
|
|
108
|
+
self._current_delay = self._next_delay
|
|
109
|
+
self._next_delay = self._calculate_next(self._next_delay)
|
|
110
|
+
self._counter += 1
|
|
111
|
+
return self._current_delay
|
|
112
|
+
|
|
113
|
+
def reset(self) -> None:
|
|
114
|
+
self._current_delay = 0.0
|
|
115
|
+
self._counter = 0
|
|
116
|
+
self._next_delay = self.min_delay
|
|
@@ -14,13 +14,15 @@ from collections.abc import Iterator, AsyncGenerator
|
|
|
14
14
|
from funpaybotengine.loggers import runner_logger
|
|
15
15
|
from funpaybotengine.exceptions import UnauthorizedError, BotUnauthenticatedError
|
|
16
16
|
from funpaybotengine.storage.base import Storage
|
|
17
|
-
from funpaybotengine.
|
|
18
|
-
|
|
19
|
-
from
|
|
17
|
+
from funpaybotengine.dispatching.events import BotAuthenticatedEvent, BotUnauthenticatedEvent
|
|
18
|
+
|
|
19
|
+
from .config import Backoff, RunnerConfig
|
|
20
|
+
from .event_collector import EventCollector
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
if TYPE_CHECKING:
|
|
23
24
|
from funpaybotengine.client.bot import Bot
|
|
25
|
+
from funpaybotengine.dispatching.events import RunnerEvent, BotEngineEvent
|
|
24
26
|
|
|
25
27
|
|
|
26
28
|
@dataclass
|
|
@@ -64,34 +66,62 @@ class Runner:
|
|
|
64
66
|
config,
|
|
65
67
|
session_storage=session_storage,
|
|
66
68
|
)
|
|
69
|
+
backoff = Backoff(config.backoff_config)
|
|
67
70
|
|
|
68
71
|
await collector.init_chats()
|
|
69
72
|
|
|
73
|
+
sleep_time: float | None = None
|
|
70
74
|
while True:
|
|
71
75
|
start = time.time()
|
|
76
|
+
result: list[RunnerEvent[Any] | BotEngineEvent[Any]] = []
|
|
72
77
|
|
|
73
78
|
try:
|
|
74
79
|
result = await collector.get_events()
|
|
80
|
+
if backoff.counter:
|
|
81
|
+
backoff.reset()
|
|
82
|
+
runner_logger.info('Connection established. Continuing collecting events.')
|
|
83
|
+
if config.on_unauthenticated_error_policy == 'event':
|
|
84
|
+
result.insert(0, BotAuthenticatedEvent(object=None))
|
|
85
|
+
|
|
75
86
|
except (BotUnauthenticatedError, UnauthorizedError) as e:
|
|
76
87
|
runner_logger.warning(
|
|
77
88
|
'Bot is unauthenticated (%s). Executing current policy %r.',
|
|
78
89
|
e.__class__.__name__,
|
|
79
90
|
config.on_unauthenticated_error_policy,
|
|
80
91
|
)
|
|
81
|
-
|
|
82
|
-
|
|
92
|
+
|
|
93
|
+
if config.on_unauthenticated_error_policy in ['event', 'ignore']:
|
|
94
|
+
next(backoff)
|
|
95
|
+
sleep_time = backoff.current_delay
|
|
96
|
+
runner_logger.warning(
|
|
97
|
+
'Current attempt: %d. Delay: %f.', backoff.counter, backoff.current_delay
|
|
98
|
+
)
|
|
99
|
+
if backoff.counter == 1 and config.on_unauthenticated_error_policy == 'event':
|
|
100
|
+
result = [
|
|
101
|
+
BotUnauthenticatedEvent(object=None, delay=backoff.current_delay)
|
|
102
|
+
]
|
|
103
|
+
|
|
83
104
|
elif config.on_unauthenticated_error_policy == 'stop':
|
|
84
105
|
return
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
106
|
+
except Exception:
|
|
107
|
+
next(backoff)
|
|
108
|
+
runner_logger.error(
|
|
109
|
+
'Failed to collect events. Current attempt: %d. Delay: %f.',
|
|
110
|
+
backoff.counter,
|
|
111
|
+
backoff.current_delay,
|
|
112
|
+
exc_info=True,
|
|
113
|
+
)
|
|
114
|
+
sleep_time = backoff.current_delay
|
|
89
115
|
|
|
90
116
|
events_stack = EventsStack(events=tuple(result))
|
|
91
117
|
for i in events_stack:
|
|
92
118
|
yield i, events_stack
|
|
93
119
|
|
|
94
|
-
|
|
120
|
+
if sleep_time:
|
|
121
|
+
await asyncio.sleep(sleep_time)
|
|
122
|
+
sleep_time = None
|
|
123
|
+
else:
|
|
124
|
+
await _sleep(start, config.interval)
|
|
95
125
|
|
|
96
126
|
|
|
97
127
|
async def _sleep(start_time: int | float, interval: int | float) -> None:
|
|
@@ -216,7 +216,6 @@ class Message(FunPayObject, BaseModel):
|
|
|
216
216
|
|
|
217
217
|
return info.context.get('chat_id') if info.context else None
|
|
218
218
|
|
|
219
|
-
|
|
220
219
|
@field_validator('chat_name', mode='before')
|
|
221
220
|
@classmethod
|
|
222
221
|
def _get_chat_name_from_context(cls, value: Any, info: ValidationInfo) -> Any:
|
|
@@ -6,7 +6,8 @@ __all__ = ('OrderPreview', 'OrderPreviewsBatch')
|
|
|
6
6
|
|
|
7
7
|
from typing import Any
|
|
8
8
|
|
|
9
|
-
from pydantic import BaseModel, PrivateAttr
|
|
9
|
+
from pydantic import BaseModel, PrivateAttr
|
|
10
|
+
from funpayparsers.parsers.utils import parse_date_string
|
|
10
11
|
|
|
11
12
|
from funpaybotengine.types.base import FunPayObject
|
|
12
13
|
from funpaybotengine.types.enums import OrderStatus, OrderPreviewType
|
|
@@ -18,7 +19,11 @@ class OrderPreview(FunPayObject, BaseModel):
|
|
|
18
19
|
|
|
19
20
|
def model_post_init(self, context: dict[Any, Any]) -> None:
|
|
20
21
|
super().model_post_init(context)
|
|
21
|
-
if
|
|
22
|
+
if (
|
|
23
|
+
self.type is OrderPreviewType.UNKNOWN
|
|
24
|
+
and context
|
|
25
|
+
and context.get('order_preview_type') is not None
|
|
26
|
+
):
|
|
22
27
|
self._type = context['order_preview_type']
|
|
23
28
|
|
|
24
29
|
id: str
|
|
@@ -42,12 +47,20 @@ class OrderPreview(FunPayObject, BaseModel):
|
|
|
42
47
|
counterparty: UserPreview
|
|
43
48
|
"""Associated counterparty info."""
|
|
44
49
|
|
|
45
|
-
|
|
50
|
+
type: OrderPreviewType = OrderPreviewType.UNKNOWN
|
|
51
|
+
"""Order preview type."""
|
|
46
52
|
|
|
47
|
-
@computed_field # type: ignore[prop-decorator]
|
|
48
53
|
@property
|
|
49
|
-
def
|
|
50
|
-
|
|
54
|
+
def timestamp(self) -> int:
|
|
55
|
+
"""
|
|
56
|
+
Order timestamp.
|
|
57
|
+
|
|
58
|
+
``0``, if an error occurred while parsing.
|
|
59
|
+
"""
|
|
60
|
+
try:
|
|
61
|
+
return parse_date_string(self.date_text)
|
|
62
|
+
except ValueError:
|
|
63
|
+
return 0
|
|
51
64
|
|
|
52
65
|
|
|
53
66
|
class OrderPreviewsBatch(FunPayObject):
|
|
@@ -114,32 +127,26 @@ class OrderPreviewsBatch(FunPayObject):
|
|
|
114
127
|
|
|
115
128
|
return await method_coroutine
|
|
116
129
|
|
|
117
|
-
@computed_field # type: ignore[prop-decorator]
|
|
118
130
|
@property
|
|
119
131
|
def order_id_filter(self) -> str | None:
|
|
120
132
|
return self._order_id_filter
|
|
121
133
|
|
|
122
|
-
@computed_field # type: ignore[prop-decorator]
|
|
123
134
|
@property
|
|
124
135
|
def buyer_username_filter(self) -> str | None:
|
|
125
136
|
return self._buyer_username_filter
|
|
126
137
|
|
|
127
|
-
@computed_field # type: ignore[prop-decorator]
|
|
128
138
|
@property
|
|
129
139
|
def status_filter(self) -> OrderStatus | str | None:
|
|
130
140
|
return self._status_filter
|
|
131
141
|
|
|
132
|
-
@computed_field # type: ignore[prop-decorator]
|
|
133
142
|
@property
|
|
134
143
|
def game_id_filter(self) -> int | None:
|
|
135
144
|
return self._game_id_filter
|
|
136
145
|
|
|
137
|
-
@computed_field # type: ignore[prop-decorator]
|
|
138
146
|
@property
|
|
139
147
|
def other_filters(self) -> dict[str, str] | None:
|
|
140
148
|
return self._other_filters
|
|
141
149
|
|
|
142
|
-
@computed_field # type: ignore[prop-decorator]
|
|
143
150
|
@property
|
|
144
151
|
def type(self) -> OrderPreviewType:
|
|
145
152
|
return self._type
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "funpaybotengine"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.4.1"
|
|
4
4
|
description = "Framework for creating FunPay bots."
|
|
5
5
|
requires-python = ">=3.10"
|
|
6
6
|
readme = "README.md"
|
|
7
7
|
license = "MIT"
|
|
8
8
|
dependencies = [
|
|
9
|
-
"aiohttp>=3.13.3",
|
|
10
|
-
"aiohttp-socks
|
|
11
|
-
"eventry>=0.3.
|
|
9
|
+
"aiohttp>=3.13.3,<3.14",
|
|
10
|
+
"aiohttp-socks>=0.11.0",
|
|
11
|
+
"eventry>=0.3.6,<1.0.0",
|
|
12
12
|
"funpayparsers>=0.5.14,<1.0.0",
|
|
13
|
-
"pydantic
|
|
13
|
+
"pydantic>=2.12,<2.13",
|
|
14
14
|
]
|
|
15
15
|
authors = [
|
|
16
16
|
{name = "Qvvonk", email="qvvonk@gmail.com"}
|
|
File without changes
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
[]
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
__all__ = ('RunnerConfig',)
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
from typing import Literal
|
|
8
|
-
from dataclasses import dataclass
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
@dataclass
|
|
12
|
-
class RunnerConfig:
|
|
13
|
-
interval: int | float = 4.0
|
|
14
|
-
"""
|
|
15
|
-
Interval between updates requests.
|
|
16
|
-
Must be more than ``0``.
|
|
17
|
-
|
|
18
|
-
Defaults to ``4.0``.
|
|
19
|
-
"""
|
|
20
|
-
|
|
21
|
-
discover_sales: bool = True
|
|
22
|
-
"""Whether to discover new sales or not."""
|
|
23
|
-
|
|
24
|
-
discover_purchases: bool = True
|
|
25
|
-
"""Whether to discover new purchases or not."""
|
|
26
|
-
|
|
27
|
-
keep_unread: bool = False
|
|
28
|
-
"""
|
|
29
|
-
Whether to preserve the unread status of chats when getting updates.
|
|
30
|
-
|
|
31
|
-
If ``False`` (default), a batch method is used: up to 10 chats can be requested in a single
|
|
32
|
-
request, but they will be marked as read.
|
|
33
|
-
|
|
34
|
-
If ``True``, only one chat can be fetched per request to keep it unread.
|
|
35
|
-
With high incoming message volume this leads to many requests and may cause
|
|
36
|
-
``429 Too Many Requests`` errors.
|
|
37
|
-
"""
|
|
38
|
-
|
|
39
|
-
on_unauthenticated_error_policy: Literal['ignore', 'event', 'stop', 'stop+event'] = 'ignore'
|
|
40
|
-
"""
|
|
41
|
-
What to do when an `BotUnauthenticatedError` occurred during fetching updates process?
|
|
42
|
-
|
|
43
|
-
- ``ignore``: ignore the error and continue fetching updates.
|
|
44
|
-
- ``event``: yield an error event and continue fetching updates.
|
|
45
|
-
- ``stop``: stop fetching updates, exit a function.
|
|
46
|
-
- ``stop+event``: yield an error event and stop fetching updates, exit a function.
|
|
47
|
-
|
|
48
|
-
Defaults to ``ignore``.
|
|
49
|
-
"""
|