diff --git a/docs/source/faq.rst b/docs/source/faq.rst index a0fe3331..6c3cc1ab 100644 --- a/docs/source/faq.rst +++ b/docs/source/faq.rst @@ -155,6 +155,14 @@ things: chats). - The chat id argument you passed is in form of a string; you have to convert it into an integer with ``int(chat_id)``. +UnicodeEncodeError: '' codec can't encode … +----------------------------------------------------- + +Where ```` might be *ascii*, *cp932*, *charmap* or anything else other than **utf-8**. This error usually +shows up when you try to print something and has very little to do with Pyrogram itself as it is strictly related to +your own terminal. To fix it, either find a way to change the encoding settings of your terminal to UTF-8 or switch to a +better one. + My verification code expires immediately! ----------------------------------------- @@ -179,8 +187,20 @@ Having said that, here's a list of what Telegram definitely doesn't like: - Spam, sending unsolicited messages or adding people to unwanted groups and channels. - Virtual/VoIP and cheap real numbers, because they are relatively easy to get and likely used for spam/flood. -However, you might be right, and your account was deactivated/limited without any reason. This could happen because of -mistakes by either the automatic systems or a moderator. In such cases you can kindly email Telegram at +And here's a good explanation of how, probably, the system works: + +.. raw:: html + + + +.. centered:: Join the discussion at `@PyrogramChat `_ + +However, you might be right, and your account was deactivated/limited without any good reason. This could happen because +of mistakes by either the automatic systems or a moderator. In such cases you can kindly email Telegram at recover@telegram.org, contact `@smstelegram`_ on Twitter or use `this form`_. Are there any secret easter eggs? diff --git a/docs/source/glossary.rst b/docs/source/glossary.rst index 72d308b7..bcb1193c 100644 --- a/docs/source/glossary.rst +++ b/docs/source/glossary.rst @@ -9,7 +9,11 @@ general. Some words may as well link to dedicated articles in case the topic is If you think something interesting could be added here, feel free to propose it by opening a `Feature Request`_. +Terms +----- + .. glossary:: + :sorted: API Application Programming Interface: a set of methods, protocols and tools that make it easier to develop programs diff --git a/docs/source/index.rst b/docs/source/index.rst index b99fcf3d..12ac705b 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -122,7 +122,8 @@ Meta :hidden: :caption: Topic Guides - topics/filters + topics/use-filters + topics/create-filters topics/more-on-updates topics/config-file topics/smart-plugins @@ -134,6 +135,7 @@ Meta topics/proxy topics/bots-interaction topics/mtproto-vs-botapi + topics/debugging topics/test-servers topics/advanced-usage topics/voice-calls diff --git a/docs/source/start/invoking.rst b/docs/source/start/invoking.rst index 1357cd7b..5cb6817b 100644 --- a/docs/source/start/invoking.rst +++ b/docs/source/start/invoking.rst @@ -24,40 +24,40 @@ Making API method calls with Pyrogram is very simple. Here's an example we are g app.stop() -Let's begin by importing the Client class from the Pyrogram package: +#. Let's begin by importing the Client class from the Pyrogram package: -.. code-block:: python + .. code-block:: python - from pyrogram import Client + from pyrogram import Client -Now instantiate a new Client object, "my_account" is a session name of your choice: +#. Now instantiate a new Client object, "my_account" is a session name of your choice: -.. code-block:: python + .. code-block:: python - app = Client("my_account") + app = Client("my_account") -To actually make use of any method, the client has to be started first: +#. To actually make use of any method, the client has to be started first: -.. code-block:: python + .. code-block:: python - app.start() + app.start() -Now, you can call any method you like: +#. Now, you can call any method you like: -.. code-block:: python + .. code-block:: python - print(app.get_me()) # Print information about yourself + print(app.get_me()) # Print information about yourself - # Send messages to yourself: - app.send_message("me", "Hi!") # Text message - app.send_location("me", 51.500729, -0.124583) # Location - app.send_sticker("me", "CAADBAADyg4AAvLQYAEYD4F7vcZ43AI") # Sticker + # Send messages to yourself: + app.send_message("me", "Hi!") # Text message + app.send_location("me", 51.500729, -0.124583) # Location + app.send_sticker("me", "CAADBAADyg4AAvLQYAEYD4F7vcZ43AI") # Sticker -Finally, when done, simply stop the client: +#. Finally, when done, simply stop the client: -.. code-block:: python + .. code-block:: python - app.stop() + app.stop() Context Manager --------------- diff --git a/docs/source/start/updates.rst b/docs/source/start/updates.rst index b6838ad3..9ac428b3 100644 --- a/docs/source/start/updates.rst +++ b/docs/source/start/updates.rst @@ -45,36 +45,37 @@ arrives: app.run() -Let's examine these four new pieces. First one: a callback function we defined which accepts two arguments - -*(client, message)*. This will be the function that gets executed every time a new message arrives and Pyrogram will -call that function by passing the client instance and the new message instance as argument. +#. Let's examine these four new pieces. First one: a callback function we defined which accepts two arguments - + *(client, message)*. This will be the function that gets executed every time a new message arrives and Pyrogram will + call that function by passing the client instance and the new message instance as argument. -.. code-block:: python + .. code-block:: python - def my_function(client, message): - print(message) + def my_function(client, message): + print(message) -Second one: the :class:`~pyrogram.MessageHandler`. This object tells Pyrogram the function we defined above must only -handle updates that are in form of a :class:`~pyrogram.Message`: +#. Second one: the :class:`~pyrogram.MessageHandler`. This object tells Pyrogram the function we defined above must + only handle updates that are in form of a :class:`~pyrogram.Message`: -.. code-block:: python + .. code-block:: python - my_handler = MessageHandler(my_function) + my_handler = MessageHandler(my_function) -Third: the method :meth:`~pyrogram.Client.add_handler`. This method is used to actually register the handler and let -Pyrogram know it needs to be taken into consideration when new updates arrive and the internal dispatching phase begins. +#. Third: the method :meth:`~pyrogram.Client.add_handler`. This method is used to actually register the handler and let + Pyrogram know it needs to be taken into consideration when new updates arrive and the internal dispatching phase + begins. -.. code-block:: python + .. code-block:: python - app.add_handler(my_handler) + app.add_handler(my_handler) -Last one, the :meth:`~pyrogram.Client.run` method. What this does is simply call :meth:`~pyrogram.Client.start` and a -special method :meth:`~pyrogram.Client.idle` that keeps your main scripts alive until you press ``CTRL+C``; the client -will be automatically stopped after that. +#. Last one, the :meth:`~pyrogram.Client.run` method. What this does is simply call :meth:`~pyrogram.Client.start` and + a special method :meth:`~pyrogram.Client.idle` that keeps your main scripts alive until you press ``CTRL+C``; the + client will be automatically stopped after that. -.. code-block:: python + .. code-block:: python - app.run() + app.run() Using Decorators ---------------- diff --git a/docs/source/topics/create-filters.rst b/docs/source/topics/create-filters.rst new file mode 100644 index 00000000..0252221c --- /dev/null +++ b/docs/source/topics/create-filters.rst @@ -0,0 +1,92 @@ +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 (to be used in a different kind of handler, +for example) you can use :meth:`~pyrogram.Filters.create`. + +.. note:: + + At the moment, the built-in filters are intended to be used with the :class:`~pyrogram.MessageHandler` only. + +Custom Filters +-------------- + +An example to demonstrate how custom filters work is to show how to create and use one for the +:class:`~pyrogram.CallbackQueryHandler`. Note that callback queries updates are only received by bots; create and +:doc:`authorize your bot <../start/auth>`, then send a message with an inline keyboard to yourself. This allows you to +test your filter by pressing the inline button: + +.. code-block:: python + + from pyrogram import InlineKeyboardMarkup, InlineKeyboardButton + + app.send_message( + "username", # Change this to your username or id + "Pyrogram's custom filter test", + reply_markup=InlineKeyboardMarkup( + [[InlineKeyboardButton("Press me", b"pyrogram")]] + ) + ) + +Basic Filters +------------- + +For this basic filter we will be using only the first two parameters 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 ``b"Pyrogram"``. + +.. code-block:: python + + static_data = Filters.create( + name="StaticdData", + func=lambda flt, callback_query: callback_query.data == b"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: + +.. code-block:: python + + def func(flt, callback_query): + return callback_query.data == b"Pyrogram" + + static_data = Filters.create( + name="StaticData", + func=func + ) + +The filter usage remains the same: + +.. code-block:: python + + @app.on_callback_query(static_data) + def pyrogram_data(client, callback_query): + client.answer_callback_query(callback_query.id, "it works!") + +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 the third parameter of :meth:`~pyrogram.Filters.create`. + +This is how a dynamic custom filter looks like: + +.. code-block:: python + + def dynamic_data(data): + return Filters.create( + name="DynamicData", + func=lambda flt, callback_query: flt.data == callback_query.data, + data=data # "data" kwarg is accessed with "filter.data" + ) + +And its usage: + +.. code-block:: python + + @app.on_callback_query(dynamic_data(b"Pyrogram")) + def pyrogram_data(client, callback_query): + client.answer_callback_query(callback_query.id, "it works!") \ No newline at end of file diff --git a/docs/source/topics/debugging.rst b/docs/source/topics/debugging.rst new file mode 100644 index 00000000..153c0927 --- /dev/null +++ b/docs/source/topics/debugging.rst @@ -0,0 +1,135 @@ +Debugging +========= + +When working with the API, chances are you'll stumble upon bugs, get stuck and start wondering how to continue. Nothing +to actually worry about -- that's normal -- and luckily for you, Pyrogram provides some commodities to help you in this. + +Caveman Debugging +----------------- + + *The most effective debugging tool is still careful thought, coupled with judiciously placed print statements.* + + -- Brian Kernighan, "Unix for Beginners" (1979) + +Adding ``print()`` statements in crucial parts of your code is by far the most ancient, yet efficient technique for +debugging programs, especially considering the concurrent nature of the framework itself. Pyrogram goodness in this +respect comes with the fact that any object can be nicely printed just by calling ``print(obj)``, thus giving to you +an insight of all its inner details. + +Consider the following code: + +.. code-block:: python + + dan = app.get_users("haskell") + print(dan) # User + +This will show a JSON representation of the object returned by :meth:`~pyrogram.Client.get_users`, which is a +:class:`~pyrogram.User` instance, in this case. The output on your terminal will be something similar to this: + +.. code-block:: json + + { + "_": "pyrogram.User", + "id": 23122162, + "is_self": false, + "is_contact": false, + "is_mutual_contact": false, + "is_deleted": false, + "is_bot": false, + "is_verified": false, + "is_restricted": false, + "is_support": false, + "is_scam": false, + "first_name": "Dan", + "status": { + "_": "pyrogram.UserStatus", + "user_id": 23122162, + "recently": true + }, + "username": "haskell", + "language_code": "en", + "photo": { + "_": "pyrogram.ChatPhoto", + "small_file_id": "AQADBAAD8tBgAQAEJjCxGgAEo5IBAAIC", + "big_file_id": "AQADBAAD8tBgAQAEJjCxGgAEpZIBAAEBAg" + } + } + +As you've probably guessed already, Pyrogram objects can be nested. That's how compound data are built, and nesting +keeps going until we are left with base data types only, such as ``str``, ``int``, ``bool``, etc. + +Accessing Attributes +-------------------- + +Even though you see a JSON output, it doesn't mean we are dealing with dictionaries; in fact, all Pyrogram types are +full-fledged Python objects and the correct way to access any attribute of them is by using the dot notation ``.``: + +.. code-block:: python + + dan_photo = dan.photo + print(dan_photo) # ChatPhoto + +.. code-block:: json + + { + "_": "pyrogram.ChatPhoto", + "small_file_id": "AQADBAAD8tBgAQAEJjCxGgAEo5IBAAIC", + "big_file_id": "AQADBAAD8tBgAQAEJjCxGgAEpZIBAAEBAg" + } + +However, the bracket notation ``[]`` is also supported, but its usage is discouraged: + +.. warning:: + + Bracket notation in Python is not commonly used for getting/setting object attributes. While it works for Pyrogram + objects, it might not work for anything else and you should not rely on this. + +.. code-block:: python + + dan_photo_big = dan["photo"]["big_file_id"] + print(dan_photo_big) # str + +.. code-block:: text + + AQADBAAD8tBgAQAEJjCxGgAEpZIBAAEBAg + +Checking an Object's Type +------------------------- + +Another thing worth talking about is how to tell and check for an object's type. + +As you noticed already, when printing an object you'll see the special attribute ``"_"``. This is just a visual thing +useful to show humans the object type, but doesn't really exist anywhere; any attempt in accessing it will lead to an +error. The correct way to get the object type is by using the built-in function ``type()``: + +.. code-block:: python + + dan_status = dan.status + print(type(dan_status)) + +.. code-block:: text + + + +And to check if an object is an instance of a given class, you use the built-in function ``isinstance()``: + +.. code-block:: python + :name: this-py + + from pyrogram import UserStatus + + dan_status = dan.status + print(isinstance(dan_status, UserStatus)) + +.. code-block:: text + + True + +.. raw:: html + + \ No newline at end of file diff --git a/docs/source/topics/filters.rst b/docs/source/topics/use-filters.rst similarity index 50% rename from docs/source/topics/filters.rst rename to docs/source/topics/use-filters.rst index 7ff02ffc..c23a98df 100644 --- a/docs/source/topics/filters.rst +++ b/docs/source/topics/use-filters.rst @@ -105,93 +105,3 @@ More handlers using different filters can also live together. @app.on_message(Filters.chat("PyrogramChat")) def from_pyrogramchat(client, message): print("New message in @PyrogramChat") - -Custom 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 (to be used in a different kind of handler, -for example) you can use :meth:`~pyrogram.Filters.create`. - -.. note:: - - At the moment, the built-in filters are intended to be used with the :class:`~pyrogram.MessageHandler` only. - -An example to demonstrate how custom filters work is to show how to create and use one for the -:class:`~pyrogram.CallbackQueryHandler`. Note that callback queries updates are only received by bots; create and -:doc:`authorize your bot <../start/auth>`, then send a message with an inline keyboard to yourself. This allows you to -test your filter by pressing the inline button: - -.. code-block:: python - - from pyrogram import InlineKeyboardMarkup, InlineKeyboardButton - - app.send_message( - "username", # Change this to your username or id - "Pyrogram's custom filter test", - reply_markup=InlineKeyboardMarkup( - [[InlineKeyboardButton("Press me", b"pyrogram")]] - ) - ) - -Basic Filters -^^^^^^^^^^^^^ - -For this basic filter we will be using only the first two parameters 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 ``b"Pyrogram"``. - -.. code-block:: python - - static_data = Filters.create( - name="StaticdData", - func=lambda flt, callback_query: callback_query.data == b"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: - -.. code-block:: python - - def func(flt, callback_query): - return callback_query.data == b"Pyrogram" - - static_data = Filters.create( - name="StaticData", - func=func - ) - -The filter usage remains the same: - -.. code-block:: python - - @app.on_callback_query(static_data) - def pyrogram_data(client, callback_query): - client.answer_callback_query(callback_query.id, "it works!") - -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 the third parameter of :meth:`~pyrogram.Filters.create`. - -This is how a dynamic custom filter looks like: - -.. code-block:: python - - def dynamic_data(data): - return Filters.create( - name="DynamicData", - func=lambda flt, callback_query: flt.data == callback_query.data, - data=data # "data" kwarg is accessed with "filter.data" - ) - -And its usage: - -.. code-block:: python - - @app.on_callback_query(dynamic_data(b"Pyrogram")) - def pyrogram_data(client, callback_query): - client.answer_callback_query(callback_query.id, "it works!") \ No newline at end of file