diff --git a/compiler/docs/compiler.py b/compiler/docs/compiler.py index 6b596b2f..8afd99f8 100644 --- a/compiler/docs/compiler.py +++ b/compiler/docs/compiler.py @@ -176,6 +176,7 @@ def pyrogram_api(): retract_vote send_dice search_messages + search_global download_media """, chats=""" diff --git a/docs/source/faq.rst b/docs/source/faq.rst index b064e10b..e56736d8 100644 --- a/docs/source/faq.rst +++ b/docs/source/faq.rst @@ -85,7 +85,7 @@ Requests against the official bot API endpoint are made via JSON/HTTP, but are h application that implements the MTProto protocol -- just like Pyrogram -- and uses its own API key, which is always required, but hidden to the public. -.. figure:: https://i.imgur.com/C108qkX.png +.. figure:: https://i.imgur.com/WvwBoZo.png :align: center Using MTProto is the only way to communicate with the actual Telegram servers, and the main API requires developers to diff --git a/docs/source/topics/mtproto-vs-botapi.rst b/docs/source/topics/mtproto-vs-botapi.rst index 3668d0da..12b73b53 100644 --- a/docs/source/topics/mtproto-vs-botapi.rst +++ b/docs/source/topics/mtproto-vs-botapi.rst @@ -35,7 +35,7 @@ accounts that are authorized via tokens instead of phone numbers. The Bot API is Telegram API, but runs on an intermediate server application that in turn communicates with the actual Telegram servers using MTProto. -.. figure:: https://i.imgur.com/C108qkX.png +.. figure:: https://i.imgur.com/WvwBoZo.png :align: center .. _Bot API: https://core.telegram.org/bots/api diff --git a/pyrogram/client/methods/bots/answer_callback_query.py b/pyrogram/client/methods/bots/answer_callback_query.py index a3effe2b..ff9a5b51 100644 --- a/pyrogram/client/methods/bots/answer_callback_query.py +++ b/pyrogram/client/methods/bots/answer_callback_query.py @@ -36,20 +36,20 @@ class AnswerCallbackQuery(BaseClient): callback_query_id (``str``): Unique identifier for the query to be answered. - text (``str``): + text (``str`` *optional*): Text of the notification. If not specified, nothing will be shown to the user, 0-200 characters. - show_alert (``bool``): + show_alert (``bool``, *optional*): If true, an alert will be shown by the client instead of a notification at the top of the chat screen. Defaults to False. - url (``str``): + url (``str``, *optional*): URL that will be opened by the user's client. If you have created a Game and accepted the conditions via @Botfather, specify the URL that opens your game – note that this will only work if the query comes from a callback_game button. Otherwise, you may use links like t.me/your_bot?start=XXXX that open your bot with a parameter. - cache_time (``int``): + cache_time (``int``, *optional*): The maximum amount of time in seconds that the result of the callback query may be cached client-side. Telegram apps will support caching starting in version 3.14. Defaults to 0. @@ -59,6 +59,9 @@ class AnswerCallbackQuery(BaseClient): Example: .. code-block:: python + # Answer only (remove the spinning circles) + app.answer_callback_query(query_id) + # Answer without alert app.answer_callback_query(query_id, text=text) @@ -70,7 +73,7 @@ class AnswerCallbackQuery(BaseClient): query_id=int(callback_query_id), cache_time=cache_time, alert=show_alert or None, - message=text, - url=url + message=text or None, + url=url or None ) ) diff --git a/pyrogram/client/methods/messages/__init__.py b/pyrogram/client/methods/messages/__init__.py index 3df4e7ce..e78d2bc5 100644 --- a/pyrogram/client/methods/messages/__init__.py +++ b/pyrogram/client/methods/messages/__init__.py @@ -33,11 +33,14 @@ from .get_messages import GetMessages from .iter_history import IterHistory from .read_history import ReadHistory from .retract_vote import RetractVote +from .search_global import SearchGlobal +from .search_messages import SearchMessages from .send_animation import SendAnimation from .send_audio import SendAudio from .send_cached_media import SendCachedMedia from .send_chat_action import SendChatAction from .send_contact import SendContact +from .send_dice import SendDice from .send_document import SendDocument from .send_location import SendLocation from .send_media_group import SendMediaGroup @@ -51,8 +54,6 @@ from .send_video_note import SendVideoNote from .send_voice import SendVoice from .stop_poll import StopPoll from .vote_poll import VotePoll -from .send_dice import SendDice -from .search_messages import SearchMessages class Messages( @@ -92,6 +93,7 @@ class Messages( EditInlineMedia, EditInlineReplyMarkup, SendDice, - SearchMessages + SearchMessages, + SearchGlobal ): pass diff --git a/pyrogram/client/methods/messages/search_global.py b/pyrogram/client/methods/messages/search_global.py new file mode 100644 index 00000000..2a889e31 --- /dev/null +++ b/pyrogram/client/methods/messages/search_global.py @@ -0,0 +1,99 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-2020 Dan +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from typing import Generator, Optional + +from async_generator import async_generator, yield_ + +import pyrogram +from pyrogram.api import functions, types +from pyrogram.client.ext import BaseClient, utils + + +class SearchGlobal(BaseClient): + @async_generator + async def search_global( + self, + query: str, + limit: int = 0, + ) -> Optional[Generator["pyrogram.Message", None, None]]: + """Search messages globally from all of your chats. + + .. note:: + + Due to server-side limitations, you can only get up to around ~10,000 messages and each message + retrieved will not have any *reply_to_message* field. + + Parameters: + query (``str``): + Text query string. + + limit (``int``, *optional*): + Limits the number of messages to be retrieved. + By default, no limit is applied and all messages are returned. + + Returns: + ``Generator``: A generator yielding :obj:`Message` objects. + + Example: + .. code-block:: python + + # Search for "pyrogram". Get the first 420 results + for message in app.search_global("pyrogram", limit=420): + print(message.text) + """ + current = 0 + # There seems to be an hard limit of 10k, beyond which Telegram starts spitting one message at a time. + total = abs(limit) or (1 << 31) + limit = min(100, total) + + offset_date = 0 + offset_peer = types.InputPeerEmpty() + offset_id = 0 + + while True: + messages = await utils.parse_messages( + self, + await self.send( + functions.messages.SearchGlobal( + q=query, + offset_rate=offset_date, + offset_peer=offset_peer, + offset_id=offset_id, + limit=limit + ) + ), + replies=0 + ) + + if not messages: + return + + last = messages[-1] + + offset_date = last.date + offset_peer = await self.resolve_peer(last.chat.id) + offset_id = last.message_id + + for message in messages: + await yield_(message) + + current += 1 + + if current >= total: + return diff --git a/pyrogram/client/methods/messages/send_dice.py b/pyrogram/client/methods/messages/send_dice.py index 719ecba4..89bdeed5 100644 --- a/pyrogram/client/methods/messages/send_dice.py +++ b/pyrogram/client/methods/messages/send_dice.py @@ -38,7 +38,7 @@ class SendDice(BaseClient): "pyrogram.ForceReply" ] = None ) -> Union["pyrogram.Message", None]: - """Send a dice. + """Send a dice with a random value from 1 to 6. Parameters: chat_id (``int`` | ``str``): @@ -47,8 +47,8 @@ class SendDice(BaseClient): For a contact that exists in your Telegram address book you can use his phone number (str). emoji (``str``, *optional*): - Emoji on which the dice throw animation is based. Currently, must be one of "🎲" or "🎯". - Defauts to "🎲". + Emoji on which the dice throw animation is based. Currently, must be one of "🎲", "🎯" or "🏀". + Defaults to "🎲". disable_notification (``bool``, *optional*): Sends the message silently. @@ -75,6 +75,9 @@ class SendDice(BaseClient): # Send a dart app.send_dice("pyrogramlounge", "🎯") + + # Send a basketball + app.send_dice("pyrogramlounge", "🏀") """ r = await self.send( diff --git a/pyrogram/client/types/bots_and_keyboards/callback_query.py b/pyrogram/client/types/bots_and_keyboards/callback_query.py index cb2df772..a15a7c35 100644 --- a/pyrogram/client/types/bots_and_keyboards/callback_query.py +++ b/pyrogram/client/types/bots_and_keyboards/callback_query.py @@ -143,20 +143,20 @@ class CallbackQuery(Object, Update): callback_query.answer("Hello", show_alert=True) Parameters: - text (``str``): + text (``str``, *optional*): Text of the notification. If not specified, nothing will be shown to the user, 0-200 characters. - show_alert (``bool``): + show_alert (``bool`` *optional*): If true, an alert will be shown by the client instead of a notification at the top of the chat screen. Defaults to False. - url (``str``): + url (``str`` *optional*): URL that will be opened by the user's client. If you have created a Game and accepted the conditions via @Botfather, specify the URL that opens your game – note that this will only work if the query comes from a callback_game button. Otherwise, you may use links like t.me/your_bot?start=XXXX that open your bot with a parameter. - cache_time (``int``): + cache_time (``int`` *optional*): The maximum amount of time in seconds that the result of the callback query may be cached client-side. Telegram apps will support caching starting in version 3.14. Defaults to 0. """ diff --git a/pyrogram/client/types/user_and_chats/chat.py b/pyrogram/client/types/user_and_chats/chat.py index 482d44a1..1b28319d 100644 --- a/pyrogram/client/types/user_and_chats/chat.py +++ b/pyrogram/client/types/user_and_chats/chat.py @@ -252,8 +252,9 @@ class Chat(Object): if full_chat.id == c.id: chat = c - if full_chat.linked_chat_id == c.id: - linked_chat = c + if isinstance(chat_full, types.ChannelFull): + if full_chat.linked_chat_id == c.id: + linked_chat = c if isinstance(full_chat, types.ChatFull): parsed_chat = Chat._parse_chat_chat(client, chat) @@ -268,7 +269,8 @@ class Chat(Object): # TODO: Add StickerSet type parsed_chat.can_set_sticker_set = full_chat.can_set_stickers parsed_chat.sticker_set_name = getattr(full_chat.stickerset, "short_name", None) - parsed_chat.linked_chat = Chat._parse_channel_chat(client, linked_chat) + if linked_chat: + parsed_chat.linked_chat = Chat._parse_channel_chat(client, linked_chat) if full_chat.pinned_msg_id: parsed_chat.pinned_message = await client.get_messages( @@ -545,13 +547,13 @@ class Chat(Object): client.restrict_chat_member( chat_id=chat_id, user_id=user_id, - permissions=ChatPermission() + permissions=ChatPermissions() ) Example: .. code-block:: python - chat.restrict_member(user_id, ChatPermission()) + chat.restrict_member(user_id, ChatPermissions()) Parameters: user_id (``int`` | ``str``):