diff --git a/compiler/docs/template/bound-methods.rst b/compiler/docs/template/bound-methods.rst index 963b5251..13a51b04 100644 --- a/compiler/docs/template/bound-methods.rst +++ b/compiler/docs/template/bound-methods.rst @@ -15,7 +15,7 @@ some of the required arguments. @app.on_message() def hello(client, message) - message.reply("hi") + message.reply_text("hi") app.run() diff --git a/compiler/docs/template/types.rst b/compiler/docs/template/types.rst index 4cc0cf92..e6c9a5d7 100644 --- a/compiler/docs/template/types.rst +++ b/compiler/docs/template/types.rst @@ -1,12 +1,12 @@ Available Types =============== -This page is about Pyrogram types. All types listed here are accessible through the main package directly. +This page is about Pyrogram types. All types listed here are accessible through ``types`` package. .. code-block:: python :emphasize-lines: 1 - from pyrogram import User, Message, ... + from pyrogram.types import User, Message, ... .. note:: diff --git a/docs/source/api/handlers.rst b/docs/source/api/handlers.rst index 80d2cf73..4e79d139 100644 --- a/docs/source/api/handlers.rst +++ b/docs/source/api/handlers.rst @@ -5,7 +5,7 @@ Handlers are used to instruct Pyrogram about which kind of updates you'd like to For a much more convenient way of registering callback functions have a look at :doc:`Decorators ` instead. .. code-block:: python - :emphasize-lines: 1, 10 + :emphasize-lines: 2, 11 from pyrogram import Client from pyrogram.handlers import MessageHandler diff --git a/docs/source/intro/install.rst b/docs/source/intro/install.rst index 9056d1f5..813961dc 100644 --- a/docs/source/intro/install.rst +++ b/docs/source/intro/install.rst @@ -40,12 +40,12 @@ Pyrogram is always evolving, although new releases on PyPI are published only wh doesn't mean you can't try new features right now! In case you'd like to try out the latest Pyrogram features, the `GitHub repo`_ is always kept updated with new changes; -you can install the development version straight from the ``develop`` branch using this command (note "develop.zip" in +you can install the development version straight from the ``master`` branch using this command (note "master.zip" in the link): .. code-block:: text - $ pip3 install -U https://github.com/pyrogram/pyrogram/archive/develop.zip + $ pip3 install -U https://github.com/pyrogram/pyrogram/archive/master.zip Verifying --------- diff --git a/docs/source/intro/quickstart.rst b/docs/source/intro/quickstart.rst index 593403b9..eeb98482 100644 --- a/docs/source/intro/quickstart.rst +++ b/docs/source/intro/quickstart.rst @@ -1,8 +1,8 @@ Quick Start =========== -The next few steps serve as a quick start for all new Pyrogrammers that want to see Pyrogram in action as fast as -possible. Let's go! +The next few steps serve as a quick start for all new :term:`Pyrogrammers ` that want to see Pyrogram in +action as fast as possible. Let's go! Get Pyrogram Real Fast ---------------------- diff --git a/docs/source/start/errors.rst b/docs/source/start/errors.rst index ff9ccf5b..f5747d77 100644 --- a/docs/source/start/errors.rst +++ b/docs/source/start/errors.rst @@ -1,7 +1,7 @@ Error Handling ============== -Errors are inevitable when working with the API, and they must be correctly handled with ``try..except`` blocks in order +Errors are inevitable when working with the API, and they can be correctly handled with ``try...except`` blocks in order to control the behaviour of your application. Pyrogram errors all live inside the ``errors`` package: .. code-block:: python diff --git a/docs/source/start/examples/bot_keyboards.rst b/docs/source/start/examples/bot_keyboards.rst index 343e22c9..6e288c43 100644 --- a/docs/source/start/examples/bot_keyboards.rst +++ b/docs/source/start/examples/bot_keyboards.rst @@ -11,7 +11,8 @@ like send_audio(), send_document(), send_location(), etc... .. code-block:: python - from pyrogram import Client, ReplyKeyboardMarkup, InlineKeyboardMarkup, InlineKeyboardButton + from pyrogram import Client + from pyrogram.types import ReplyKeyboardMarkup, InlineKeyboardMarkup, InlineKeyboardButton # Create a client using your bot token app = Client("my_bot", bot_token="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11") diff --git a/docs/source/start/examples/echobot.rst b/docs/source/start/examples/echobot.rst index a751a2cd..61dc9929 100644 --- a/docs/source/start/examples/echobot.rst +++ b/docs/source/start/examples/echobot.rst @@ -3,19 +3,19 @@ echobot This simple echo bot replies to every private text message. -It uses the @on_message decorator to register a MessageHandler and applies two filters on it: -Filters.text and Filters.private to make sure it will reply to private text messages only. +It uses the ``@on_message`` decorator to register a ``MessageHandler`` and applies two filters on it: +``filters.text`` and ``filters.private`` to make sure it will reply to private text messages only. .. code-block:: python - from pyrogram import Client, Filters + from pyrogram import Client, filters app = Client("my_account") - @app.on_message(Filters.text & Filters.private) + @app.on_message(filters.text & filters.private) def echo(client, message): - message.reply(message.text) + message.reply_text(message.text) app.run() # Automatically start() and idle() \ No newline at end of file diff --git a/docs/source/start/examples/inline_queries.rst b/docs/source/start/examples/inline_queries.rst index 158394e8..023b9c6e 100644 --- a/docs/source/start/examples/inline_queries.rst +++ b/docs/source/start/examples/inline_queries.rst @@ -8,9 +8,9 @@ It uses the @on_inline_query decorator to register an InlineQueryHandler. .. code-block:: python - from pyrogram import ( - Client, InlineQueryResultArticle, InputTextMessageContent, InlineKeyboardMarkup, InlineKeyboardButton - ) + from pyrogram import Client + from pyrogram.types import (InlineQueryResultArticle, InputTextMessageContent, + InlineKeyboardMarkup, InlineKeyboardButton) app = Client("my_bot", bot_token="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11") diff --git a/docs/source/start/examples/welcomebot.rst b/docs/source/start/examples/welcomebot.rst index a3bb3299..cdd7f022 100644 --- a/docs/source/start/examples/welcomebot.rst +++ b/docs/source/start/examples/welcomebot.rst @@ -1,14 +1,12 @@ welcomebot ========== -This is the Welcome Bot in @PyrogramChat. - -It uses the Emoji module to easily add emojis in your text messages and Filters +This example uses the ``emoji`` module to easily add emoji in your text messages and ``filters`` to make it only work for specific messages in a specific chat. .. code-block:: python - from pyrogram import Client, Emoji, Filters + from pyrogram import Client, emoji, filters TARGET = "PyrogramChat" # Target chat. Can also be a list of multiple chat ids/usernames MENTION = "[{}](tg://user?id={})" # User mention markup @@ -18,16 +16,16 @@ to make it only work for specific messages in a specific chat. # Filter in only new_chat_members updates generated in TARGET chat - @app.on_message(Filters.chat(TARGET) & Filters.new_chat_members) + @app.on_message(filters.chat(TARGET) & filters.new_chat_members) def welcome(client, message): # 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 = [u.mention for u in message.new_chat_members] # Build the welcome message by using an emoji and the list we built above - text = MESSAGE.format(Emoji.SPARKLES, ", ".join(new_members)) + text = MESSAGE.format(emoji.SPARKLES, ", ".join(new_members)) # Send the welcome message, without the web page preview - message.reply(text, disable_web_page_preview=True) + message.reply_text(text, disable_web_page_preview=True) app.run() # Automatically start() and idle() \ No newline at end of file diff --git a/docs/source/start/invoking.rst b/docs/source/start/invoking.rst index 0abea089..b8eddb9e 100644 --- a/docs/source/start/invoking.rst +++ b/docs/source/start/invoking.rst @@ -14,8 +14,7 @@ account; we are now aiming towards the core of the library. It's time to start p Basic Usage ----------- -Making API method calls with Pyrogram is very simple. Here's a basic example we are going to examine step by step and -then expand to explain what happens underneath: +Making API method calls with Pyrogram is very simple. Here's a basic example we are going to examine step by step: .. code-block:: python @@ -56,9 +55,9 @@ Basic step-by-step Context Manager --------------- -The ``with`` statement starts a context manager, which is used as a shortcut to automatically call -:meth:`~pyrogram.Client.start` and :meth:`~pyrogram.Client.stop`, which are methods required for Pyrogram to work -properly. The context manager does also gracefully stop the client, even in case of unhandled exceptions in your code. +The ``with`` statement starts a context manager used as a shortcut to automatically call :meth:`~pyrogram.Client.start` +and :meth:`~pyrogram.Client.stop`, which are methods required for Pyrogram to work properly. The context manager does +also gracefully stop the client, even in case of unhandled exceptions in your code. This is how Pyrogram looks without the context manager: @@ -111,8 +110,8 @@ Asynchronous step-by-step async with app: await app.send_message("me", "Hi!") -#. Finally, we tell Python to schedule our ``main()`` async function, which in turn will execute Pyrogram's code. Using - :meth:`~pyrogram.Client.run` this way is a friendly alternative for the much more verbose +#. Finally, we tell Python to schedule our ``main()`` async function, which in turn will execute Pyrogram's methods. + Using :meth:`~pyrogram.Client.run` this way is a friendly alternative for the much more verbose ``asyncio.get_event_loop().run_until_complete(main())``: .. code-block:: python diff --git a/docs/source/start/updates.rst b/docs/source/start/updates.rst index ef569bf9..a13d7fd7 100644 --- a/docs/source/start/updates.rst +++ b/docs/source/start/updates.rst @@ -26,8 +26,8 @@ Registering a Handler --------------------- To explain how handlers work let's examine the one which will be in charge for handling :class:`~pyrogram.types.Message` -updates coming from all around your chats. Every other handler shares the same setup logic; you should not have -troubles settings them up once you learn from this section. +updates coming from all around your chats. Every other kind of handler shares the same setup logic and you should not +have troubles settings them up once you learn from this section. Using Decorators ^^^^^^^^^^^^^^^^ @@ -98,3 +98,9 @@ The same about asynchronous handlers applies for :meth:`~pyrogram.Client.add_han async def my_function(client, message): await message.forward("me") + +.. note:: + + From now on, you'll see examples using synchronous code (i.e.: without ``async`` and ``await``, unless when actually + relevant). This is done to keep snippets concise and more readable. Once you get the idea behind a feature, you can + easily turn examples asynchronous later on. diff --git a/docs/source/topics/create-filters.rst b/docs/source/topics/create-filters.rst index 3e87a3de..191eeb05 100644 --- a/docs/source/topics/create-filters.rst +++ b/docs/source/topics/create-filters.rst @@ -2,7 +2,8 @@ Creating Filters ================ Pyrogram already provides lots of built-in :class:`~pyrogram.filters` to work with, but in case you can't find a -specific one for your needs or want to build a custom filter by yourself you can use :meth:`~pyrogram.filters.create`. +specific one for your needs or want to build a custom filter by yourself you can use +:meth:`filters.create() `. .. contents:: Contents :backlinks: none @@ -37,29 +38,45 @@ Basic Filters For this basic filter we will be using only the first parameter of :meth:`~pyrogram.filters.create`. -The code below creates a simple filter for hardcoded, static callback data. This filter will only allow callback queries -containing "pyrogram" as data, that is, the function *func* you pass returns True in case the callback query data -equals to ``"pyrogram"``. +The heart of a filter is its callback function, which accepts three arguments *(self, client, update)* and returns +either ``True``, in case you want the update to pass the filter or ``False`` otherwise. + +In this example we are matching the query data to "pyrogram", which means that the filter will only allow callback +queries containing "pyrogram" as data: .. code-block:: python from pyrogram import filters - static_data_filter = filters.create(lambda _, query: query.data == "pyrogram") + static_data_filter = filters.create(lambda _, __, query: query.data == "pyrogram") -The ``lambda`` operator in python is used to create small anonymous functions and is perfect for this example, the same -could be achieved with a normal function, but we don't really need it as it makes sense only inside the filter's scope: +The first two arguments of the callback function are unused here and because of this we named them using underscores. + +The ``lambda`` operator in python is used to create small anonymous functions and is perfect for this example. The same +can be achieved with a normal function, but we don't really need it as it makes sense only inside the filter's scope: .. code-block:: python from pyrogram import filters - def func(_, query): + def func(_, __, query): return query.data == "pyrogram" static_data_filter = filters.create(func) -The filter usage remains the same: +Asynchronous filters are also possible. Sadly, Python itself doesn't have an ``async lambda``, so we are left with +using a named function: + +.. code-block:: python + + from pyrogram import filters + + async def func(_, __, query): + return query.data == "pyrogram" + + static_data_filter = filters.create(func) + +Finally, the filter usage remains the same: .. code-block:: python @@ -70,8 +87,10 @@ The filter usage remains the same: Filters with Arguments ---------------------- -A much cooler filter would be one that accepts "pyrogram" or any other data as argument at usage time. -A dynamic filter like this will make use of named arguments for the :meth:`~pyrogram.filters.create` method. +A much cooler filter would be one that accepts "pyrogram" or any other string as argument at usage time. +A dynamic filter like this will make use of named arguments for the :meth:`~pyrogram.filters.create` method and the +first argument of the callback function, which is a reference to the filter object itself holding the extra data passed +via named arguments. This is how a dynamic custom filter looks like: @@ -81,14 +100,52 @@ This is how a dynamic custom filter looks like: def dynamic_data_filter(data): return filters.create( - lambda flt, query: flt.data == query.data, + lambda flt, _, query: flt.data == query.data, data=data # "data" kwarg is accessed with "flt.data" above ) -And its usage: +And its asynchronous variant: + +.. code-block:: python + + from pyrogram import filters + + def dynamic_data_filter(data): + async def func(flt, _, query): + return flt.data == query.data + + # "data" kwarg is accessed with "flt.data" above + return filters.create(func, data=data) + +And finally its usage: .. code-block:: python @app.on_callback_query(dynamic_data_filter("pyrogram")) def pyrogram_data(_, query): query.answer("it works!") + + +Method Calls Inside Filters +--------------------------- + +The missing piece we haven't covered yet is the second argument of a filter callback function, namely, the ``client`` +argument. This is a reference to the :obj:`~pyrogram.Client` instance that is running the filter and it is useful in +case you would like to make some API calls before deciding whether the filter should allow the update or not: + +.. code-block:: python + + def func(_, client, query): + # r = client.some_api_method() + # check response "r" and decide to return True or False + ... + +Asynchronous filters making API calls work fine as well. Just remember that you need to put ``async`` in front of +function definitions and ``await`` in front of method calls: + +.. code-block:: python + + async def func(_, client, query): + # r = await client.some_api_method() + # check response "r" and decide to return True or False + ... \ No newline at end of file diff --git a/pyrogram/client.py b/pyrogram/client.py index a293efd5..ca0440aa 100644 --- a/pyrogram/client.py +++ b/pyrogram/client.py @@ -58,7 +58,7 @@ class Client(Methods, Scaffold): session_name (``str``): Pass a string of your choice to give a name to the client session, e.g.: "*my_account*". This name will be used to save a file on disk that stores details needed to reconnect without asking again for credentials. - Alternatively, if you don't want a file to be saved on disk, pass the special name "**:memory:**" to start + Alternatively, if you don't want a file to be saved on disk, pass the special name ``":memory:"`` to start an in-memory session that will be discarded as soon as you stop the Client. In order to reconnect again using a memory storage without having to login again, you can use :meth:`~pyrogram.Client.export_session_string` before stopping the client to get a session string you can