diff --git a/src/pymax/api/chats/service.py b/src/pymax/api/chats/service.py index 9b88b18..611b81f 100644 --- a/src/pymax/api/chats/service.py +++ b/src/pymax/api/chats/service.py @@ -4,6 +4,9 @@ from functools import reduce from operator import or_ from typing import TYPE_CHECKING +from datetime import datetime, timedelta + +from pymax.api.messages import DateTimeUnion from pymax.api.binding import bind_api_model from pymax.api.response import ( @@ -82,6 +85,19 @@ def _remove_cached_chat(self, chat_id: int) -> None: self.app.chats = [chat for chat in self.app.chats if chat.id != chat_id] + def _convert_period(self, period: DateTimeUnion) -> int: + if isinstance(period, timedelta): + return int(period.total_seconds()) + if isinstance(period, datetime): + raise ValueError("Period cannot be a datetime") + return int(period) + + def _convert_time(self, time_: DateTimeUnion) -> int: + if isinstance(time_, datetime): + return int(time_.timestamp() * 1000) + if isinstance(time_, timedelta): + return int((datetime.now() + time_).timestamp() * 1000) + @staticmethod def _process_chat_join_link(link: str) -> str | None: idx = link.find(ChatLinkPrefix.JOIN) @@ -164,12 +180,12 @@ async def remove_users_from_group( self, chat_id: int, user_ids: list[int], - clean_msg_period: int, + clean_msg_period: DateTimeUnion, ) -> bool: frame = RemoveUsersPayload( chat_id=chat_id, user_ids=user_ids, - clean_msg_period=clean_msg_period, + clean_msg_period=self._convert_period(clean_msg_period), ) response = await self.app.invoke( @@ -309,8 +325,16 @@ async def leave_group(self, chat_id: int) -> None: async def leave_channel(self, chat_id: int) -> None: await self.leave_group(chat_id) - async def fetch_chats(self, marker: int | None = None) -> list[Chat]: - frame = FetchChatsPayload(marker=marker or int(time.time() * 1000)) + async def fetch_chats(self, marker: DateTimeUnion | None = None) -> list[Chat]: + frame = FetchChatsPayload( + marker=( + self._convert_time(marker) + if isinstance(marker, (datetime, timedelta)) + # Если marker=0, он будет перезаписан текущим временем (т.к. 0 расценивается как False). + # Чтобы это исправить, можно заменить на: else (int(marker) if marker is not None else int(time.time() * 1000)) + else (marker or int(time.time() * 1000)) + ) + ) response = await self.app.invoke(Opcode.CHATS_LIST, frame.to_payload()) chats = [ @@ -395,13 +419,15 @@ async def decline_join_request( async def delete_chat( self, chat_id: int, - last_event_time: int | None = None, + last_event_time: DateTimeUnion | None = None, for_all: bool = True, ) -> None: frame = DeleteChatPayload( chat_id=chat_id, last_event_time=( - last_event_time if last_event_time is not None else int(time.time() * 1000) + self._convert_time(last_event_time) + if isinstance(last_event_time, (datetime, timedelta)) + else (int(last_event_time) if last_event_time is not None else int(time.time() * 1000)) ), for_all=for_all, ) diff --git a/src/pymax/api/messages/service.py b/src/pymax/api/messages/service.py index 255031a..75f2e07 100644 --- a/src/pymax/api/messages/service.py +++ b/src/pymax/api/messages/service.py @@ -132,7 +132,14 @@ def _convert_time(self, time: DateTimeUnion) -> int: if isinstance(time, timedelta): return int((datetime.now() + time).timestamp() * 1000) - return time * 1000 + return int(time) * 1000 + + def _convert_period(self, period: DateTimeUnion) -> int: + if isinstance(period, timedelta): + return int(period.total_seconds() * 1000) + if isinstance(period, datetime): + raise ValueError("Period cannot be a datetime") + return int(period) async def send_message( self, @@ -284,9 +291,9 @@ async def fetch_history( chat_id: int, forward: int = 0, backward: int = 40, - backward_time: int = 0, - forward_time: int = 0, - from_: int | None = None, + backward_time: DateTimeUnion = 0, + forward_time: DateTimeUnion = 0, + from_: DateTimeUnion | None = None, item_type: ItemType = ItemType.REGULAR, get_chat: bool = False, get_messages: bool = True, @@ -296,9 +303,15 @@ async def fetch_history( chat_id=chat_id, forward=forward, backward=backward, - backward_time=backward_time, - forward_time=forward_time, - from_=from_ or int(time.time() * 1000), + backward_time=self._convert_period(backward_time), + forward_time=self._convert_period(forward_time), + from_=( + self._convert_time(from_) + if isinstance(from_, (datetime, timedelta)) + # Если from_=0, он будет перезаписан текущим временем (т.к. 0 расценивается как False). + # Чтобы это исправить, можно заменить на: else (int(from_) if from_ is not None else int(time.time() * 1000)) + else (from_ or int(time.time() * 1000)) + ), item_type=item_type, get_chat=get_chat, get_messages=get_messages, diff --git a/src/pymax/infra/chat.py b/src/pymax/infra/chat.py index 669ee1b..81b199a 100644 --- a/src/pymax/infra/chat.py +++ b/src/pymax/infra/chat.py @@ -79,7 +79,7 @@ async def remove_users_from_group( self, chat_id: int, user_ids: list[int], - clean_msg_period: int, + clean_msg_period: DateTimeUnion, ) -> bool: """Удаляет пользователей из группы. @@ -254,7 +254,7 @@ async def leave_channel(self, chat_id: int) -> None: async def delete_chat( self, chat_id: int, - last_event_time: int | None = None, + last_event_time: DateTimeUnion | None = None, for_all: bool = True, ) -> None: """Удаляет чат. @@ -272,7 +272,7 @@ async def delete_chat( for_all=for_all, ) - async def fetch_chats(self, marker: int | None = None) -> list[Chat]: + async def fetch_chats(self, marker: DateTimeUnion | None = None) -> list[Chat]: """Загружает список чатов с сервера и обновляет кеш клиента. Args: diff --git a/src/pymax/infra/message.py b/src/pymax/infra/message.py index 8b7fc1d..19a7a43 100644 --- a/src/pymax/infra/message.py +++ b/src/pymax/infra/message.py @@ -145,9 +145,9 @@ async def fetch_history( chat_id: int, forward: int = 0, backward: int = 40, - backward_time: int = 0, - forward_time: int = 0, - from_time: int | None = None, + backward_time: DateTimeUnion = 0, + forward_time: DateTimeUnion = 0, + from_time: DateTimeUnion | None = None, item_type: ItemType = ItemType.REGULAR, get_chat: bool = False, get_messages: bool = True, diff --git a/src/pymax/types/domain/chat.py b/src/pymax/types/domain/chat.py index 4805a74..4f29af6 100644 --- a/src/pymax/types/domain/chat.py +++ b/src/pymax/types/domain/chat.py @@ -10,7 +10,7 @@ if TYPE_CHECKING: from pymax.api.chats import ChatService - from pymax.api.messages import MessageService + from pymax.api.messages import MessageService, DateTimeUnion from pymax.api.messages.enums import ItemType @@ -190,9 +190,9 @@ async def history( self, forward: int = 0, backward: int = 40, - backward_time: int = 0, - forward_time: int = 0, - from_time: int | None = None, + backward_time: DateTimeUnion = 0, + forward_time: DateTimeUnion = 0, + from_time: DateTimeUnion | None = None, item_type: ItemType | None = None, get_chat: bool = False, get_messages: bool = True, @@ -209,12 +209,12 @@ async def history( :param backward: Сколько сообщений загрузить назад от ``from_time``. :type backward: int :param backward_time: Временное окно назад в миллисекундах. - :type backward_time: int + :type backward_time: DateTimeUnion :param forward_time: Временное окно вперед в миллисекундах. - :type forward_time: int + :type forward_time: DateTimeUnion :param from_time: Точка отсчета в миллисекундах Unix time. Если ``None``, используется текущий момент. - :type from_time: int | None + :type from_time: DateTimeUnion | None :param item_type: Тип элементов истории: обычные или отложенные. :type item_type: ItemType | None :param get_chat: Запросить данные чата вместе с историей. @@ -365,14 +365,14 @@ async def invite( async def remove_users( self, user_ids: list[int], - clean_msg_period: int = 0, + clean_msg_period: DateTimeUnion = 0, ) -> bool: """Удаляет пользователей из группы. :param user_ids: ID пользователей, которых нужно удалить. :type user_ids: list[int] :param clean_msg_period: Период удаления сообщений пользователей. - :type clean_msg_period: int + :type clean_msg_period: DateTimeUnion :returns: ``True``, если сервер принял запрос. :rtype: bool :raises RuntimeError: Если чат не привязан к клиенту.