diff --git a/README.md b/README.md index c10ffd1f..b2df39e2 100644 --- a/README.md +++ b/README.md @@ -74,9 +74,9 @@ tg = Telegram( ) tg.login() -# If this is the first run, the library needs to preload all chats. -# Otherwise, the message will not be sent. -result = tg.get_chats() +# The chat must be in the tdlib database before you can send a message to it. +# `get_chats` loads up to `limit` chats from the main chat list. +result = tg.get_chats(limit=100) result.wait() chat_id = 123456789 diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index c1669130..f9055c2c 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -13,6 +13,8 @@ Changelog - Calling a method after ``stop`` raises ``ClientDestroyedError`` instead of crashing the process. The destroyed tdlib handle was passed to the C library as a ``NULL`` pointer. - An exception raised by an update handler no longer stops the worker thread. The error is logged and the worker keeps processing the queue. - When the handler queue is full, the update is dropped and an error is logged instead of raising ``queue.Full``. +- ``get_chats`` no longer sends the ``offset_order`` and ``offset_chat_id`` parameters. tdlib removed them from ``getChats`` in 1.8.0 and silently ignored them since then, so passing them had no effect. The signature is now ``get_chats(limit=100, chat_list=None)``, which also makes it possible to read the archive and chat folders instead of only the main chat list. This is a breaking change for code that passes the offsets. +- Added ``load_chats``, which wraps the tdlib ``loadChats`` method. [0.19.0] - 2024-06-23 --------------------- diff --git a/examples/clear_group_messages.py b/examples/clear_group_messages.py index 97385a28..ee764618 100644 --- a/examples/clear_group_messages.py +++ b/examples/clear_group_messages.py @@ -82,7 +82,7 @@ def delete_messages(chat_id, message_ids): print(result.update) # get chats - result = tg.get_chats(9223372036854775807) # const 2^62-1: from the first + result = tg.get_chats(limit=100) result.wait() chats = result.update["chat_ids"] diff --git a/examples/send_message.py b/examples/send_message.py index 6adce6be..637cb57b 100644 --- a/examples/send_message.py +++ b/examples/send_message.py @@ -34,9 +34,9 @@ # you must call login method before others tg.login() - # if this is the first run, library needs to preload all chats - # otherwise the message will not be sent - get_chats_result = tg.get_chats() + # the chat must be in the tdlib database before you can send a message to it, + # `get_chats` loads up to `limit` chats from the main chat list + get_chats_result = tg.get_chats(limit=100) # `tdlib` is asynchronous, so `python-telegram` always returns you an `AsyncResult` object. # You can wait for a result with the blocking `wait` method. get_chats_result.wait() diff --git a/telegram/client.py b/telegram/client.py index b780def3..faed7981 100644 --- a/telegram/client.py +++ b/telegram/client.py @@ -396,9 +396,20 @@ def get_user_full_info(self, user_id: int) -> AsyncResult: return self.call_method("getUserFullInfo", params={"user_id": user_id}) - def get_chats(self, offset_order: int = 0, offset_chat_id: int = 0, limit: int = 100) -> AsyncResult: + def get_chats(self, limit: int = 100, chat_list: dict | None = None) -> AsyncResult: """ - Returns a list of chats. + Returns an ordered list of chats from the beginning of a chat list. + + tdlib loads chats from the server until ``limit`` chats are available + or the end of the list is reached, so this method also saves those + chats to the database. + + https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1get_chats.html + + Args: + limit: the maximum number of chats to return + chat_list: the chat list to return chats from, the main chat list + if not set. For example: ``{'@type': 'chatListArchive'}`` Returns: AsyncResult @@ -407,6 +418,7 @@ def get_chats(self, offset_order: int = 0, offset_chat_id: int = 0, limit: int = { '@type': 'chats', + 'total_count': 10, 'chat_ids': [...], '@extra': { 'request_id': '...' @@ -415,8 +427,36 @@ def get_chats(self, offset_order: int = 0, offset_chat_id: int = 0, limit: int = """ data = { "@type": "getChats", - "offset_order": offset_order, - "offset_chat_id": offset_chat_id, + "chat_list": chat_list, + "limit": limit, + } + + return self._send_data(data) + + def load_chats(self, limit: int = 100, chat_list: dict | None = None) -> AsyncResult: + """ + Loads more chats from a chat list. + + The chats are not returned by this method, they are sent through + updates. tdlib chooses how many chats to load and can load fewer + than ``limit``. + + https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1load_chats.html + + Args: + limit: the maximum number of chats to load + chat_list: the chat list to load chats from, the main chat list + if not set. For example: ``{'@type': 'chatListArchive'}`` + + Returns: + AsyncResult + + The update will be ``{'@type': 'ok'}``, or an error with the code + 404 when all the chats in the list have already been loaded. + """ + data = { + "@type": "loadChats", + "chat_list": chat_list, "limit": limit, } diff --git a/tests/test_telegram_methods.py b/tests/test_telegram_methods.py index fc7a8f5c..ab14f468 100644 --- a/tests/test_telegram_methods.py +++ b/tests/test_telegram_methods.py @@ -261,16 +261,42 @@ def test_get_chat(self, telegram): telegram._tdjson.send.assert_called_once_with(exp_data) def test_get_chats(self, telegram): - offset_order = 1 - offset_chat_id = 1 limit = 100 - async_result = telegram.get_chats(offset_order=offset_order, offset_chat_id=offset_chat_id, limit=limit) + async_result = telegram.get_chats(limit=limit) exp_data = { "@type": "getChats", - "offset_order": offset_order, - "offset_chat_id": offset_chat_id, + "chat_list": None, + "limit": limit, + "@extra": {"request_id": async_result.id}, + } + + telegram._tdjson.send.assert_called_once_with(exp_data) + + def test_get_chats_with_chat_list(self, telegram): + limit = 10 + chat_list = {"@type": "chatListArchive"} + + async_result = telegram.get_chats(limit=limit, chat_list=chat_list) + + exp_data = { + "@type": "getChats", + "chat_list": chat_list, + "limit": limit, + "@extra": {"request_id": async_result.id}, + } + + telegram._tdjson.send.assert_called_once_with(exp_data) + + def test_load_chats(self, telegram): + limit = 100 + + async_result = telegram.load_chats(limit=limit) + + exp_data = { + "@type": "loadChats", + "chat_list": None, "limit": limit, "@extra": {"request_id": async_result.id}, }