mirror of
https://github.com/TeamPGM/pyrogram.git
synced 2024-11-18 05:30:15 +00:00
Merge branch 'develop' into asyncio
# Conflicts: # pyrogram/client/dispatcher/dispatcher.py # pyrogram/client/methods/messages/get_history.py
This commit is contained in:
commit
dd86aba9d3
@ -87,3 +87,4 @@ MESSAGE_POLL_CLOSED You can't interact with a closed poll
|
|||||||
MEDIA_INVALID The media is invalid
|
MEDIA_INVALID The media is invalid
|
||||||
BOT_SCORE_NOT_MODIFIED The bot score was not modified
|
BOT_SCORE_NOT_MODIFIED The bot score was not modified
|
||||||
USER_BOT_REQUIRED The method can be used by bots only
|
USER_BOT_REQUIRED The method can be used by bots only
|
||||||
|
IMAGE_PROCESS_FAILED The server failed to process your image
|
|
@ -1,2 +1,3 @@
|
|||||||
id message
|
id message
|
||||||
FLOOD_WAIT_X A wait of {x} seconds is required
|
FLOOD_WAIT_X A wait of {x} seconds is required
|
||||||
|
TAKEOUT_INIT_DELAY_X You have to confirm the data export request using one of your mobile devices or wait {x} seconds
|
||||||
|
|
@ -99,6 +99,7 @@ To get started, press the Next button.
|
|||||||
resources/ErrorHandling
|
resources/ErrorHandling
|
||||||
resources/TestServers
|
resources/TestServers
|
||||||
resources/AdvancedUsage
|
resources/AdvancedUsage
|
||||||
|
resources/VoiceCalls
|
||||||
resources/Changelog
|
resources/Changelog
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
|
@ -91,13 +91,14 @@ Stop Propagation
|
|||||||
In order to prevent further propagation of an update in the dispatching phase, you can do *one* of the following:
|
In order to prevent further propagation of an update in the dispatching phase, you can do *one* of the following:
|
||||||
|
|
||||||
- Call the update's bound-method ``.stop_propagation()`` (preferred way).
|
- Call the update's bound-method ``.stop_propagation()`` (preferred way).
|
||||||
- Manually ``raise StopPropagation`` error (more suitable for raw updates only).
|
- Manually ``raise StopPropagation`` exception (more suitable for raw updates only).
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
Note that ``.stop_propagation()`` is just an elegant and intuitive way to raise a ``StopPropagation`` error;
|
Internally, the propagation is stopped by handling a custom exception. ``.stop_propagation()`` is just an elegant
|
||||||
this means that any code coming *after* calling it won't be executed as your function just raised a custom exception
|
and intuitive way to ``raise StopPropagation``; this also means that any code coming *after* calling the method
|
||||||
to signal the dispatcher not to propagate the update anymore.
|
won't be executed as your function just raised an exception to signal the dispatcher not to propagate the
|
||||||
|
update anymore.
|
||||||
|
|
||||||
Example with ``stop_propagation()``:
|
Example with ``stop_propagation()``:
|
||||||
|
|
||||||
@ -139,10 +140,82 @@ Example with ``raise StopPropagation``:
|
|||||||
def _(client, message):
|
def _(client, message):
|
||||||
print(2)
|
print(2)
|
||||||
|
|
||||||
The handler in group number 2 will never be executed because the propagation was stopped before. The output of both
|
Each handler is registered in a different group, but the handler in group number 2 will never be executed because the
|
||||||
examples will be:
|
propagation was stopped earlier. The output of both (equivalent) examples will be:
|
||||||
|
|
||||||
.. code-block:: text
|
.. code-block:: text
|
||||||
|
|
||||||
0
|
0
|
||||||
1
|
1
|
||||||
|
|
||||||
|
Continue Propagation
|
||||||
|
^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
As opposed to `stopping the update propagation <#stop-propagation>`_ and also as an alternative to the
|
||||||
|
`handler groups <#handler-groups>`_, you can signal the internal dispatcher to continue the update propagation within
|
||||||
|
the group regardless of the next handler's filters. This allows you to register multiple handlers with overlapping
|
||||||
|
filters in the same group; to let the dispatcher process the next handler you can do *one* of the following in each
|
||||||
|
handler you want to grant permission to continue:
|
||||||
|
|
||||||
|
- Call the update's bound-method ``.continue_propagation()`` (preferred way).
|
||||||
|
- Manually ``raise ContinuePropagation`` exception (more suitable for raw updates only).
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Internally, the propagation is continued by handling a custom exception. ``.continue_propagation()`` is just an
|
||||||
|
elegant and intuitive way to ``raise ContinuePropagation``; this also means that any code coming *after* calling the
|
||||||
|
method won't be executed as your function just raised an exception to signal the dispatcher to continue with the
|
||||||
|
next available handler.
|
||||||
|
|
||||||
|
|
||||||
|
Example with ``continue_propagation()``:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
@app.on_message(Filters.private)
|
||||||
|
def _(client, message):
|
||||||
|
print(0)
|
||||||
|
message.continue_propagation()
|
||||||
|
|
||||||
|
|
||||||
|
@app.on_message(Filters.private)
|
||||||
|
def _(client, message):
|
||||||
|
print(1)
|
||||||
|
message.continue_propagation()
|
||||||
|
|
||||||
|
|
||||||
|
@app.on_message(Filters.private)
|
||||||
|
def _(client, message):
|
||||||
|
print(2)
|
||||||
|
|
||||||
|
Example with ``raise ContinuePropagation``:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from pyrogram import ContinuePropagation
|
||||||
|
|
||||||
|
@app.on_message(Filters.private)
|
||||||
|
def _(client, message):
|
||||||
|
print(0)
|
||||||
|
raise ContinuePropagation
|
||||||
|
|
||||||
|
|
||||||
|
@app.on_message(Filters.private)
|
||||||
|
def _(client, message):
|
||||||
|
print(1)
|
||||||
|
raise ContinuePropagation
|
||||||
|
|
||||||
|
|
||||||
|
@app.on_message(Filters.private)
|
||||||
|
def _(client, message):
|
||||||
|
print(2)
|
||||||
|
|
||||||
|
Three handlers are registered in the same group, and all of them will be executed because the propagation was continued
|
||||||
|
in each handler (except in the last one, where is useless to do so since there is no more handlers after).
|
||||||
|
The output of both (equivalent) examples will be:
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
0
|
||||||
|
1
|
||||||
|
2
|
10
docs/source/resources/VoiceCalls.rst
Normal file
10
docs/source/resources/VoiceCalls.rst
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
Voice Calls
|
||||||
|
===========
|
||||||
|
|
||||||
|
A working proof-of-concept of Telegram voice calls using Pyrogram can be found here:
|
||||||
|
https://github.com/bakatrouble/pylibtgvoip. Thanks to `@bakatrouble <https://t.me/bakatrouble>`_.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
This page will be updated with more information once voice calls become eventually more usable and more integrated
|
||||||
|
in Pyrogram itself.
|
@ -6,13 +6,15 @@ to make it only work for specific messages in a specific chat.
|
|||||||
|
|
||||||
from pyrogram import Client, Emoji, Filters
|
from pyrogram import Client, Emoji, Filters
|
||||||
|
|
||||||
MENTION = "[{}](tg://user?id={})"
|
TARGET = "PyrogramChat" # Target chat. Can also be a list of multiple chat ids/usernames
|
||||||
MESSAGE = "{} Welcome to [Pyrogram](https://docs.pyrogram.ml/)'s group chat {}!"
|
MENTION = "[{}](tg://user?id={})" # User mention markup
|
||||||
|
MESSAGE = "{} Welcome to [Pyrogram](https://docs.pyrogram.ml/)'s group chat {}!" # Welcome message
|
||||||
|
|
||||||
app = Client("my_account")
|
app = Client("my_account")
|
||||||
|
|
||||||
|
|
||||||
@app.on_message(Filters.chat("PyrogramChat") & Filters.new_chat_members)
|
# Filter in only new_chat_members updates generated in TARGET chat
|
||||||
|
@app.on_message(Filters.chat(TARGET) & Filters.new_chat_members)
|
||||||
def welcome(client, message):
|
def welcome(client, message):
|
||||||
# Build the new members list (with mentions) by using their first_name
|
# Build the new members list (with mentions) by using their first_name
|
||||||
new_members = [MENTION.format(i.first_name, i.id) for i in message.new_chat_members]
|
new_members = [MENTION.format(i.first_name, i.id) for i in message.new_chat_members]
|
||||||
|
@ -40,7 +40,8 @@ from .client.types import (
|
|||||||
Location, Message, MessageEntity, Dialog, Dialogs, Photo, PhotoSize, Sticker, User, UserStatus,
|
Location, Message, MessageEntity, Dialog, Dialogs, Photo, PhotoSize, Sticker, User, UserStatus,
|
||||||
UserProfilePhotos, Venue, Animation, Video, VideoNote, Voice, CallbackQuery, Messages, ForceReply,
|
UserProfilePhotos, Venue, Animation, Video, VideoNote, Voice, CallbackQuery, Messages, ForceReply,
|
||||||
InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove,
|
InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove,
|
||||||
Poll, PollOption, ChatPreview, StopPropagation, Game, CallbackGame, GameHighScore, GameHighScores
|
Poll, PollOption, ChatPreview, StopPropagation, ContinuePropagation, Game, CallbackGame, GameHighScore,
|
||||||
|
GameHighScores
|
||||||
)
|
)
|
||||||
from .client import (
|
from .client import (
|
||||||
Client, ChatAction, ParseMode, Emoji,
|
Client, ChatAction, ParseMode, Emoji,
|
||||||
|
@ -141,8 +141,9 @@ class Client(Methods, BaseClient):
|
|||||||
Only applicable for new sessions.
|
Only applicable for new sessions.
|
||||||
|
|
||||||
first_name (``str``, *optional*):
|
first_name (``str``, *optional*):
|
||||||
Pass a First Name to avoid entering it manually. It will be used to automatically
|
Pass a First Name as string to avoid entering it manually. Or pass a callback function which accepts no
|
||||||
create a new Telegram account in case the phone number you passed is not registered yet.
|
arguments and must return the correct name as string (e.g., "Dan"). It will be used to automatically create
|
||||||
|
a new Telegram account in case the phone number you passed is not registered yet.
|
||||||
Only applicable for new sessions.
|
Only applicable for new sessions.
|
||||||
|
|
||||||
last_name (``str``, *optional*):
|
last_name (``str``, *optional*):
|
||||||
@ -160,7 +161,8 @@ class Client(Methods, BaseClient):
|
|||||||
Path of the configuration file. Defaults to ./config.ini
|
Path of the configuration file. Defaults to ./config.ini
|
||||||
|
|
||||||
plugins (``dict``, *optional*):
|
plugins (``dict``, *optional*):
|
||||||
TODO: doctrings
|
Your Smart Plugins settings as dict, e.g.: *dict(root="plugins")*.
|
||||||
|
This is an alternative way to setup plugins if you don't want to use the *config.ini* file.
|
||||||
|
|
||||||
no_updates (``bool``, *optional*):
|
no_updates (``bool``, *optional*):
|
||||||
Pass True to completely disable incoming updates for the current session.
|
Pass True to completely disable incoming updates for the current session.
|
||||||
|
@ -125,34 +125,36 @@ class Dispatcher:
|
|||||||
|
|
||||||
parser = self.update_parsers.get(type(update), None)
|
parser = self.update_parsers.get(type(update), None)
|
||||||
|
|
||||||
if parser is None:
|
parsed_update, handler_type = (
|
||||||
continue
|
await parser(update, users, chats)
|
||||||
|
if parser is not None
|
||||||
parsed_update, handler_type = await parser(update, users, chats)
|
else (None, type(None))
|
||||||
|
)
|
||||||
|
|
||||||
for group in self.groups.values():
|
for group in self.groups.values():
|
||||||
try:
|
for handler in group:
|
||||||
for handler in group:
|
args = None
|
||||||
args = None
|
|
||||||
|
|
||||||
if isinstance(handler, RawUpdateHandler):
|
if isinstance(handler, handler_type):
|
||||||
args = (update, users, chats)
|
if handler.check(parsed_update):
|
||||||
elif isinstance(handler, handler_type):
|
args = (parsed_update,)
|
||||||
if handler.check(parsed_update):
|
elif isinstance(handler, RawUpdateHandler):
|
||||||
args = (parsed_update,)
|
args = (update, users, chats)
|
||||||
|
|
||||||
if args is None:
|
if args is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await handler.callback(self.client, *args)
|
await handler.callback(self.client, *args)
|
||||||
except StopIteration:
|
except pyrogram.StopPropagation:
|
||||||
raise
|
raise
|
||||||
except Exception as e:
|
except pyrogram.ContinuePropagation:
|
||||||
log.error(e, exc_info=True)
|
continue
|
||||||
|
except Exception as e:
|
||||||
|
log.error(e, exc_info=True)
|
||||||
|
|
||||||
break
|
|
||||||
except StopIteration:
|
|
||||||
break
|
break
|
||||||
|
except pyrogram.StopPropagation:
|
||||||
|
pass
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.error(e, exc_info=True)
|
log.error(e, exc_info=True)
|
||||||
|
@ -45,10 +45,3 @@ class CallbackQueryHandler(Handler):
|
|||||||
|
|
||||||
def __init__(self, callback: callable, filters=None):
|
def __init__(self, callback: callable, filters=None):
|
||||||
super().__init__(callback, filters)
|
super().__init__(callback, filters)
|
||||||
|
|
||||||
def check(self, callback_query):
|
|
||||||
return (
|
|
||||||
self.filters(callback_query)
|
|
||||||
if callable(self.filters)
|
|
||||||
else True
|
|
||||||
)
|
|
||||||
|
@ -48,8 +48,4 @@ class DeletedMessagesHandler(Handler):
|
|||||||
super().__init__(callback, filters)
|
super().__init__(callback, filters)
|
||||||
|
|
||||||
def check(self, messages):
|
def check(self, messages):
|
||||||
return (
|
return super().check(messages.messages[0])
|
||||||
self.filters(messages.messages[0])
|
|
||||||
if callable(self.filters)
|
|
||||||
else True
|
|
||||||
)
|
|
||||||
|
@ -21,3 +21,10 @@ class Handler:
|
|||||||
def __init__(self, callback: callable, filters=None):
|
def __init__(self, callback: callable, filters=None):
|
||||||
self.callback = callback
|
self.callback = callback
|
||||||
self.filters = filters
|
self.filters = filters
|
||||||
|
|
||||||
|
def check(self, update):
|
||||||
|
return (
|
||||||
|
self.filters(update)
|
||||||
|
if callable(self.filters)
|
||||||
|
else True
|
||||||
|
)
|
||||||
|
@ -46,10 +46,3 @@ class MessageHandler(Handler):
|
|||||||
|
|
||||||
def __init__(self, callback: callable, filters=None):
|
def __init__(self, callback: callable, filters=None):
|
||||||
super().__init__(callback, filters)
|
super().__init__(callback, filters)
|
||||||
|
|
||||||
def check(self, message):
|
|
||||||
return (
|
|
||||||
self.filters(message)
|
|
||||||
if callable(self.filters)
|
|
||||||
else True
|
|
||||||
)
|
|
||||||
|
@ -45,10 +45,3 @@ class UserStatusHandler(Handler):
|
|||||||
|
|
||||||
def __init__(self, callback: callable, filters=None):
|
def __init__(self, callback: callable, filters=None):
|
||||||
super().__init__(callback, filters)
|
super().__init__(callback, filters)
|
||||||
|
|
||||||
def check(self, user_status):
|
|
||||||
return (
|
|
||||||
self.filters(user_status)
|
|
||||||
if callable(self.filters)
|
|
||||||
else True
|
|
||||||
)
|
|
||||||
|
@ -16,12 +16,17 @@
|
|||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import time
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
import pyrogram
|
import pyrogram
|
||||||
from pyrogram.api import functions
|
from pyrogram.api import functions
|
||||||
|
from pyrogram.api.errors import FloodWait
|
||||||
from ...ext import BaseClient
|
from ...ext import BaseClient
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class GetHistory(BaseClient):
|
class GetHistory(BaseClient):
|
||||||
async def get_history(self,
|
async def get_history(self,
|
||||||
@ -66,21 +71,28 @@ class GetHistory(BaseClient):
|
|||||||
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
|
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
messages = await pyrogram.Messages._parse(
|
while True:
|
||||||
self,
|
try:
|
||||||
await self.send(
|
messages = await pyrogram.Messages._parse(
|
||||||
functions.messages.GetHistory(
|
self,
|
||||||
peer=await self.resolve_peer(chat_id),
|
await self.send(
|
||||||
offset_id=offset_id,
|
functions.messages.GetHistory(
|
||||||
offset_date=offset_date,
|
peer=await self.resolve_peer(chat_id),
|
||||||
add_offset=offset * (-1 if reverse else 1) - (limit if reverse else 0),
|
offset_id=offset_id,
|
||||||
limit=limit,
|
offset_date=offset_date,
|
||||||
max_id=0,
|
add_offset=offset * (-1 if reverse else 1) - (limit if reverse else 0),
|
||||||
min_id=0,
|
limit=limit,
|
||||||
hash=0
|
max_id=0,
|
||||||
|
min_id=0,
|
||||||
|
hash=0
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
except FloodWait as e:
|
||||||
)
|
log.warning("Sleeping for {}s".format(e.x))
|
||||||
|
await asyncio.sleep(e.x)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
if reverse:
|
if reverse:
|
||||||
messages.messages.reverse()
|
messages.messages.reverse()
|
||||||
|
@ -30,7 +30,7 @@ from .messages_and_media import (
|
|||||||
Sticker, Venue, Video, VideoNote, Voice, UserProfilePhotos,
|
Sticker, Venue, Video, VideoNote, Voice, UserProfilePhotos,
|
||||||
Message, Messages, MessageEntity, Poll, PollOption, Game
|
Message, Messages, MessageEntity, Poll, PollOption, Game
|
||||||
)
|
)
|
||||||
from .update import StopPropagation
|
from .update import StopPropagation, ContinuePropagation
|
||||||
from .user_and_chats import (
|
from .user_and_chats import (
|
||||||
Chat, ChatMember, ChatMembers, ChatPhoto,
|
Chat, ChatMember, ChatMembers, ChatPhoto,
|
||||||
Dialog, Dialogs, User, UserStatus, ChatPreview
|
Dialog, Dialogs, User, UserStatus, ChatPreview
|
||||||
|
@ -40,15 +40,15 @@ class CallbackQuery(PyrogramType, Update):
|
|||||||
Sender.
|
Sender.
|
||||||
|
|
||||||
chat_instance (``str``, *optional*):
|
chat_instance (``str``, *optional*):
|
||||||
|
Global identifier, uniquely corresponding to the chat to which the message with the callback button was
|
||||||
|
sent. Useful for high scores in games.
|
||||||
|
|
||||||
|
message (:obj:`Message <pyrogram.Message>`, *optional*):
|
||||||
Message with the callback button that originated the query. Note that message content and message date will
|
Message with the callback button that originated the query. Note that message content and message date will
|
||||||
not be available if the message is too old.
|
not be available if the message is too old.
|
||||||
|
|
||||||
message (:obj:`Message <pyrogram.Message>`, *optional*):
|
|
||||||
Identifier of the message sent via the bot in inline mode, that originated the query.
|
|
||||||
|
|
||||||
inline_message_id (``str``):
|
inline_message_id (``str``):
|
||||||
Global identifier, uniquely corresponding to the chat to which the message with the callback button was
|
Identifier of the message sent via the bot in inline mode, that originated the query.
|
||||||
sent. Useful for high scores in games.
|
|
||||||
|
|
||||||
data (``bytes``, *optional*):
|
data (``bytes``, *optional*):
|
||||||
Data associated with the callback button. Be aware that a bad client can send arbitrary data in this field.
|
Data associated with the callback button. Be aware that a bad client can send arbitrary data in this field.
|
||||||
@ -72,9 +72,9 @@ class CallbackQuery(PyrogramType, Update):
|
|||||||
|
|
||||||
self.id = id
|
self.id = id
|
||||||
self.from_user = from_user
|
self.from_user = from_user
|
||||||
|
self.chat_instance = chat_instance
|
||||||
self.message = message
|
self.message = message
|
||||||
self.inline_message_id = inline_message_id
|
self.inline_message_id = inline_message_id
|
||||||
self.chat_instance = chat_instance
|
|
||||||
self.data = data
|
self.data = data
|
||||||
self.game_short_name = game_short_name
|
self.game_short_name = game_short_name
|
||||||
|
|
||||||
|
@ -21,6 +21,13 @@ class StopPropagation(StopIteration):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ContinuePropagation(StopIteration):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Update:
|
class Update:
|
||||||
def stop_propagation(self):
|
def stop_propagation(self):
|
||||||
raise StopPropagation
|
raise StopPropagation
|
||||||
|
|
||||||
|
def continue_propagation(self):
|
||||||
|
raise ContinuePropagation
|
||||||
|
2
setup.py
2
setup.py
@ -127,7 +127,7 @@ class Generate(Command):
|
|||||||
docs_compiler.start()
|
docs_compiler.start()
|
||||||
|
|
||||||
|
|
||||||
if len(argv) > 1 and argv[1] in ["bdist_wheel", "install"]:
|
if len(argv) > 1 and argv[1] in ["bdist_wheel", "install", "develop"]:
|
||||||
error_compiler.start()
|
error_compiler.start()
|
||||||
api_compiler.start()
|
api_compiler.start()
|
||||||
docs_compiler.start()
|
docs_compiler.start()
|
||||||
|
Loading…
Reference in New Issue
Block a user