Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
* text=auto

*.py text eol=lf
*.toml text eol=lf
*.yaml text eol=lf
*.yml text eol=lf
*.md text eol=lf
Makefile text eol=lf
7 changes: 5 additions & 2 deletions funpaybotengine/client/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@
TransactionFilter,
OrderPreviewsBatch,
PrivateChatPreview,
RaiseOffersResponse,
TransactionPreviewsBatch,
CurrentlyViewingOfferInfo,
RaiseOffersResponse
)
from funpaybotengine.utils import (
random_runner_tag,
Expand Down Expand Up @@ -466,6 +466,8 @@ async def calc_lots(self, subcategory_id: int, price: float) -> CalcResult:
async def logout(self) -> bool:
if self._logout_token is None:
await self.update()
if self._logout_token is None:
raise BotNotInitializedError(self)

return (await Logout(logout_token=self._logout_token).execute(self)).response_obj

Expand Down Expand Up @@ -602,7 +604,8 @@ async def get_chat_messages(

:param args: Tuple of (chat_id, after_message_id)

:returns: A dictionary [chat_id, list] or a list of up to 100 ``Message`` objects, sorted from oldest to newest.
:returns: A dictionary [chat_id, list] or a list of up to 100 ``Message`` objects,
sorted from oldest to newest.
"""
if not args and chat_id is None:
raise ValueError('Either `chat_id` or `args` must be provided.')
Expand Down
39 changes: 19 additions & 20 deletions funpaybotengine/exceptions/method_exceptions.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
__all__ = [
'MethodError',
'InvalidOfferFieldsError'
]


from .base import FunPayBotEngineError


class MethodError(FunPayBotEngineError):
...


class InvalidOfferFieldsError(MethodError):
def __init__(self, message: str, fields: dict[str, str] | None = None):
self.message = message
self.fields = fields

def __str__(self):
return self.message
from __future__ import annotations


__all__ = ['MethodError', 'InvalidOfferFieldsError']


from .base import FunPayBotEngineError


class MethodError(FunPayBotEngineError): ...


class InvalidOfferFieldsError(MethodError):
def __init__(self, message: str, fields: dict[str, str] | None = None):
self.message = message
self.fields = fields

def __str__(self) -> str:
return self.message
2 changes: 1 addition & 1 deletion funpaybotengine/methods/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@
from .get_settings_page import *
from .save_offer_fields import *
from .set_offers_hidden import *
from .get_sras_info_page import *
from .get_my_offers_page import *
from .get_sras_info_page import *
from .get_subcategory_page import *
from .get_transactions_page import *
from .update_notice_channel import *
Expand Down
6 changes: 3 additions & 3 deletions funpaybotengine/methods/raise_offers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@
__all__ = ('RaiseOffers',)

import json
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING, Any, cast
from collections.abc import Sequence

from pydantic import Field
from typing_extensions import Annotated

from funpaybotengine.types.enums import Language
from funpaybotengine.methods.base import FunPayMethod
from funpaybotengine.client.session.http_methods import HTTPMethod
from funpaybotengine.types.common import RaiseOffersResponse
from funpaybotengine.client.session.http_methods import HTTPMethod


if TYPE_CHECKING:
Expand Down Expand Up @@ -47,4 +47,4 @@ def __init__(

async def parse_result(self, response: RawResponse[bool]) -> dict[str, Any]:
data = json.loads(response.raw_response)
return {'raw_source': response.raw_response} | data
return {'raw_source': response.raw_response} | cast(dict[str, Any], data)
4 changes: 2 additions & 2 deletions funpaybotengine/methods/refund.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ def __init__(self, order_id: str, locale: Language | None = None):
async def parse_result(self, response: RawResponse[Any]) -> bool:
try:
result = json.loads(response.raw_response)
except:
except json.decoder.JSONDecodeError:
raise RefundError(
order_id=self.order_id,
message=f'Unable to refund order {self.order_id}',
)
) from None

if result.get('error'):
raise RefundError(
Expand Down
23 changes: 12 additions & 11 deletions funpaybotengine/methods/save_offer_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@

__all__ = ('SaveOfferFields',)

from typing import Any
import json
from typing import Any, cast

from pydantic import BaseModel
from funpayparsers.types import Language
from funpayparsers.parsers import OfferFieldsParser

from funpaybotengine.exceptions.method_exceptions import InvalidOfferFieldsError
from funpaybotengine.methods.base import FunPayMethod
from funpaybotengine.types.offers import OfferFields
from funpaybotengine.client.session import HTTPMethod, RawResponse
import json
from funpaybotengine.exceptions.method_exceptions import InvalidOfferFieldsError


class SaveOfferFields(FunPayMethod[bool], BaseModel):
Expand Down Expand Up @@ -43,27 +43,28 @@ def __init__(

async def parse_result(self, response: RawResponse[Any]) -> bool | dict[str, Any]:
try:
return json.loads(response.raw_response)
return cast(dict[str, Any], json.loads(response.raw_response))
except json.decoder.JSONDecodeError:
return True

async def transform_result(
self,
parsing_result: bool | dict[str, Any],
response: RawResponse[Any]
response: RawResponse[Any],
) -> bool:
if isinstance(parsing_result, bool):
return parsing_result


if not parsing_result.get('error'):
return True

error_msg = str(parsing_result.get('msg', '')) or str(parsing_result.get('error'))
fields: list[list[str, str]] = parsing_result.get('errors')
try:
fields_dict = {field: error for field, error in fields}
except Exception:
fields_dict = {}
raw_fields = parsing_result.get('errors')
fields_dict: dict[str, str] = {}
if isinstance(raw_fields, list):
for item in raw_fields:
if isinstance(item, list) and len(item) == 2:
field, error = item
fields_dict[str(field)] = str(error)

raise InvalidOfferFieldsError(error_msg, fields_dict)
6 changes: 4 additions & 2 deletions funpaybotengine/runner/event_collector.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import time
from typing import TYPE_CHECKING, Any, Type, Literal, TypeVar, cast
from itertools import chain
from collections import ChainMap
from collections.abc import Callable

from funpaybotengine.types import PrivateChatPreview
Expand Down Expand Up @@ -53,7 +54,6 @@
from funpaybotengine.storage.base import Storage
from funpaybotengine.types.orders import OrderPreview
from funpaybotengine.types.updates import RunnerResponse
from collections import ChainMap


CHAT_EVENTS = ChatChangedEvent | NewMessageEvent
Expand Down Expand Up @@ -103,6 +103,7 @@ async def inner(*args: Any, **kwargs: Any) -> Any:
except UnexpectedHTTPStatusError:
if not attempts:
raise
return None

return inner # type: ignore

Expand Down Expand Up @@ -324,7 +325,8 @@ async def get_new_message_events(self, total: EventsPack) -> None:
if from_id != 0:
if from_id < message.id <= to_id:
logger.debug(
'New message in chat %r (%r): %r (from IDs difference: %r < %r <= %r).',
'New message in chat %r (%r): %r '
'(from IDs difference: %r < %r <= %r).',
chat_event.chat_preview.username,
chat_event.chat_preview.id,
message.id,
Expand Down
18 changes: 9 additions & 9 deletions funpaybotengine/runner/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import asyncio
from typing import TYPE_CHECKING, Any
from dataclasses import field, dataclass
from collections.abc import Iterator, AsyncGenerator
from collections.abc import Iterator, Sequence, AsyncGenerator

from funpaybotengine.loggers import runner_logger
from funpaybotengine.exceptions import UnauthorizedError, BotUnauthenticatedError
Expand Down Expand Up @@ -41,7 +41,7 @@ def __getitem__(self, item: Any) -> Any:
def __setitem__(self, item: Any, value: Any) -> None:
self.data[item] = value

def __iter__(self) -> Iterator[RunnerEvent[Any]]:
def __iter__(self) -> Iterator[RunnerEvent[Any] | BotEngineEvent[Any]]:
return iter(self.events)

def get(self, value: str, fallback: Any = None) -> Any:
Expand All @@ -60,7 +60,7 @@ async def listen(
self,
config: RunnerConfig | None = None,
session_storage: Storage | None = None,
) -> AsyncGenerator[tuple[RunnerEvent[Any], EventsStack], None]:
) -> AsyncGenerator[tuple[RunnerEvent[Any] | BotEngineEvent[Any], EventsStack], None]:
config = config or RunnerConfig()
collector = EventCollector(
self.bot,
Expand All @@ -74,15 +74,15 @@ async def listen(
sleep_time: float | None = None
while True:
start = time.time()
result: list[RunnerEvent[Any] | BotEngineEvent[Any]] = []
result: Sequence[RunnerEvent[Any] | BotEngineEvent[Any]] = ()

try:
result = await collector.get_events()
if backoff.counter:
backoff.reset()
runner_logger.info('Connection established. Continuing collecting events.')
if config.on_unauthenticated_error_policy == 'event':
result.insert(0, BotAuthenticatedEvent(object=None))
result = (BotAuthenticatedEvent(object=None), *result)

except (BotUnauthenticatedError, UnauthorizedError) as e:
runner_logger.warning(
Expand All @@ -98,9 +98,9 @@ async def listen(
'Current attempt: %d. Delay: %f.', backoff.counter, backoff.current_delay
)
if backoff.counter == 1 and config.on_unauthenticated_error_policy == 'event':
result = [
BotUnauthenticatedEvent(object=None, delay=backoff.current_delay)
]
result = (
BotUnauthenticatedEvent(object=None, delay=backoff.current_delay),
)

elif config.on_unauthenticated_error_policy == 'stop':
return
Expand All @@ -116,7 +116,7 @@ async def listen(

events_stack = EventsStack(events=())
if not backoff.counter:
result.insert(0, NewEventsPack(object=events_stack.id))
result = (NewEventsPack(object=events_stack.id), *result)
events_stack.events = tuple(result)

for i in events_stack:
Expand Down
2 changes: 1 addition & 1 deletion funpaybotengine/types/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from .base import *
from .calc import *
from .chat import *
from .sras import *
from .enums import *
from .common import *
from .offers import *
Expand All @@ -11,7 +12,6 @@
from .updates import *
from .finances import *
from .messages import *
from .sras import *
from .settings import *
from .categories import *
from .common_page_elements import *
3 changes: 2 additions & 1 deletion funpaybotengine/types/calc.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@

class MethodResult(FunPayObject, BaseModel):
"""
Represents a result of offer price calculation method (`lots/calc`) for a single payment method.
Represents a result of offer price calculation method (`lots/calc`) for a single
payment method.
"""

name: str = ''
Expand Down
3 changes: 2 additions & 1 deletion funpaybotengine/types/offers.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ def wrapper(*args: P.args, **kwargs: P.kwargs) -> Any:
if not obj.is_common:
raise RuntimeError(
f'Instance of {obj.__class__.__name__} is not describing a common lot fields.\n'
f'Use {obj.__class__.__name__}.convert_to_common to convert it to common lot fields.',
f'Use {obj.__class__.__name__}.convert_to_common to convert it to common lot '
'fields.',
)
return func(*args, **kwargs)

Expand Down
2 changes: 1 addition & 1 deletion funpaybotengine/types/pages/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@
from .my_chips_page import *
from .settings_page import *
from .my_offers_page import *
from .subcategory_page import *
from .sras_info_page import *
from .subcategory_page import *
from .transactions_page import *