Update docs

This commit is contained in:
Dan 2022-10-06 12:03:05 +02:00 committed by GitHub
parent 0e68bf35b7
commit 8077eb4130
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
93 changed files with 85 additions and 3865 deletions

2
.gitignore vendored
View File

@ -1,8 +1,10 @@
# Development
docs
*.session
config.ini
main.py
unknown_errors.txt
.DS_Store
# Pyrogram generated code
pyrogram/errors/exceptions/

View File

@ -1,53 +1,33 @@
VENV := venv
PYTHON := $(VENV)/bin/python
HOST = $(shell ifconfig | grep "inet " | tail -1 | cut -d\ -f2)
RM := rm -rf
.PHONY: venv build docs
.PHONY: venv clean-build clean-api clean api build
venv:
$(RM) $(VENV)
python3 -m venv $(VENV)
$(PYTHON) -m pip install -U pip wheel setuptools
$(PYTHON) -m pip install -U -r requirements.txt -r dev-requirements.txt -r docs/requirements.txt
$(PYTHON) -m pip install -U -r requirements.txt -r dev-requirements.txt
@echo "Created venv with $$($(PYTHON) --version)"
clean-build:
$(RM) *.egg-info build dist
clean-docs:
$(RM) docs/build
$(RM) docs/source/api/bound-methods docs/source/api/methods docs/source/api/types docs/source/telegram
clean-api:
$(RM) pyrogram/errors/exceptions pyrogram/raw/all.py pyrogram/raw/base pyrogram/raw/functions pyrogram/raw/types
clean:
make clean-build
make clean-docs
make clean-api
api:
cd compiler/api && ../../$(PYTHON) compiler.py
cd compiler/errors && ../../$(PYTHON) compiler.py
docs-live:
make clean-docs
cd compiler/docs && ../../$(PYTHON) compiler.py
$(RM) docs/source/telegram
$(VENV)/bin/sphinx-autobuild \
--host $(shell ifconfig | grep "inet " | grep -v 127.0.0.1 | cut -d\ -f2) \
--watch pyrogram --watch docs/resources \
-b html "docs/source" "docs/build/html" -j auto
docs:
make clean-docs
cd compiler/docs && ../../$(PYTHON) compiler.py
$(VENV)/bin/sphinx-build \
-b html "docs/source" "docs/build/html" -j auto
build:
make clean-build
make clean-api
make clean
$(PYTHON) setup.py sdist
$(PYTHON) setup.py bdist_wheel

View File

@ -16,6 +16,7 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import json
import os
import re
import shutil
@ -59,6 +60,16 @@ namespaces_to_types = {}
namespaces_to_constructors = {}
namespaces_to_functions = {}
try:
with open("docs.json") as f:
docs = json.load(f)
except FileNotFoundError:
docs = {
"type": {},
"constructor": {},
"method": {}
}
class Combinator(NamedTuple):
section: str
@ -149,7 +160,7 @@ def remove_whitespaces(source: str) -> str:
return "\n".join(lines)
def get_docstring_arg_type(t: str, is_list: bool = False, is_pyrogram_type: bool = False):
def get_docstring_arg_type(t: str):
if t in CORE_TYPES:
if t == "long":
return "``int`` ``64-bit``"
@ -167,9 +178,9 @@ def get_docstring_arg_type(t: str, is_list: bool = False, is_pyrogram_type: bool
elif t == "TLObject" or t == "X":
return "Any object from :obj:`~pyrogram.raw.types`"
elif t == "!X":
return "Any method from :obj:`~pyrogram.raw.functions`"
return "Any function from :obj:`~pyrogram.raw.functions`"
elif t.lower().startswith("vector"):
return "List of " + get_docstring_arg_type(t.split("<", 1)[1][:-1], True)
return "List of " + get_docstring_arg_type(t.split("<", 1)[1][:-1])
else:
return f":obj:`{t} <pyrogram.raw.base.{t}>`"
@ -183,10 +194,7 @@ def get_references(t: str, kind: str):
raise ValueError("Invalid kind")
if t:
return "\n ".join(
f"- :obj:`{i} <pyrogram.raw.functions.{i}>`"
for i in t
), len(t)
return "\n ".join(t), len(t)
return None, 0
@ -315,17 +323,33 @@ def start(format: bool = False):
constructors = sorted(types_to_constructors[qualtype])
constr_count = len(constructors)
items = "\n ".join([f"- :obj:`{c} <pyrogram.raw.types.{c}>`" for c in constructors])
items = "\n ".join([f"{c}" for c in constructors])
docstring = f"This base type has {constr_count} constructor{'s' if constr_count > 1 else ''} available.\n\n"
docstring += f" Constructors:\n .. hlist::\n :columns: 2\n\n {items}"
type_docs = docs["type"].get(qualtype, None)
if type_docs:
type_docs = type_docs["desc"]
else:
type_docs = "Telegram API base type."
docstring = type_docs
docstring += f"\n\n Constructors:\n" \
f" This base type has {constr_count} constructor{'s' if constr_count > 1 else ''} available.\n\n" \
f" .. currentmodule:: pyrogram.raw.types\n\n" \
f" .. autosummary::\n" \
f" :nosignatures:\n\n" \
f" {items}"
references, ref_count = get_references(qualtype, "types")
if references:
docstring += f"\n\n See Also:\n This object can be returned by " \
f"{ref_count} method{'s' if ref_count > 1 else ''}:" \
f"\n\n .. hlist::\n :columns: 2\n\n " + references
docstring += f"\n\n Functions:\n This object can be returned by " \
f"{ref_count} function{'s' if ref_count > 1 else ''}.\n\n" \
f" .. currentmodule:: pyrogram.raw.functions\n\n" \
f" .. autosummary::\n" \
f" :nosignatures:\n\n" \
f" " + references
with open(dir_path / f"{snake(module)}.py", "w") as f:
f.write(
@ -359,41 +383,67 @@ def start(format: bool = False):
docstring = ""
docstring_args = []
if c.section == "functions":
combinator_docs = docs["method"]
else:
combinator_docs = docs["constructor"]
for i, arg in enumerate(sorted_args):
arg_name, arg_type = arg
is_optional = FLAGS_RE.match(arg_type)
flag_number = is_optional.group(1) if is_optional else -1
arg_type = arg_type.split("?")[-1]
arg_docs = combinator_docs.get(c.qualname, None)
if arg_docs:
arg_docs = arg_docs["params"].get(arg_name, "N/A")
else:
arg_docs = "N/A"
docstring_args.append(
"{}{}: {}".format(
"{} ({}{}):\n {}\n".format(
arg_name,
" (optional)".format(flag_number) if is_optional else "",
get_docstring_arg_type(arg_type, is_pyrogram_type=c.namespace == "pyrogram")
get_docstring_arg_type(arg_type),
", *optional*".format(flag_number) if is_optional else "",
arg_docs
)
)
if c.section == "types":
docstring += f"This object is a constructor of the base type :obj:`~pyrogram.raw.base.{c.qualtype}`.\n\n"
else:
docstring += f"Telegram API method.\n\n"
constructor_docs = docs["constructor"].get(c.qualname, None)
docstring += f" Details:\n - Layer: ``{layer}``\n - ID: ``{c.id[2:].upper()}``\n\n"
if docstring_args:
docstring += " Parameters:\n " + "\n ".join(docstring_args)
if constructor_docs:
constructor_docs = constructor_docs["desc"]
else:
docstring += " **No parameters required.**"
constructor_docs = "Telegram API type."
docstring += constructor_docs + "\n"
docstring += f"\n Constructor of :obj:`~pyrogram.raw.base.{c.qualtype}`."
else:
function_docs = docs["method"].get(c.qualname, None)
if function_docs:
docstring += function_docs["desc"] + "\n"
else:
docstring += f"Telegram API function."
docstring += f"\n\n Details:\n - Layer: ``{layer}``\n - ID: ``{c.id[2:].upper()}``\n\n"
docstring += f" Parameters:\n " + \
(f"\n ".join(docstring_args) if docstring_args else "No parameters required.\n")
if c.section == "functions":
docstring += "\n\n Returns:\n " + get_docstring_arg_type(c.qualtype)
docstring += "\n Returns:\n " + get_docstring_arg_type(c.qualtype)
else:
references, count = get_references(c.qualname, "constructors")
if references:
docstring += f"\n\n See Also:\n This object can be returned by " \
f"{count} method{'s' if count > 1 else ''}:" \
f"\n\n .. hlist::\n :columns: 2\n\n " + references
docstring += f"\n Functions:\n This object can be returned by " \
f"{count} function{'s' if count > 1 else ''}.\n\n" \
f" .. currentmodule:: pyrogram.raw.functions\n\n" \
f" .. autosummary::\n" \
f" :nosignatures:\n\n" \
f" " + references
write_types = read_types = "" if c.has_flags else "# No flags\n "

View File

@ -19,10 +19,6 @@ some of the required arguments.
app.run()
.. contents:: Contents
:backlinks: none
:local:
-----
.. currentmodule:: pyrogram.types

View File

@ -14,10 +14,6 @@ the main package directly.
with app:
app.send_message("me", "hi")
.. contents:: Contents
:backlinks: none
:local:
-----
.. currentmodule:: pyrogram.Client

View File

@ -4,4 +4,6 @@
.. module:: {module}
.. toctree::
:titlesonly:
{entities}

View File

@ -17,10 +17,6 @@ are only returned by other methods. You also don't need to import them, unless y
To tell whether a field is set or not, do a simple boolean check: ``if message.photo: ...``.
.. contents:: Contents
:backlinks: none
:local:
-----
.. currentmodule:: pyrogram.types

View File

@ -1,4 +0,0 @@
sphinx
sphinx_rtd_theme==1.0.0
sphinx_copybutton
sphinx-autobuild

View File

@ -1,24 +0,0 @@
Pyrogram Client
===============
You have entered the API Reference section where you can find detailed information about Pyrogram's API. The main Client
class, all available methods and types, filters, handlers, decorators and bound-methods detailed descriptions can be
found starting from this page.
This page is about the Client class, which exposes high-level methods for an easy access to the API.
.. code-block:: python
from pyrogram import Client
app = Client("my_account")
with app:
app.send_message("me", "Hi!")
-----
Details
-------
.. autoclass:: pyrogram.Client()

View File

@ -1,68 +0,0 @@
Decorators
==========
Decorators are able to register callback functions for handling updates in a much easier and cleaner way compared to
:doc:`Handlers <handlers>`; they do so by instantiating the correct handler and calling
:meth:`~pyrogram.Client.add_handler` automatically. All you need to do is adding the decorators on top of your
functions.
.. code-block:: python
from pyrogram import Client
app = Client("my_account")
@app.on_message()
def log(client, message):
print(message)
app.run()
.. contents:: Contents
:backlinks: none
:depth: 1
:local:
-----
.. currentmodule:: pyrogram
Index
-----
.. hlist::
:columns: 3
- :meth:`~Client.on_message`
- :meth:`~Client.on_edited_message`
- :meth:`~Client.on_callback_query`
- :meth:`~Client.on_inline_query`
- :meth:`~Client.on_chosen_inline_result`
- :meth:`~Client.on_chat_member_updated`
- :meth:`~Client.on_chat_join_request`
- :meth:`~Client.on_deleted_messages`
- :meth:`~Client.on_user_status`
- :meth:`~Client.on_poll`
- :meth:`~Client.on_disconnect`
- :meth:`~Client.on_raw_update`
-----
Details
-------
.. Decorators
.. autodecorator:: pyrogram.Client.on_message()
.. autodecorator:: pyrogram.Client.on_edited_message()
.. autodecorator:: pyrogram.Client.on_callback_query()
.. autodecorator:: pyrogram.Client.on_inline_query()
.. autodecorator:: pyrogram.Client.on_chosen_inline_result()
.. autodecorator:: pyrogram.Client.on_chat_member_updated()
.. autodecorator:: pyrogram.Client.on_chat_join_request()
.. autodecorator:: pyrogram.Client.on_deleted_messages()
.. autodecorator:: pyrogram.Client.on_user_status()
.. autodecorator:: pyrogram.Client.on_poll()
.. autodecorator:: pyrogram.Client.on_disconnect()
.. autodecorator:: pyrogram.Client.on_raw_update()

View File

@ -1,8 +0,0 @@
ChatAction
==========
.. autoclass:: pyrogram.enums.ChatAction()
:members:
.. raw:: html
:file: ./cleanup.html

View File

@ -1,8 +0,0 @@
ChatEventAction
===============
.. autoclass:: pyrogram.enums.ChatEventAction()
:members:
.. raw:: html
:file: ./cleanup.html

View File

@ -1,8 +0,0 @@
ChatMemberStatus
================
.. autoclass:: pyrogram.enums.ChatMemberStatus()
:members:
.. raw:: html
:file: ./cleanup.html

View File

@ -1,8 +0,0 @@
ChatMembersFilter
=================
.. autoclass:: pyrogram.enums.ChatMembersFilter()
:members:
.. raw:: html
:file: ./cleanup.html

View File

@ -1,8 +0,0 @@
ChatType
========
.. autoclass:: pyrogram.enums.ChatType()
:members:
.. raw:: html
:file: ./cleanup.html

View File

@ -1,8 +0,0 @@
MessageEntityType
=================
.. autoclass:: pyrogram.enums.MessageEntityType()
:members:
.. raw:: html
:file: ./cleanup.html

View File

@ -1,8 +0,0 @@
MessageMediaType
================
.. autoclass:: pyrogram.enums.MessageMediaType()
:members:
.. raw:: html
:file: ./cleanup.html

View File

@ -1,8 +0,0 @@
MessageServiceType
==================
.. autoclass:: pyrogram.enums.MessageServiceType()
:members:
.. raw:: html
:file: ./cleanup.html

View File

@ -1,8 +0,0 @@
MessagesFilter
==============
.. autoclass:: pyrogram.enums.MessagesFilter()
:members:
.. raw:: html
:file: ./cleanup.html

View File

@ -1,8 +0,0 @@
NextCodeType
============
.. autoclass:: pyrogram.enums.NextCodeType()
:members:
.. raw:: html
:file: ./cleanup.html

View File

@ -1,8 +0,0 @@
ParseMode
=========
.. autoclass:: pyrogram.enums.ParseMode()
:members:
.. raw:: html
:file: ./cleanup.html

View File

@ -1,8 +0,0 @@
PollType
========
.. autoclass:: pyrogram.enums.PollType()
:members:
.. raw:: html
:file: ./cleanup.html

View File

@ -1,8 +0,0 @@
SentCodeType
============
.. autoclass:: pyrogram.enums.SentCodeType()
:members:
.. raw:: html
:file: ./cleanup.html

View File

@ -1,8 +0,0 @@
UserStatus
==========
.. autoclass:: pyrogram.enums.UserStatus()
:members:
.. raw:: html
:file: ./cleanup.html

View File

@ -1,9 +0,0 @@
<script>
document
.querySelectorAll("em.property")
.forEach((elem, i) => i !== 0 ? elem.remove() : true)
document
.querySelectorAll("a.headerlink")
.forEach((elem, i) => [0, 1].includes(i) ? true : elem.remove())
</script>

View File

@ -1,47 +0,0 @@
Enumerations
============
This page is about Pyrogram enumerations.
Enumerations are types that hold a group of related values to be used whenever a constant value is required.
They will help you deal with those values in a type-safe way and also enable code completion so that you can be sure
to apply only a valid value among the expected ones.
-----
.. currentmodule:: pyrogram.enums
.. autosummary::
:nosignatures:
ChatAction
ChatEventAction
ChatMemberStatus
ChatMembersFilter
ChatType
MessageEntityType
MessageMediaType
MessageServiceType
MessagesFilter
ParseMode
PollType
SentCodeType
NextCodeType
UserStatus
.. toctree::
:hidden:
ChatAction
ChatEventAction
ChatMemberStatus
ChatMembersFilter
ChatType
MessageEntityType
MessageMediaType
MessageServiceType
MessagesFilter
ParseMode
PollType
SentCodeType
NextCodeType
UserStatus

View File

@ -1,7 +0,0 @@
400 - BadRequest
----------------
.. csv-table::
:file: ../../../../compiler/errors/source/400_BAD_REQUEST.tsv
:delim: tab
:header-rows: 1

View File

@ -1,7 +0,0 @@
420 - Flood
-----------
.. csv-table::
:file: ../../../../compiler/errors/source/420_FLOOD.tsv
:delim: tab
:header-rows: 1

View File

@ -1,7 +0,0 @@
403 - Forbidden
---------------
.. csv-table::
:file: ../../../../compiler/errors/source/403_FORBIDDEN.tsv
:delim: tab
:header-rows: 1

View File

@ -1,37 +0,0 @@
RPC Errors
==========
All Pyrogram API errors live inside the ``errors`` sub-package: ``pyrogram.errors``.
The errors ids listed here are shown as *UPPER_SNAKE_CASE*, but the actual exception names to import from Pyrogram
follow the usual *PascalCase* convention.
.. code-block:: python
from pyrogram.errors import FloodWait
try:
...
except FloodWait as e:
...
.. hlist::
:columns: 1
- :doc:`see-other`
- :doc:`bad-request`
- :doc:`unauthorized`
- :doc:`forbidden`
- :doc:`not-acceptable`
- :doc:`flood`
- :doc:`internal-server-error`
.. toctree::
:hidden:
see-other
bad-request
unauthorized
forbidden
not-acceptable
flood
internal-server-error

View File

@ -1,7 +0,0 @@
500 - InternalServerError
-------------------------
.. csv-table::
:file: ../../../../compiler/errors/source/500_INTERNAL_SERVER_ERROR.tsv
:delim: tab
:header-rows: 1

View File

@ -1,7 +0,0 @@
406 - NotAcceptable
-------------------
.. csv-table::
:file: ../../../../compiler/errors/source/406_NOT_ACCEPTABLE.tsv
:delim: tab
:header-rows: 1

View File

@ -1,7 +0,0 @@
303 - SeeOther
--------------
.. csv-table::
:file: ../../../../compiler/errors/source/303_SEE_OTHER.tsv
:delim: tab
:header-rows: 1

View File

@ -1,7 +0,0 @@
401 - Unauthorized
------------------
.. csv-table::
:file: ../../../../compiler/errors/source/401_UNAUTHORIZED.tsv
:delim: tab
:header-rows: 1

View File

@ -1,11 +0,0 @@
Update Filters
==============
Filters are objects that can be used to filter the content of incoming updates.
:doc:`Read more about how filters work <../topics/use-filters>`.
Details
-------
.. automodule:: pyrogram.filters
:members:

View File

@ -1,66 +0,0 @@
Update Handlers
===============
Handlers are used to instruct Pyrogram about which kind of updates you'd like to handle with your callback functions.
For a much more convenient way of registering callback functions have a look at :doc:`Decorators <decorators>` instead.
.. code-block:: python
from pyrogram import Client
from pyrogram.handlers import MessageHandler
app = Client("my_account")
def dump(client, message):
print(message)
app.add_handler(MessageHandler(dump))
app.run()
.. contents:: Contents
:backlinks: none
:depth: 1
:local:
-----
.. currentmodule:: pyrogram.handlers
Index
-----
.. hlist::
:columns: 3
- :class:`MessageHandler`
- :class:`EditedMessageHandler`
- :class:`DeletedMessagesHandler`
- :class:`CallbackQueryHandler`
- :class:`InlineQueryHandler`
- :class:`ChosenInlineResultHandler`
- :class:`ChatMemberUpdatedHandler`
- :class:`UserStatusHandler`
- :class:`PollHandler`
- :class:`DisconnectHandler`
- :class:`RawUpdateHandler`
-----
Details
-------
.. Handlers
.. autoclass:: MessageHandler()
.. autoclass:: EditedMessageHandler()
.. autoclass:: DeletedMessagesHandler()
.. autoclass:: CallbackQueryHandler()
.. autoclass:: InlineQueryHandler()
.. autoclass:: ChosenInlineResultHandler()
.. autoclass:: ChatMemberUpdatedHandler()
.. autoclass:: UserStatusHandler()
.. autoclass:: PollHandler()
.. autoclass:: DisconnectHandler()
.. autoclass:: RawUpdateHandler()

View File

@ -1,91 +0,0 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-present Dan <https://github.com/delivrance>
#
# 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 <http://www.gnu.org/licenses/>.
import os
import sys
sys.path.insert(0, os.path.abspath("../.."))
from pyrogram import __version__
from pygments.styles.friendly import FriendlyStyle
FriendlyStyle.background_color = "#f3f2f1"
project = "Pyrogram"
copyright = f"2017-present, Dan"
author = "Dan"
version = ".".join(__version__.split(".")[:-1])
extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.napoleon",
"sphinx.ext.autosummary",
"sphinx.ext.intersphinx",
"sphinx_copybutton"
]
intersphinx_mapping = {
"python": ("https://docs.python.org/3", None)
}
master_doc = "index"
source_suffix = ".rst"
autodoc_member_order = "bysource"
templates_path = ["../resources/templates"]
html_copy_source = False
napoleon_use_rtype = False
napoleon_use_param = False
pygments_style = "friendly"
copybutton_prompt_text = "$ "
suppress_warnings = ["image.not_readable"]
html_title = "Pyrogram Documentation"
html_theme = "sphinx_rtd_theme"
html_static_path = ["../resources/static"]
html_show_sourcelink = True
html_show_copyright = False
html_theme_options = {
"canonical_url": "https://docs.pyrogram.org/",
"collapse_navigation": True,
"sticky_navigation": False,
"logo_only": True,
"display_version": False,
"style_external_links": True
}
html_logo = "../resources/static/img/pyrogram.png"
html_favicon = "../resources/static/img/favicon.ico"
latex_engine = "xelatex"
latex_logo = "../resources/static/img/pyrogram.png"
latex_elements = {
"pointsize": "12pt",
"fontpkg": r"""
\setmainfont{Open Sans}
\setsansfont{Bitter}
\setmonofont{Ubuntu Mono}
"""
}

View File

@ -1,11 +0,0 @@
Client started, but nothing happens
===================================
A possible cause might be network issues, either yours or Telegram's. To check this, add the following code at
the top of your script and run it again. You should see some error mentioning a socket timeout or an unreachable
network:
.. code-block:: python
import logging
logging.basicConfig(level=logging.INFO)

View File

@ -1,12 +0,0 @@
Code hangs when calling stop, restart, add/remove_handler
=========================================================
You tried to ``.stop()``, ``.restart()``, ``.add_handler()`` or ``.remove_handler()`` inside a running handler, but
that can't be done because the way Pyrogram deals with handlers would make it hang.
When calling one of the methods above inside an event handler, Pyrogram needs to wait for all running handlers to finish
in order to continue. Since your handler is blocking the execution by waiting for the called method to finish
and since Pyrogram needs to wait for your handler to finish, you are left with a deadlock.
The solution to this problem is to pass ``block=False`` to such methods so that they return immediately and the actual
code called asynchronously.

View File

@ -1,23 +0,0 @@
How to avoid Flood Waits?
=========================
Slow things down and make less requests. Moreover, exact limits are unknown and can change anytime based on normal
usages.
When a flood wait happens the server will tell you how much time to wait before continuing.
The following shows how to catch the exception in your code and wait the required seconds.
.. code-block:: python
import asyncio
from pyrogram.errors import FloodWait
...
try:
... # Your code
except FloodWait as e:
await asyncio.sleep(e.value) # Wait "value" seconds before continuing
...
More info about error handling can be found :doc:`here <../start/errors>`.

View File

@ -1,9 +0,0 @@
How to use webhooks?
====================
There is no webhook in Pyrogram, simply because there is no HTTP involved. However, a similar technique is
being used to make receiving updates efficient.
Pyrogram uses persistent connections via TCP sockets to interact with the server and instead of actively asking for
updates every time (polling), Pyrogram will sit down and wait for the server to send updates by itself the very moment
they are available (server push).

View File

@ -1,45 +0,0 @@
Frequently Asked Questions
==========================
This FAQ page provides answers to common questions about Pyrogram and, to some extent, Telegram in general.
**Contents**
- :doc:`why-is-the-api-key-needed-for-bots`
- :doc:`how-to-use-webhooks`
- :doc:`using-the-same-file-id-across-different-accounts`
- :doc:`using-multiple-clients-at-once-on-the-same-account`
- :doc:`client-started-but-nothing-happens`
- :doc:`what-are-the-ip-addresses-of-telegram-data-centers`
- :doc:`migrating-the-account-to-another-data-center`
- :doc:`why-is-the-client-reacting-slowly-in-supergroups-channels`
- :doc:`peer-id-invalid-error`
- :doc:`code-hangs-when-calling-stop-restart-add-remove-handler`
- :doc:`unicodeencodeerror-codec-cant-encode`
- :doc:`uploading-with-urls-gives-error-webpage-curl-failed`
- :doc:`sqlite3-operationalerror-database-is-locked`
- :doc:`sqlite3-interfaceerror-error-binding-parameter`
- :doc:`socket-send-oserror-timeouterror-connection-lost-reset`
- :doc:`how-to-avoid-flood-waits`
- :doc:`the-account-has-been-limited-deactivated`
.. toctree::
:hidden:
why-is-the-api-key-needed-for-bots
how-to-use-webhooks
using-the-same-file-id-across-different-accounts
using-multiple-clients-at-once-on-the-same-account
client-started-but-nothing-happens
what-are-the-ip-addresses-of-telegram-data-centers
migrating-the-account-to-another-data-center
why-is-the-client-reacting-slowly-in-supergroups-channels
peer-id-invalid-error
code-hangs-when-calling-stop-restart-add-remove-handler
unicodeencodeerror-codec-cant-encode
uploading-with-urls-gives-error-webpage-curl-failed
sqlite3-operationalerror-database-is-locked
sqlite3-interfaceerror-error-binding-parameter
socket-send-oserror-timeouterror-connection-lost-reset
how-to-avoid-flood-waits
the-account-has-been-limited-deactivated

View File

@ -1,10 +0,0 @@
Migrating the account to another data center
============================================
This question is asked by people who find their account always being connected to one DC (data center), but are
connecting from a place far away, thus resulting in slower interactions when using the API because of the greater
physical distance between the user and the associated DC.
When registering an account for the first time, is up to Telegram to decide which DC the new user is going to be
created in. It's also up to the server to decide whether to automatically migrate a user in case of prolonged usages
from a distant location.

View File

@ -1,14 +0,0 @@
PEER_ID_INVALID error
=====================
This error could mean several things:
- The chat id you tried to use is simply wrong, check it again.
- The chat id refers to a group or channel you are not a member of.
- The chat id argument you passed is in form of a string; you have to convert it into an integer with ``int(chat_id)``.
- The chat id refers to a user or chat your current session hasn't met yet.
About the last point: in order for you to meet a user and thus communicate with them, you should ask yourself how to
contact people using official apps. The answer is the same for Pyrogram too and involves normal usages such as searching
for usernames, meeting them in a common group, having their phone contacts saved, getting a message mentioning them
or obtaining the dialogs list.

View File

@ -1,12 +0,0 @@
socket.send(), OSError(), TimeoutError(), Connection lost/reset
===============================================================
If you get any of these errors chances are you ended up with a slow or inconsistent network connection.
Another reason could be because you are blocking the event loop for too long.
You can consider the following:
- Use Pyrogram asynchronously in its intended way.
- Use shorter non-asynchronous processing loops.
- Use ``asyncio.sleep()`` instead of ``time.sleep()``.
- Use a stable network connection.

View File

@ -1,13 +0,0 @@
sqlite3.InterfaceError: Error binding parameter
===============================================
This error occurs when you pass a chat id value of the wrong type when trying to call a method. Most likely, you
accidentally passed the whole user or chat object instead of the id or username.
.. code-block:: python
# Wrong. You passed the whole Chat instance
app.send_message(chat, "text")
# Correct
app.send_message(chat.id, "text")

View File

@ -1,17 +0,0 @@
sqlite3.OperationalError: database is locked
============================================
This error occurs when more than one process is using the same session file, that is, when you run two or more clients
at the same time using the same session name or in case another program has accessed the file.
For example, it could occur when a background script is still running and you forgot about it. In this case, you either
restart your system or find and kill the process that is locking the database. On Unix based systems, you can try the
following:
#. ``cd`` into your session file directory.
#. ``fuser my_account.session`` to find the process id.
#. ``kill 1234`` to gracefully stop the process.
#. If the last command doesn't help, use ``kill -9 1234`` instead.
If you want to run multiple clients on the same account, you must authorize your account (either user or bot)
from the beginning every time, and use different session names for each parallel client you are going to use.

View File

@ -1,16 +0,0 @@
The account has been limited/deactivated
========================================
Pyrogram is a framework that interfaces with Telegram; it is at your commands, meaning it only does what you tell it to
do, the rest is up to you and Telegram (see `Telegram's ToS`_).
If you found your account being limited/deactivated, it could be due spam/flood/abuse of the API or the usage of certain
virtual/VoIP numbers.
If you think your account was limited/deactivated by mistake, you can write to recover@telegram.org, contact
`@SpamBot`_ or use `this form`_.
.. _@SpamBot: https://t.me/spambot
.. _this form: https://telegram.org/support
.. _Telegram's ToS: https://telegram.org/tos

View File

@ -1,7 +0,0 @@
UnicodeEncodeError: '...' codec can't encode ...
================================================
Where ``<encoding>`` 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
another terminal altogether.

View File

@ -1,7 +0,0 @@
Uploading with URLs gives error WEBPAGE_CURL_FAILED
===================================================
When uploading media files using an URL, the server automatically tries to download the media and uploads it to the
Telegram cloud. This error usually happens in case the provided URL is not publicly accessible by Telegram itself or the
media file is too large. In such cases, your only option is to download the media yourself and upload it from your
local machine.

View File

@ -1,7 +0,0 @@
Using multiple clients at once on the same account
==================================================
Both user and bot accounts are able to run multiple sessions in parallel. However, you must not use the same session
in more than one client at the same time. The correct way to run multiple clients on the same account is by authorizing
your account (either user or bot) from the beginning each time, and use one separate session for each parallel client.

View File

@ -1,6 +0,0 @@
Using the same file_id across different accounts
================================================
Telegram file_id strings are bound to the account which generated them. An attempt in using a foreign file id will
result in errors such as ``[400 MEDIA_EMPTY]``. The only exception are stickers' file ids; you can use them across
different accounts without any problem.

View File

@ -1,30 +0,0 @@
What are the IP addresses of Telegram Data Centers?
===================================================
Telegram is currently composed by a decentralized, multi-DC infrastructure (currently 5 DCs, each of which can
work independently) spread across different locations worldwide. However, some of the less busy DCs have been lately
dismissed and their IP addresses are now kept as aliases to the nearest one.
.. csv-table:: Production Environment
:header: ID, Location, IPv4, IPv6
:widths: auto
:align: center
DC1, "MIA, Miami FL, USA", ``149.154.175.53``, ``2001:b28:f23d:f001::a``
DC2, "AMS, Amsterdam, NL", ``149.154.167.51``, ``2001:67c:4e8:f002::a``
DC3*, "MIA, Miami FL, USA", ``149.154.175.100``, ``2001:b28:f23d:f003::a``
DC4, "AMS, Amsterdam, NL", ``149.154.167.91``, ``2001:67c:4e8:f004::a``
DC5, "SIN, Singapore, SG", ``91.108.56.130``, ``2001:b28:f23f:f005::a``
.. csv-table:: Test Environment
:header: ID, Location, IPv4, IPv6
:widths: auto
:align: center
DC1, "MIA, Miami FL, USA", ``149.154.175.10``, ``2001:b28:f23d:f001::e``
DC2, "AMS, Amsterdam, NL", ``149.154.167.40``, ``2001:67c:4e8:f002::e``
DC3*, "MIA, Miami FL, USA", ``149.154.175.117``, ``2001:b28:f23d:f003::e``
.. centered:: More info about the Test Environment can be found :doc:`here <../topics/test-servers>`.
***** Alias DC

View File

@ -1,12 +0,0 @@
Why is the API key needed for bots?
===================================
Requests against the official bot API endpoints are made via JSON/HTTP and are handled by an intermediate server
application that implements the MTProto protocol and uses its own API key to communicate with the MTProto servers.
.. figure:: //_static/img/mtproto-vs-bot-api.png
:align: center
Using MTProto is the only way to communicate with the actual Telegram servers, and the main API requires developers to
identify applications by means of a unique key; the bot token identifies a bot as a user and replaces the user's phone
number only.

View File

@ -1,18 +0,0 @@
Why is the client reacting slowly in supergroups/channels?
==========================================================
Because of how Telegram works internally, every message you receive and send must pass through the creator's DC, and in
the worst case where you, the creator and another member all belong to three different DCs, the other member messages
have to go through from their DC to the creator's DC and finally to your DC. This is applied to each message and member
of a supergroup/channel and the process will inevitably take its time.
Another reason that makes responses come slowly is that messages are dispatched by priority. Depending on the kind
of member, some users receive messages faster than others and for big and busy supergroups the delay might become
noticeable, especially if you are among the lower end of the priority list:
1. Creator.
2. Administrators.
3. Bots.
4. Mentioned users.
5. Recent online users.
6. Everyone else.

View File

@ -1,172 +0,0 @@
Welcome to Pyrogram
===================
.. raw:: html
<div align="center">
<a href="/">
<div class="pyrogram-logo-index"><img src="_static/pyrogram.png" alt="Pyrogram"></div>
<div class="pyrogram-text pyrogram-text-index">Pyrogram</div>
</a>
</div>
<p align="center">
<b>Telegram MTProto API Framework for Python</b>
<br>
<a href="https://pyrogram.org">
Homepage
</a>
<a href="https://github.com/pyrogram/pyrogram">
Development
</a>
<a href="https://docs.pyrogram.org/releases">
Releases
</a>
<a href="https://t.me/pyrogram">
News
</a>
</p>
.. code-block:: python
from pyrogram import Client, filters
app = Client("my_account")
@app.on_message(filters.private)
async def hello(client, message):
await message.reply("Hello from Pyrogram!")
app.run()
**Pyrogram** is a modern, elegant and asynchronous :doc:`MTProto API <topics/mtproto-vs-botapi>` framework.
It enables you to easily interact with the main Telegram API through a user account (custom client) or a bot identity
(bot API alternative) using Python.
Support
-------
If you'd like to support Pyrogram, you can consider:
- `Become a GitHub sponsor <https://github.com/sponsors/delivrance>`_.
- `Become a LiberaPay patron <https://liberapay.com/delivrance>`_.
- `Become an OpenCollective backer <https://opencollective.com/pyrogram>`_.
How the Documentation is Organized
----------------------------------
Contents are organized into sections composed of self-contained topics which can be all accessed from the sidebar, or by
following them in order using the :guilabel:`Next` button at the end of each page.
You can also switch to Dark or Light theme or leave on Auto (follows system preferences) by using the dedicated button
in the top left corner.
Here below you can, instead, find a list of the most relevant pages for a quick access.
First Steps
^^^^^^^^^^^
.. hlist::
:columns: 1
- :doc:`Quick Start <intro/quickstart>`: Overview to get you started quickly.
- :doc:`Invoking Methods <start/invoking>`: How to call Pyrogram's methods.
- :doc:`Handling Updates <start/updates>`: How to handle Telegram updates.
- :doc:`Error Handling <start/errors>`: How to handle API errors correctly.
API Reference
^^^^^^^^^^^^^
.. hlist::
:columns: 1
- :doc:`Pyrogram Client <api/client>`: Reference details about the Client class.
- :doc:`Available Methods <api/methods/index>`: List of available high-level methods.
- :doc:`Available Types <api/types/index>`: List of available high-level types.
- :doc:`Enumerations <api/enums/index>`: List of available enumerations.
- :doc:`Bound Methods <api/bound-methods/index>`: List of convenient bound methods.
Meta
^^^^
.. hlist::
:columns: 1
- :doc:`Pyrogram FAQ <faq/index>`: Answers to common Pyrogram questions.
- :doc:`Support Pyrogram <support>`: Ways to show your appreciation.
- :doc:`Release Notes <releases/index>`: Release notes for Pyrogram releases.
.. toctree::
:hidden:
:caption: Introduction
intro/quickstart
intro/install
.. toctree::
:hidden:
:caption: Getting Started
start/setup
start/auth
start/invoking
start/updates
start/errors
start/examples/index
.. toctree::
:hidden:
:caption: API Reference
api/client
api/methods/index
api/types/index
api/bound-methods/index
api/enums/index
api/handlers
api/decorators
api/errors/index
api/filters
.. toctree::
:hidden:
:caption: Topic Guides
topics/use-filters
topics/create-filters
topics/more-on-updates
topics/client-settings
topics/speedups
topics/text-formatting
topics/synchronous
topics/smart-plugins
topics/storage-engines
topics/serializing
topics/proxy
topics/scheduling
topics/mtproto-vs-botapi
topics/debugging
topics/test-servers
topics/advanced-usage
topics/voice-calls
.. toctree::
:hidden:
:caption: Meta
faq/index
support
releases/index
.. toctree::
:hidden:
:caption: Telegram Raw API
telegram/functions/index
telegram/types/index
telegram/base/index

View File

@ -1,50 +0,0 @@
Install Guide
=============
Being a modern Python framework, Pyrogram requires an up to date version of Python to be installed in your system.
We recommend using the latest versions of both Python 3 and pip.
.. contents:: Contents
:backlinks: none
:depth: 1
:local:
-----
Install Pyrogram
----------------
- The easiest way to install and upgrade Pyrogram to its latest stable version is by using **pip**:
.. code-block:: text
$ pip3 install -U pyrogram
- or, with :doc:`TgCrypto <../topics/speedups>` as extra requirement (recommended):
.. code-block:: text
$ pip3 install -U pyrogram tgcrypto
Bleeding Edge
-------------
You can install the development version from the git ``master`` branch using this command:
.. code-block:: text
$ pip3 install -U https://github.com/pyrogram/pyrogram/archive/master.zip
Verifying
---------
To verify that Pyrogram is correctly installed, open a Python shell and import it.
If no error shows up you are good to go.
.. parsed-literal::
>>> import pyrogram
>>> pyrogram.__version__
'x.y.z'
.. _`Github repo`: http://github.com/pyrogram/pyrogram

View File

@ -1,56 +0,0 @@
Quick Start
===========
The next few steps serve as a quick start to see Pyrogram in action as fast as possible.
Get Pyrogram Real Fast
----------------------
.. admonition :: Cloud Credits
:class: tip
If you need a cloud server to host your applications, try Hetzner Cloud. You can sign up with
`this link <https://hetzner.cloud/?ref=9CyT92gZEINU>`_ to get €20 in cloud credits.
1. Install Pyrogram with ``pip3 install -U pyrogram``.
2. Get your own Telegram API key from https://my.telegram.org/apps.
3. Open the text editor of your choice and paste the following:
.. code-block:: python
import asyncio
from pyrogram import Client
api_id = 12345
api_hash = "0123456789abcdef0123456789abcdef"
async def main():
async with Client("my_account", api_id, api_hash) as app:
await app.send_message("me", "Greetings from **Pyrogram**!")
asyncio.run(main())
4. Replace *api_id* and *api_hash* values with your own.
5. Save the file as ``hello.py``.
6. Run the script with ``python3 hello.py``
7. Follow the instructions on your terminal to login.
8. Watch Pyrogram send a message to yourself.
Enjoy the API
-------------
That was just a quick overview. In the next few pages of the introduction, we'll take a much more in-depth look of what
we have just done above.
If you are feeling eager to continue you can take a shortcut to :doc:`../start/invoking` and come back
later to learn some more details.
.. _community: https://t.me/Pyrogram

View File

@ -1,93 +0,0 @@
Authorization
=============
Once a :doc:`project is set up <setup>`, you will still have to follow a few steps before you can actually use Pyrogram to make
API calls. This section provides all the information you need in order to authorize yourself as user or bot.
.. contents:: Contents
:backlinks: none
:depth: 1
:local:
-----
User Authorization
------------------
In order to use the API, Telegram requires that users be authorized via their phone numbers.
Pyrogram automatically manages this process, all you need to do is create an instance of the
:class:`~pyrogram.Client` class by passing to it a ``name`` of your choice (e.g.: "my_account") and call
the :meth:`~pyrogram.Client.run` method:
.. code-block:: python
from pyrogram import Client
api_id = 12345
api_hash = "0123456789abcdef0123456789abcdef"
app = Client("my_account", api_id=api_id, api_hash=api_hash)
app.run()
This starts an interactive shell asking you to input your **phone number**, including your `Country Code`_ (the plus
``+`` and minus ``-`` symbols can be omitted) and the **phone code** you will receive in your devices that are already
authorized or via SMS:
.. code-block:: text
Enter phone number: +1-123-456-7890
Is "+1-123-456-7890" correct? (y/n): y
Enter phone code: 12345
Logged in successfully
After successfully authorizing yourself, a new file called ``my_account.session`` will be created allowing Pyrogram to
execute API calls with your identity. This file is personal and will be loaded again when you restart your app.
You can now remove the api_id and api_hash values from the code as they are not needed anymore.
.. note::
The code above does nothing except asking for credentials and keeping the client online, hit :guilabel:`CTRL+C` now
to stop your application and keep reading.
Bot Authorization
-----------------
Bots are a special kind of users that are authorized via their tokens (instead of phone numbers), which are created by
the `Bot Father`_. Bot tokens replace the users' phone numbers only — you still need to
:doc:`configure a Telegram API key <../start/setup>` with Pyrogram, even when using bots.
The authorization process is automatically managed. All you need to do is choose a ``name`` (can be anything,
usually your bot username) and pass your bot token using the ``bot_token`` parameter. The session file will be named
after the session name, which will be ``my_bot.session`` for the example below.
.. code-block:: python
from pyrogram import Client
api_id = 12345
api_hash = "0123456789abcdef0123456789abcdef"
bot_token = "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11"
app = Client(
"my_bot",
api_id=api_id, api_hash=api_hash,
bot_token=bot_token
)
app.run()
.. _Country Code: https://en.wikipedia.org/wiki/List_of_country_calling_codes
.. _Bot Father: https://t.me/botfather
.. note::
The API key (api_id and api_hash) and the bot_token are not required anymore after a successful authorization.
This means you can now simply use the following:
.. code-block:: python
from pyrogram import Client
app = Client("my_account")
app.run()

View File

@ -1,101 +0,0 @@
Error Handling
==============
Errors 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
from pyrogram import errors
.. contents:: Contents
:backlinks: none
:depth: 1
:local:
-----
RPCError
--------
The father of all errors is named ``RPCError`` and is able to catch all Telegram API related errors.
This error is raised every time a method call against Telegram's API was unsuccessful.
.. code-block:: python
from pyrogram.errors import RPCError
.. warning::
Avoid catching this error everywhere, especially when no feedback is given (i.e. by logging/printing the full error
traceback), because it makes it impossible to understand what went wrong.
Error Categories
----------------
The ``RPCError`` packs together all the possible errors Telegram could raise, but to make things tidier, Pyrogram
provides categories of errors, which are named after the common HTTP errors and are subclass-ed from the ``RPCError``:
.. code-block:: python
from pyrogram.errors import BadRequest, Forbidden, ...
- :doc:`303 - SeeOther <../api/errors/see-other>`
- :doc:`400 - BadRequest <../api/errors/bad-request>`
- :doc:`401 - Unauthorized <../api/errors/unauthorized>`
- :doc:`403 - Forbidden <../api/errors/forbidden>`
- :doc:`406 - NotAcceptable <../api/errors/not-acceptable>`
- :doc:`420 - Flood <../api/errors/flood>`
- :doc:`500 - InternalServerError <../api/errors/internal-server-error>`
Single Errors
-------------
For a fine-grained control over every single error, Pyrogram does also expose errors that deal each with a specific
issue. For example:
.. code-block:: python
from pyrogram.errors import FloodWait
These errors subclass directly from the category of errors they belong to, which in turn subclass from the father
``RPCError``, thus building a class of error hierarchy such as this:
- RPCError
- BadRequest
- ``MessageEmpty``
- ``UsernameOccupied``
- ``...``
- InternalServerError
- ``RpcCallFail``
- ``InterDcCallError``
- ``...``
- ``...``
.. _Errors: api/errors
Unknown Errors
--------------
In case Pyrogram does not know anything about a specific error yet, it raises a generic error from its known category,
for example, an unknown error with error code ``400``, will be raised as a ``BadRequest``. This way you can catch the
whole category of errors and be sure to also handle these unknown errors.
Errors with Values
------------------
Exception objects may also contain some informative values. For example, ``FloodWait`` holds the amount of seconds you
have to wait before you can try again, some other errors contain the DC number on which the request must be repeated on.
The value is stored in the ``value`` attribute of the exception object:
.. code-block:: python
import asyncio
from pyrogram.errors import FloodWait
...
try:
... # Your code
except FloodWait as e:
await asyncio.sleep(e.value) # Wait N seconds before continuing
...

View File

@ -1,68 +0,0 @@
bot_keyboards
=============
This example will show you how to send normal and inline keyboards (as bot).
You must log-in as a regular bot in order to send keyboards (use the token from @BotFather).
Any attempt in sending keyboards with a user account will be simply ignored by the server.
send_message() is used as example, but a keyboard can be sent with any other send_* methods,
like send_audio(), send_document(), send_location(), etc...
.. code-block:: python
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")
async def main():
async with app:
await app.send_message(
"me", # Edit this
"This is a ReplyKeyboardMarkup example",
reply_markup=ReplyKeyboardMarkup(
[
["A", "B", "C", "D"], # First row
["E", "F", "G"], # Second row
["H", "I"], # Third row
["J"] # Fourth row
],
resize_keyboard=True # Make the keyboard smaller
)
)
await app.send_message(
"me", # Edit this
"This is a InlineKeyboardMarkup example",
reply_markup=InlineKeyboardMarkup(
[
[ # First row
InlineKeyboardButton( # Generates a callback query when pressed
"Button",
callback_data="data"
),
InlineKeyboardButton( # Opens a web URL
"URL",
url="https://docs.pyrogram.org"
),
],
[ # Second row
InlineKeyboardButton( # Opens the inline interface
"Choose chat",
switch_inline_query="pyrogram"
),
InlineKeyboardButton( # Opens the inline interface in the current chat
"Inline here",
switch_inline_query_current_chat="pyrogram"
)
]
]
)
)
app.run(main())

View File

@ -1,21 +0,0 @@
callback_queries
================
This example shows how to handle callback queries, i.e.: queries coming from inline button presses.
It uses the @on_callback_query decorator to register a CallbackQueryHandler.
.. code-block:: python
from pyrogram import Client
app = Client("my_bot", bot_token="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11")
@app.on_callback_query()
async def answer(client, callback_query):
await callback_query.answer(
f"Button contains: '{callback_query.data}'",
show_alert=True)
app.run() # Automatically start() and idle()

View File

@ -1,21 +0,0 @@
echo_bot
========
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.
.. code-block:: python
from pyrogram import Client, filters
app = Client("my_account")
@app.on_message(filters.text & filters.private)
async def echo(client, message):
await message.reply(message.text)
app.run() # Automatically start() and idle()

View File

@ -1,20 +0,0 @@
get_history
===========
This example shows how to get the full message history of a chat, starting from the latest message.
.. code-block:: python
from pyrogram import Client
app = Client("my_account")
async def main():
async with app:
# "me" refers to your own chat (Saved Messages)
async for message in app.get_chat_history("me"):
print(message)
app.run(main())

View File

@ -1,22 +0,0 @@
get_chat_members
================
This example shows how to get all the members of a chat.
.. code-block:: python
from pyrogram import Client
# Target channel/supergroup
TARGET = -100123456789
app = Client("my_account")
async def main():
async with app:
async for member in app.get_chat_members(TARGET):
print(member)
app.run(main())

View File

@ -1,19 +0,0 @@
get_dialogs
===========
This example shows how to get the full dialogs list (as user).
.. code-block:: python
from pyrogram import Client
app = Client("my_account")
async def main():
async with app:
async for dialog in app.get_dialogs():
print(dialog.chat.title or dialog.chat.first_name)
app.run(main())

View File

@ -1,20 +0,0 @@
hello_world
===========
This example demonstrates a basic API usage
.. code-block:: python
from pyrogram import Client
# Create a new Client instance
app = Client("my_account")
async def main():
async with app:
# Send a message, Markdown is enabled by default
await app.send_message("me", "Hi there! I'm using **Pyrogram**")
app.run(main())

View File

@ -1,46 +0,0 @@
Examples
========
This page contains example scripts to show you how Pyrogram looks like.
Every script is working right away (provided you correctly set up your credentials), meaning you can simply copy-paste
and run. The only things you have to change are session names and target chats, where applicable.
The examples listed below can be treated as building blocks for your own applications and are meant to be simple enough
to give you a basic idea.
-----
.. csv-table::
:header: Example, Description
:widths: auto
:align: center
:doc:`hello_world`, "Demonstration of basic API usage"
:doc:`echo_bot`, "Echo every private text message"
:doc:`welcome_bot`, "The Welcome Bot in @PyrogramChat"
:doc:`get_chat_history`, "Get the full message history of a chat"
:doc:`get_chat_members`, "Get all the members of a chat"
:doc:`get_dialogs`, "Get all of your dialog chats"
:doc:`callback_queries`, "Handle callback queries (as bot) coming from inline button presses"
:doc:`inline_queries`, "Handle inline queries (as bot) and answer with results"
:doc:`use_inline_bots`, "Query an inline bot (as user) and send a result to a chat"
:doc:`bot_keyboards`, "Send normal and inline keyboards using regular bots"
:doc:`raw_updates`, "Handle raw updates (old, should be avoided)"
For more advanced examples, see https://snippets.pyrogram.org.
.. toctree::
:hidden:
hello_world
echo_bot
welcome_bot
get_chat_history
get_chat_members
get_dialogs
callback_queries
inline_queries
use_inline_bots
bot_keyboards
raw_updates

View File

@ -1,59 +0,0 @@
inline_queries
==============
This example shows how to handle inline queries.
Two results are generated when users invoke the bot inline mode, e.g.: @pyrogrambot hi.
It uses the @on_inline_query decorator to register an InlineQueryHandler.
.. code-block:: python
from pyrogram import Client
from pyrogram.types import (InlineQueryResultArticle, InputTextMessageContent,
InlineKeyboardMarkup, InlineKeyboardButton)
app = Client("my_bot", bot_token="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11")
@app.on_inline_query()
async def answer(client, inline_query):
await inline_query.answer(
results=[
InlineQueryResultArticle(
title="Installation",
input_message_content=InputTextMessageContent(
"Here's how to install **Pyrogram**"
),
url="https://docs.pyrogram.org/intro/install",
description="How to install Pyrogram",
reply_markup=InlineKeyboardMarkup(
[
[InlineKeyboardButton(
"Open website",
url="https://docs.pyrogram.org/intro/install"
)]
]
)
),
InlineQueryResultArticle(
title="Usage",
input_message_content=InputTextMessageContent(
"Here's how to use **Pyrogram**"
),
url="https://docs.pyrogram.org/start/invoking",
description="How to use Pyrogram",
reply_markup=InlineKeyboardMarkup(
[
[InlineKeyboardButton(
"Open website",
url="https://docs.pyrogram.org/start/invoking"
)]
]
)
)
],
cache_time=1
)
app.run() # Automatically start() and idle()

View File

@ -1,18 +0,0 @@
raw_updates
===========
This example shows how to handle raw updates.
.. code-block:: python
from pyrogram import Client
app = Client("my_account")
@app.on_raw_update()
async def raw(client, update, users, chats):
print(update)
app.run() # Automatically start() and idle()

View File

@ -1,25 +0,0 @@
use_inline_bots
===============
This example shows how to query an inline bot (as user).
.. code-block:: python
from pyrogram import Client
# Create a new Client
app = Client("my_account")
async def main():
async with app:
# Get bot results for "hello" from the inline bot @vid
bot_results = await app.get_inline_bot_results("vid", "hello")
# Send the first result to your own chat (Saved Messages)
await app.send_inline_bot_result(
"me", bot_results.query_id,
bot_results.results[0].id)
app.run(main())

View File

@ -1,30 +0,0 @@
welcome_bot
===========
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
# Target chat. Can also be a list of multiple chat ids/usernames
TARGET = -100123456789
# Welcome message template
MESSAGE = "{} Welcome to [Pyrogram](https://docs.pyrogram.org/)'s group chat {}!"
app = Client("my_account")
# Filter in only new_chat_members updates generated in TARGET chat
@app.on_message(filters.chat(TARGET) & filters.new_chat_members)
async def welcome(client, message):
# Build the new members list (with mentions) by using their first_name
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))
# Send the welcome message, without the web page preview
await message.reply_text(text, disable_web_page_preview=True)
app.run() # Automatically start() and idle()

View File

@ -1,110 +0,0 @@
Invoking Methods
================
At this point, we have successfully :doc:`installed Pyrogram <../intro/install>` and :doc:`authorized <auth>` our
account; we are now aiming towards the core of the framework.
.. contents:: Contents
:backlinks: none
:depth: 1
:local:
-----
Basic Usage
-----------
Making API calls with Pyrogram is very simple. Here's a basic example we are going to examine step by step:
.. code-block:: python
from pyrogram import Client
app = Client("my_account")
async def main():
async with app:
await app.send_message("me", "Hi!")
app.run(main())
Step-by-step
^^^^^^^^^^^^
#. Let's begin by importing the Client class.
.. code-block:: python
from pyrogram import Client
#. Now instantiate a new Client object, "my_account" is a session name of your choice.
.. code-block:: python
app = Client("my_account")
#. Async methods must be invoked within an async context.
Here we define an async function and put our code inside. Also notice the ``await`` keyword in front of the method
call; this is required for all asynchronous methods.
.. code-block:: python
async def main():
async with app:
await app.send_message("me", "Hi!")
#. Finally, we tell Python to schedule our ``main()`` async function by using Pyrogram's :meth:`~pyrogram.Client.run`
method.
.. code-block:: python
app.run(main())
Context Manager
---------------
The ``async with`` statement starts a context manager, which is used as a shortcut for starting, executing and stopping
the Client, asynchronously. It does so by automatically calling :meth:`~pyrogram.Client.start` and
:meth:`~pyrogram.Client.stop` in a more convenient way which also gracefully stops the client, even in case of
unhandled exceptions in your code.
Below there's the same example as above, but without the use of the context manager:
.. code-block:: python
from pyrogram import Client
app = Client("my_account")
async def main():
await app.start()
await app.send_message("me", "Hi!")
await app.stop()
app.run(main())
Using asyncio.run()
-------------------
Alternatively to the :meth:`~pyrogram.Client.run` method, you can use Python's ``asyncio.run()`` to execute the main
function, with one little caveat: the Client instance (and possibly other asyncio resources you are going to use) must
be instantiated inside the main function.
.. code-block:: python
import asyncio
from pyrogram import Client
async def main():
app = Client("my_account")
async with app:
await app.send_message("me", "Hi!")
asyncio.run(main())

View File

@ -1,40 +0,0 @@
Project Setup
=============
We have just :doc:`installed Pyrogram <../intro/install>`. In this page we'll discuss what you need to do in order to set up a
project with the framework.
.. contents:: Contents
:backlinks: none
:depth: 1
:local:
-----
API Key
-------
The first step requires you to obtain a valid Telegram API key (api_id and api_hash pair):
#. Visit https://my.telegram.org/apps and log in with your Telegram account.
#. Fill out the form with your details and register a new Telegram application.
#. Done. The API key consists of two parts: **api_id** and **api_hash**. Keep it secret.
.. note::
The API key defines a token for a Telegram *application* you are going to build.
This means that you are able to authorize multiple users or bots with a single API key.
Configuration
-------------
Having the API key from the previous step in handy, we can now begin to configure a Pyrogram project: pass your API key to Pyrogram by using the *api_id* and *api_hash* parameters of the Client class:
.. code-block:: python
from pyrogram import Client
api_id = 12345
api_hash = "0123456789abcdef0123456789abcdef"
app = Client("my_account", api_id=api_id, api_hash=api_hash)

View File

@ -1,78 +0,0 @@
Handling Updates
================
:doc:`Invoking API methods <invoking>` sequentially is one way to use Pyrogram. This page deals with Telegram updates
and how to handle new incoming messages or other events in Pyrogram.
.. contents:: Contents
:backlinks: none
:depth: 1
:local:
-----
Defining Updates
----------------
Updates are events that happen in your Telegram account (incoming messages, new members join,
bot button presses, etc.), which are meant to notify you about a new specific state that has changed. These updates are
handled by registering one or more callback functions in your app using :doc:`Handlers <../api/handlers>`.
Each handler deals with a specific event and once a matching update arrives from Telegram, your registered callback
function will be called back by the framework and its body executed.
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 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
^^^^^^^^^^^^^^^^
The most elegant way to register a message handler is by using the :meth:`~pyrogram.Client.on_message` decorator:
.. code-block:: python
from pyrogram import Client
app = Client("my_account")
@app.on_message()
async def my_handler(client, message):
await message.forward("me")
app.run()
The defined function ``my_handler``, which accepts the two arguments *(client, message)*, will be the function that gets
executed every time a new message arrives.
In the last line we see again the :meth:`~pyrogram.Client.run` method, this time used without any argument.
Its purpose here is simply to automatically :meth:`~pyrogram.Client.start`, keep the Client online so that it can listen
for updates and :meth:`~pyrogram.Client.stop` it once you hit ``CTRL+C``.
Using add_handler()
^^^^^^^^^^^^^^^^^^^
The :meth:`~pyrogram.Client.add_handler` method takes any handler instance that wraps around your defined callback
function and registers it in your Client. It is useful in case you want to programmatically add handlers.
.. code-block:: python
from pyrogram import Client
from pyrogram.handlers import MessageHandler
async def my_function(client, message):
await message.forward("me")
app = Client("my_account")
my_handler = MessageHandler(my_function)
app.add_handler(my_handler)
app.run()

View File

@ -1,63 +0,0 @@
Support Pyrogram
================
.. raw:: html
<script async defer src="https://buttons.github.io/buttons.js"></script>
<div style="float: right; margin-bottom: 10px">
<a class="github-button"
href="https://github.com/pyrogram/pyrogram"
data-color-scheme="no-preference: light; light: light; dark: dark;"
data-icon="octicon-star" data-size="large" data-show-count="true"
aria-label="Star pyrogram/pyrogram on GitHub">Star</a>
<a class="github-button"
href="https://github.com/pyrogram/pyrogram/fork"
data-color-scheme="no-preference: light; light: light; dark: dark;"
data-icon="octicon-repo-forked" data-size="large"
data-show-count="true" aria-label="Fork pyrogram/pyrogram on GitHub">Fork</a>
</div>
<br style="clear: both"/>
Pyrogram is a free and open source project.
If you enjoy Pyrogram and would like to show your appreciation, consider donating or becoming
a sponsor of the project. You can support Pyrogram via the ways shown below:
-----
GitHub Sponsor
--------------
`Become a GitHub sponsor <https://github.com/sponsors/delivrance>`_.
.. raw:: html
<a class="github-button"
href="https://github.com/sponsors/delivrance"
data-color-scheme="no-preference: light; light: light; dark: dark;"
data-icon="octicon-heart" data-size="large"
aria-label="Sponsor @delivrance on GitHub">Sponsor</a>
-----
LiberaPay Patron
----------------
`Become a LiberaPay patron <https://liberapay.com/delivrance>`_.
.. raw:: html
<script src="https://liberapay.com/delivrance/widgets/button.js"></script>
-----
OpenCollective Backer
---------------------
`Become an OpenCollective backer <https://opencollective.com/pyrogram>`_
.. raw:: html
<script src="https://opencollective.com/pyrogram/banner.js"></script>

View File

@ -1,124 +0,0 @@
Advanced Usage
==============
Pyrogram's API -- which consists of well documented :doc:`methods <../api/methods/index>` and
:doc:`types <../api/types/index>` -- exists to provide an easier interface to the more complex Telegram API.
In this section, you'll be shown the alternative way of communicating with Telegram using Pyrogram: the main "raw"
Telegram API with its functions and types.
.. contents:: Contents
:backlinks: none
:depth: 1
:local:
-----
Telegram Raw API
----------------
If you can't find a high-level method for your needs or if you want complete, low-level access to the whole
Telegram API, you have to use the raw :mod:`~pyrogram.raw.functions` and :mod:`~pyrogram.raw.types`.
As already hinted, raw functions and types can be less convenient. This section will therefore explain some pitfalls to
take into consideration when working with the raw API.
.. tip::
Every available high-level method in Pyrogram is built on top of these raw functions.
Invoking Functions
------------------
Unlike the :doc:`methods <../api/methods/index>` found in Pyrogram's API, which can be called in the usual simple way,
functions to be invoked from the raw Telegram API have a different way of usage.
First of all, both :doc:`raw functions <../telegram/functions/index>` and :doc:`raw types <../telegram/types/index>`
live in their respective packages (and sub-packages): ``pyrogram.raw.functions``, ``pyrogram.raw.types``. They all exist
as Python classes, meaning you need to create an instance of each every time you need them and fill them in with the
correct values using named arguments.
Next, to actually invoke the raw function you have to use the :meth:`~pyrogram.Client.invoke` method provided by the
Client class and pass the function object you created.
Here's some examples:
- Update first name, last name and bio:
.. code-block:: python
from pyrogram import Client
from pyrogram.raw import functions
async with Client("my_account") as app:
await app.invoke(
functions.account.UpdateProfile(
first_name="First Name", last_name="Last Name",
about="New bio text"
)
)
- Set online/offline status:
.. code-block:: python
from pyrogram import Client
from pyrogram.raw import functions, types
async with Client("my_account") as app:
# Set online status
await app.invoke(functions.account.UpdateStatus(offline=False))
# Set offline status
await app.invoke(functions.account.UpdateStatus(offline=True))
- Get chat info:
.. code-block:: python
from pyrogram import Client
from pyrogram.raw import functions, types
async with Client("my_account") as app:
r = await app.invoke(
functions.channels.GetFullChannel(
channel=app.resolve_peer("username")
)
)
print(r)
Chat IDs
--------
The way Telegram works makes it not possible to directly send a message to a user or a chat by using their IDs only.
Instead, a pair of ``id`` and ``access_hash`` wrapped in a so called ``InputPeer`` is always needed. Pyrogram allows
sending messages with IDs only thanks to cached access hashes.
There are three different InputPeer types, one for each kind of Telegram entity.
Whenever an InputPeer is needed you must pass one of these:
- :class:`~pyrogram.raw.types.InputPeerUser` - Users
- :class:`~pyrogram.raw.types.InputPeerChat` - Basic Chats
- :class:`~pyrogram.raw.types.InputPeerChannel` - Channels & Supergroups
But you don't necessarily have to manually instantiate each object because Pyrogram already provides
:meth:`~pyrogram.Client.resolve_peer` as a convenience utility method that returns the correct InputPeer
by accepting a peer ID only.
Another thing to take into consideration about chat IDs is the way they are represented: they are all integers and
all positive within their respective raw types.
Things are different when working with Pyrogram's API because having them in the same space could lead to
collisions, and that's why Pyrogram uses a slightly different representation for each kind of ID.
For example, given the ID *123456789*, here's how Pyrogram can tell entities apart:
- ``+ID`` User: *123456789*
- ``-ID`` Chat: *-123456789*
- ``-100ID`` Channel or Supergroup: *-100123456789*
So, every time you take a raw ID, make sure to translate it into the correct ID when you want to use it with an
high-level method.
.. _Community: https://t.me/Pyrogram

View File

@ -1,46 +0,0 @@
Client Settings
===============
You can control the way your client appears in the Active Sessions menu of an official client by changing some client
settings. By default you will see something like the following:
- Device Model: ``CPython x.y.z``
- Application: ``Pyrogram x.y.z``
- System Version: ``Linux x.y.z``
.. contents:: Contents
:backlinks: none
:depth: 1
:local:
-----
Set Custom Values
-----------------
To set custom values, you can pass the arguments directly in the Client's constructor.
.. code-block:: python
app = Client(
"my_account",
app_version="1.2.3",
device_model="PC",
system_version="Linux"
)
Set Custom Languages
--------------------
To tell Telegram in which language should speak to you (terms of service, bots, service messages, ...) you can
set ``lang_code`` in `ISO 639-1 <https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes>`_ standard (defaults to "en",
English).
With the following code we make Telegram know we want it to speak in Italian (it):
.. code-block:: python
app = Client(
"my_account",
lang_code="it",
)

View File

@ -1,109 +0,0 @@
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:`filters.create() <pyrogram.filters.create>`.
.. contents:: Contents
:backlinks: none
:depth: 1
:local:
-----
Custom Filters
--------------
An example to demonstrate how custom filters work is to show how to create and use one for the
:class:`~pyrogram.handlers.CallbackQueryHandler`. Note that callback queries updates are only received by bots as result
of a user pressing an inline button attached to the bot's message; 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.types import InlineKeyboardMarkup, InlineKeyboardButton
await app.send_message(
"username", # Change this to your username or id
"Pyrogram custom filter test",
reply_markup=InlineKeyboardMarkup(
[[InlineKeyboardButton("Press me", "pyrogram")]]
)
)
Basic Filters
-------------
For this basic filter we will be using only the first parameter of :meth:`~pyrogram.filters.create`.
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
async def func(_, __, query):
return query.data == "pyrogram"
static_data_filter = filters.create(func)
The first two arguments of the callback function are unused here and because of this we named them using underscores.
Finally, the filter usage remains the same:
.. code-block:: python
@app.on_callback_query(static_data_filter)
async def pyrogram_data(_, query):
query.answer("it works!")
Filters with Arguments
----------------------
A more flexible 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:
.. 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"))
async 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
async def func(_, client, query):
# r = await client.some_api_method()
# check response "r" and decide to return True or False
...

View File

@ -1,122 +0,0 @@
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 since Pyrogram provides some commodities to help you in this.
.. contents:: Contents
:backlinks: none
:depth: 1
:local:
-----
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
me = await app.get_users("me")
print(me) # User
This will show a JSON representation of the object returned by :meth:`~pyrogram.Client.get_users`, which is a
:class:`~pyrogram.types.User` instance, in this case. The output on your terminal will be something similar to this:
.. code-block:: json
{
"_": "User",
"id": 123456789,
"is_self": true,
"is_contact": false,
"is_mutual_contact": false,
"is_deleted": false,
"is_bot": false,
"is_verified": false,
"is_restricted": false,
"is_support": false,
"first_name": "Pyrogram",
"photo": {
"_": "ChatPhoto",
"small_file_id": "AbCdE...EdCbA",
"small_photo_unique_id": "VwXyZ...ZyXwV",
"big_file_id": "AbCdE...EdCbA",
"big_photo_unique_id": "VwXyZ...ZyXwV"
}
}
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
fully-fledged Python objects and the correct way to access any attribute of them is by using the dot notation ``.``:
.. code-block:: python
photo = me.photo
print(photo) # ChatPhoto
.. code-block:: json
{
"_": "ChatPhoto",
"small_file_id": "AbCdE...EdCbA",
"small_photo_unique_id": "VwXyZ...ZyXwV",
"big_file_id": "AbCdE...EdCbA",
"big_photo_unique_id": "VwXyZ...ZyXwV"
}
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
status = me.status
print(type(status))
.. code-block:: text
<class 'pyrogram.types.UserStatus'>
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.types import UserStatus
status = me.status
print(isinstance(status, UserStatus))
.. code-block:: text
True
.. raw:: html
<script>
var e = document.querySelector("blockquote p.attribution");
var s = e.innerHTML;
e.innerHTML = s[0] + " " + s.slice(1);
</script>

View File

@ -1,226 +0,0 @@
More on Updates
===============
Here we'll show some advanced usages when working with :doc:`update handlers <../start/updates>` and
:doc:`filters <use-filters>`.
.. contents:: Contents
:backlinks: none
:depth: 1
:local:
-----
Handler Groups
--------------
If you register handlers with overlapping (conflicting) filters, only the first one is executed and any other handler
will be ignored. This is intended by design.
In order to handle the very same update more than once, you have to register your handler in a different dispatching
group. Dispatching groups hold one or more handlers and are processed sequentially, they are identified by a number
(number 0 being the default) and sorted, that is, a lower group number has a higher priority:
For example, take these two handlers:
.. code-block:: python
@app.on_message(filters.text | filters.sticker)
async def text_or_sticker(client, message):
print("Text or Sticker")
@app.on_message(filters.text)
async def just_text(client, message):
print("Just Text")
Here, ``just_text`` is never executed because ``text_or_sticker``, which has been registered first, already handles
texts (``filters.text`` is shared and conflicting). To enable it, register the handler using a different group:
.. code-block:: python
@app.on_message(filters.text, group=1)
async def just_text(client, message):
print("Just Text")
Or, if you want ``just_text`` to be executed *before* ``text_or_sticker`` (note ``-1``, which is less than ``0``):
.. code-block:: python
@app.on_message(filters.text, group=-1)
async def just_text(client, message):
print("Just Text")
With :meth:`~pyrogram.Client.add_handler` (without decorators) the same can be achieved with:
.. code-block:: python
app.add_handler(MessageHandler(just_text, filters.text), -1)
Update propagation
------------------
Registering multiple handlers, each in a different group, becomes useful when you want to handle the same update more
than once. Any incoming update will be sequentially processed by all of your registered functions by respecting the
groups priority policy described above. Even in case any handler raises an unhandled exception, Pyrogram will still
continue to propagate the same update to the next groups until all the handlers are done. Example:
.. code-block:: python
@app.on_message(filters.private)
async def _(client, message):
print(0)
@app.on_message(filters.private, group=1)
async def _(client, message):
raise Exception("Unhandled exception!") # Simulate an unhandled exception
@app.on_message(filters.private, group=2)
async def _(client, message):
print(2)
All these handlers will handle the same kind of messages, that are, messages sent or received in private chats.
The output for each incoming update will therefore be:
.. code-block:: text
0
Exception: Unhandled exception!
2
Stop Propagation
^^^^^^^^^^^^^^^^
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).
- Manually ``raise StopPropagation`` exception (more suitable for raw updates only).
.. note::
Internally, the propagation is stopped by handling a custom exception. ``.stop_propagation()`` is just an elegant
and intuitive way to ``raise StopPropagation``; 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 not to propagate the
update anymore.
Example with ``stop_propagation()``:
.. code-block:: python
@app.on_message(filters.private)
async def _(client, message):
print(0)
@app.on_message(filters.private, group=1)
async def _(client, message):
print(1)
message.stop_propagation()
@app.on_message(filters.private, group=2)
async def _(client, message):
print(2)
Example with ``raise StopPropagation``:
.. code-block:: python
from pyrogram import StopPropagation
@app.on_message(filters.private)
async def _(client, message):
print(0)
@app.on_message(filters.private, group=1)
async ef _(client, message):
print(1)
raise StopPropagation
@app.on_message(filters.private, group=2)
async def _(client, message):
print(2)
Each handler is registered in a different group, but the handler in group number 2 will never be executed because the
propagation was stopped earlier. The output of both (equivalent) examples will be:
.. code-block:: text
0
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 same group** despite having conflicting filters in the next registered handler. 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)
async def _(client, message):
print(0)
message.continue_propagation()
@app.on_message(filters.private)
async def _(client, message):
print(1)
message.continue_propagation()
@app.on_message(filters.private)
async def _(client, message):
print(2)
Example with ``raise ContinuePropagation``:
.. code-block:: python
from pyrogram import ContinuePropagation
@app.on_message(filters.private)
async def _(client, message):
print(0)
raise ContinuePropagation
@app.on_message(filters.private)
async def _(client, message):
print(1)
raise ContinuePropagation
@app.on_message(filters.private)
async 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

View File

@ -1,112 +0,0 @@
MTProto vs. Bot API
===================
Pyrogram is a framework written from the ground up that acts as a fully-fledged Telegram client based on the MTProto
API. This means that Pyrogram is able to execute any official client and bot API action and more. This page will
therefore show you why Pyrogram might be a better choice for your project by comparing the two APIs, but first, let's
make it clear what actually is the MTProto and the Bot API.
.. contents:: Contents
:backlinks: none
:depth: 1
:local:
-----
What is the MTProto API?
------------------------
`MTProto`_, took alone, is the name of the custom-made, open and encrypted communication protocol created by Telegram
itself --- it's the only protocol used to exchange information between a client and the actual Telegram servers.
The MTProto API on the other hand, is what people for convenience call the main Telegram API in order to distinguish it
from the Bot API. The main Telegram API is able to authorize both users and bots and is built on top of the MTProto
encryption protocol by means of `binary data serialized`_ in a specific way, as described by the `TL language`_, and
delivered using UDP, TCP or even HTTP as transport-layer protocol. Clients that make use of Telegram's main API, such as
Pyrogram, implement all these details.
.. _MTProto: https://core.telegram.org/mtproto
.. _binary data serialized: https://core.telegram.org/mtproto/serialize
.. _TL language: https://core.telegram.org/mtproto/TL
What is the Bot API?
--------------------
The `Bot API`_ is an HTTP(S) interface for building normal bots using a sub-set of the main Telegram API. Bots are
special accounts that are authorized via tokens instead of phone numbers. The Bot API is built yet again on top of the
main Telegram API, but runs on an intermediate server application that in turn communicates with the actual Telegram
servers using MTProto.
.. figure:: //_static/img/mtproto-vs-bot-api.png
:align: center
.. _Bot API: https://core.telegram.org/bots/api
Advantages of the MTProto API
-----------------------------
Here is a non-exhaustive list of all the advantages in using MTProto-based libraries -- such as Pyrogram -- instead of
the official HTTP Bot API. Using Pyrogram you can:
.. hlist::
:columns: 1
- :guilabel:`+` **Authorize both user and bot identities**
- :guilabel:`--` The Bot API only allows bot accounts
.. hlist::
:columns: 1
- :guilabel:`+` **Upload & download any file, up to 2000 MiB each (~2 GB)**
- :guilabel:`--` The Bot API allows uploads and downloads of files only up to 50 MB / 20 MB in size (respectively).
.. hlist::
:columns: 1
- :guilabel:`+` **Has less overhead due to direct connections to Telegram**
- :guilabel:`--` The Bot API uses an intermediate server to handle HTTP requests before they are sent to the actual
Telegram servers.
.. hlist::
:columns: 1
- :guilabel:`+` **Run multiple sessions at once (for both user and bot identities)**
- :guilabel:`--` The Bot API intermediate server will terminate any other session in case you try to use the same
bot again in a parallel connection.
.. hlist::
:columns: 1
- :guilabel:`+` **Has much more detailed types and powerful methods**
- :guilabel:`--` The Bot API types often miss some useful information about Telegram entities and some of the
methods are limited as well.
.. hlist::
:columns: 1
- :guilabel:`+` **Obtain information about any message existing in a chat using their ids**
- :guilabel:`--` The Bot API simply doesn't support this
.. hlist::
:columns: 1
- :guilabel:`+` **Retrieve the whole chat members list of either public or private chats**
- :guilabel:`--` The Bot API simply doesn't support this
.. hlist::
:columns: 1
- :guilabel:`+` **Receive extra updates, such as the one about a user name change**
- :guilabel:`--` The Bot API simply doesn't support this
.. hlist::
:columns: 1
- :guilabel:`+` **Has more meaningful errors in case something went wrong**
- :guilabel:`--` The Bot API reports less detailed errors
.. hlist::
:columns: 1
- :guilabel:`+` **Get API version updates, and thus new features, sooner**
- :guilabel:`--` The Bot API is simply slower in implementing new features

View File

@ -1,34 +0,0 @@
Proxy Settings
==============
Pyrogram supports proxies with and without authentication. This feature allows Pyrogram to exchange data with Telegram
through an intermediate SOCKS 4/5 or HTTP (CONNECT) proxy server.
.. contents:: Contents
:backlinks: none
:depth: 1
:local:
-----
Usage
-----
To use Pyrogram with a proxy, use the *proxy* parameter in the Client class. If your proxy doesn't require authorization
you can omit ``username`` and ``password``.
.. code-block:: python
from pyrogram import Client
proxy = {
"scheme": "socks5", # "socks4", "socks5" and "http" are supported
"hostname": "11.22.33.44",
"port": 1234,
"username": "username",
"password": "password"
}
app = Client("my_account", proxy=proxy)
app.run()

View File

@ -1,65 +0,0 @@
Scheduling Tasks
================
Scheduling tasks means executing one or more functions periodically at pre-defined intervals or after a delay. This is
useful, for example, to send recurring messages to specific chats or users.
This page will show examples on how to integrate Pyrogram with ``apscheduler`` in both asynchronous and
non-asynchronous contexts. For more detailed information, you can visit and learn from the library documentation.
.. contents:: Contents
:backlinks: none
:depth: 1
:local:
-----
Using apscheduler
-----------------
- Install with ``pip3 install apscheduler``
- Documentation: https://apscheduler.readthedocs.io
Asynchronously
^^^^^^^^^^^^^^
.. code-block:: python
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from pyrogram import Client
app = Client("my_account")
async def job():
await app.send_message("me", "Hi!")
scheduler = AsyncIOScheduler()
scheduler.add_job(job, "interval", seconds=3)
scheduler.start()
app.run()
Non-Asynchronously
^^^^^^^^^^^^^^^^^^
.. code-block:: python
from apscheduler.schedulers.background import BackgroundScheduler
from pyrogram import Client
app = Client("my_account")
def job():
app.send_message("me", "Hi!")
scheduler = BackgroundScheduler()
scheduler.add_job(job, "interval", seconds=3)
scheduler.start()
app.run()

View File

@ -1,56 +0,0 @@
Object Serialization
====================
Serializing means converting a Pyrogram object, which exists as Python class instance, to a text string that can be
easily shared and stored anywhere. Pyrogram provides two formats for serializing its objects: one good looking for
humans and another more compact for machines that is able to recover the original structures.
.. contents:: Contents
:backlinks: none
:depth: 1
:local:
-----
For Humans - str(obj)
---------------------
If you want a nicely formatted, human readable JSON representation of any object in the API you can use ``str(obj)``.
.. code-block:: python
...
async with app:
r = await app.get_chat("me")
print(str(r))
.. tip::
When using ``print()`` you don't actually need to use ``str()`` on the object because it is called automatically, we
have done that above just to show you how to explicitly convert a Pyrogram object to JSON.
For Machines - repr(obj)
------------------------
If you want to share or store objects for future references in a more compact way, you can use ``repr(obj)``. While
still pretty much readable, this format is not intended for humans. The advantage of this format is that once you
serialize your object, you can use ``eval()`` to get back the original structure; just make sure to ``import pyrogram``,
as the process requires the package to be in scope.
.. code-block:: python
import pyrogram
...
async with app:
r = await app.get_chat("me")
print(repr(r))
print(eval(repr(r)) == r) # True
.. note::
Type definitions are subject to changes between versions. You should make sure to store and load objects using the
same Pyrogram version.

View File

@ -1,306 +0,0 @@
Smart Plugins
=============
Pyrogram embeds a smart, lightweight yet powerful plugin system that is meant to further simplify the organization
of large projects and to provide a way for creating pluggable (modular) components that can be easily shared across
different Pyrogram applications with minimal boilerplate code.
.. tip::
Smart Plugins are completely optional and disabled by default.
.. contents:: Contents
:backlinks: none
:depth: 1
:local:
-----
Introduction
------------
Prior to the Smart Plugin system, pluggable handlers were already possible. For example, if you wanted to modularize
your applications, you had to put your function definitions in separate files and register them inside your main script
after importing your modules, like this:
.. note::
This is an example application that replies in private chats with two messages: one containing the same
text message you sent and the other containing the reversed text message.
Example: *"Pyrogram"* replies with *"Pyrogram"* and *"margoryP"*
.. code-block:: text
myproject/
handlers.py
main.py
- ``handlers.py``
.. code-block:: python
async def echo(client, message):
await message.reply(message.text)
async def echo_reversed(client, message):
await message.reply(message.text[::-1])
- ``main.py``
.. code-block:: python
from pyrogram import Client, filters
from pyrogram.handlers import MessageHandler
from handlers import echo, echo_reversed
app = Client("my_account")
app.add_handler(
MessageHandler(
echo,
filters.text & filters.private))
app.add_handler(
MessageHandler(
echo_reversed,
filters.text & filters.private),
group=1)
app.run()
This is already nice and doesn't add *too much* boilerplate code, but things can get boring still; you have to
manually ``import``, manually :meth:`~pyrogram.Client.add_handler` and manually instantiate each
:class:`~pyrogram.handlers.MessageHandler` object because you can't use decorators for your functions.
So, what if you could? Smart Plugins solve this issue by taking care of handlers registration automatically.
Using Smart Plugins
-------------------
Setting up your Pyrogram project to accommodate Smart Plugins is pretty straightforward:
#. Create a new folder to store all the plugins (e.g.: "plugins", "handlers", ...).
#. Put your python files full of plugins inside. Organize them as you wish.
#. Enable plugins in your Client.
.. note::
This is the same example application as shown above, written using the Smart Plugin system.
.. code-block:: text
myproject/
plugins/
handlers.py
main.py
- ``plugins/handlers.py``
.. code-block:: python
from pyrogram import Client, filters
@Client.on_message(filters.text & filters.private)
async def echo(client, message):
await message.reply(message.text)
@Client.on_message(filters.text & filters.private, group=1)
async def echo_reversed(client, message):
await message.reply(message.text[::-1])
- ``main.py``
.. code-block:: python
from pyrogram import Client
plugins = dict(root="plugins")
Client("my_account", plugins=plugins).run()
The first important thing to note is the new ``plugins`` folder. You can put *any python file* in *any subfolder* and
each file can contain *any decorated function* (handlers) with one limitation: within a single module (file) you must
use different names for each decorated function.
The second thing is telling Pyrogram where to look for your plugins: you can use the Client parameter "plugins";
the *root* value must match the name of your plugins root folder. Your Pyrogram Client instance will **automatically**
scan the folder upon starting to search for valid handlers and register them for you.
Then you'll notice you can now use decorators. That's right, you can apply the usual decorators to your callback
functions in a static way, i.e. **without having the Client instance around**: simply use ``@Client`` (Client class)
instead of the usual ``@app`` (Client instance) and things will work just the same.
Specifying the Plugins to include
---------------------------------
By default, if you don't explicitly supply a list of plugins, every valid one found inside your plugins root folder will
be included by following the alphabetical order of the directory structure (files and subfolders); the single handlers
found inside each module will be, instead, loaded in the order they are defined, from top to bottom.
.. note::
Remember: there can be at most one handler, within a group, dealing with a specific update. Plugins with overlapping
filters included a second time will not work, by design. Learn more at :doc:`More on Updates <more-on-updates>`.
This default loading behaviour is usually enough, but sometimes you want to have more control on what to include (or
exclude) and in which exact order to load plugins. The way to do this is to make use of ``include`` and ``exclude``
directives in the dictionary passed as Client argument. Here's how they work:
- If both ``include`` and ``exclude`` are omitted, all plugins are loaded as described above.
- If ``include`` is given, only the specified plugins will be loaded, in the order they are passed.
- If ``exclude`` is given, the plugins specified here will be unloaded.
The ``include`` and ``exclude`` value is a **list of strings**. Each string containing the path of the module relative
to the plugins root folder, in Python notation (dots instead of slashes).
E.g.: ``subfolder.module`` refers to ``plugins/subfolder/module.py``, with ``root="plugins"``.
You can also choose the order in which the single handlers inside a module are loaded, thus overriding the default
top-to-bottom loading policy. You can do this by appending the name of the functions to the module path, each one
separated by a blank space.
E.g.: ``subfolder.module fn2 fn1 fn3`` will load *fn2*, *fn1* and *fn3* from *subfolder.module*, in this order.
Examples
^^^^^^^^
Given this plugins folder structure with three modules, each containing their own handlers (fn1, fn2, etc...), which are
also organized in subfolders:
.. code-block:: text
myproject/
plugins/
subfolder1/
plugins1.py
- fn1
- fn2
- fn3
subfolder2/
plugins2.py
...
plugins0.py
...
...
- Load every handler from every module, namely *plugins0.py*, *plugins1.py* and *plugins2.py* in alphabetical order
(files) and definition order (handlers inside files):
.. code-block:: python
plugins = dict(root="plugins")
Client("my_account", plugins=plugins).run()
- Load only handlers defined inside *plugins2.py* and *plugins0.py*, in this order:
.. code-block:: python
plugins = dict(
root="plugins",
include=[
"subfolder2.plugins2",
"plugins0"
]
)
Client("my_account", plugins=plugins).run()
- Load everything except the handlers inside *plugins2.py*:
.. code-block:: python
plugins = dict(
root="plugins",
exclude=["subfolder2.plugins2"]
)
Client("my_account", plugins=plugins).run()
- Load only *fn3*, *fn1* and *fn2* (in this order) from *plugins1.py*:
.. code-block:: python
plugins = dict(
root="plugins",
include=["subfolder1.plugins1 fn3 fn1 fn2"]
)
Client("my_account", plugins=plugins).run()
Load/Unload Plugins at Runtime
------------------------------
In the previous section we've explained how to specify which plugins to load and which to ignore before your Client
starts. Here we'll show, instead, how to unload and load again a previously registered plugin at runtime.
Each function decorated with the usual ``on_message`` decorator (or any other decorator that deals with Telegram
updates) will be modified in such a way that a special ``handlers`` attribute pointing to a list of tuples of
*(handler: Handler, group: int)* is attached to the function object itself.
- ``plugins/handlers.py``
.. code-block:: python
@Client.on_message(filters.text & filters.private)
async def echo(client, message):
await message.reply(message.text)
print(echo)
print(echo.handlers)
- Printing ``echo`` will show something like ``<function echo at 0x10e3b6598>``.
- Printing ``echo.handlers`` will reveal the handlers, that is, a list of tuples containing the actual handlers and
the groups they were registered on ``[(<MessageHandler object at 0x10e3abc50>, 0)]``.
Unloading
^^^^^^^^^
In order to unload a plugin, all you need to do is obtain a reference to it by importing the relevant module and call
:meth:`~pyrogram.Client.remove_handler` Client's method with your function's *handler* instance:
- ``main.py``
.. code-block:: python
from plugins.handlers import echo
handlers = echo.handlers
for h in handlers:
app.remove_handler(*h)
The star ``*`` operator is used to unpack the tuple into positional arguments so that *remove_handler* will receive
exactly what is needed. The same could have been achieved with:
.. code-block:: python
handlers = echo.handlers
handler, group = handlers[0]
app.remove_handler(handler, group)
Loading
^^^^^^^
Similarly to the unloading process, in order to load again a previously unloaded plugin you do the same, but this time
using :meth:`~pyrogram.Client.add_handler` instead. Example:
- ``main.py``
.. code-block:: python
from plugins.handlers import echo
...
handlers = echo.handlers
for h in handlers:
app.add_handler(*h)

View File

@ -1,88 +0,0 @@
Speedups
========
Pyrogram's speed can be boosted up by using TgCrypto and uvloop.
.. contents:: Contents
:backlinks: none
:depth: 1
:local:
-----
TgCrypto
--------
TgCrypto_ is a high-performance, easy-to-install cryptography library specifically written in C for Pyrogram as a Python
extension. It is a replacement for a slower Python-only alternative and implements the cryptographic algorithms Telegram
requires, namely: AES-256-IGE, AES-256-CTR and AES-256-CBC.
Installation
^^^^^^^^^^^^
.. code-block:: bash
$ pip3 install -U tgcrypto
Usage
^^^^^
Pyrogram will automatically make use of TgCrypto when detected, all you need to do is to install it.
uvloop
------
uvloop_ is a fast, drop-in replacement of the built-in asyncio event loop. uvloop is implemented in Cython and uses
libuv under the hood. It makes asyncio 2-4x faster.
Installation
^^^^^^^^^^^^
.. code-block:: bash
$ pip3 install -U uvloop
Usage
^^^^^
Call ``uvloop.install()`` before calling ``asyncio.run()`` or ``app.run()``.
.. code-block:: python
import asyncio
import uvloop
from pyrogram import Client
async def main():
app = Client("my_account")
async with app:
print(await app.get_me())
uvloop.install()
asyncio.run(main())
The ``uvloop.install()`` call also needs to be placed before creating a Client instance.
.. code-block:: python
import uvloop
from pyrogram import Client
uvloop.install()
app = Client("my_account")
@app.on_message()
async def hello(client, message):
print(await client.get_me())
app.run()
.. _TgCrypto: https://github.com/pyrogram/tgcrypto
.. _uvloop: https://github.com/MagicStack/uvloop

View File

@ -1,90 +0,0 @@
Storage Engines
===============
Every time you login to Telegram, some personal piece of data are created and held by both parties (the client, Pyrogram
and the server, Telegram). This session data is uniquely bound to your own account, indefinitely (until you logout or
decide to manually terminate it) and is used to authorize a client to execute API calls on behalf of your identity.
.. contents:: Contents
:backlinks: none
:depth: 1
:local:
-----
Persisting Sessions
-------------------
In order to make a client reconnect successfully between restarts, that is, without having to start a new
authorization process from scratch each time, Pyrogram needs to store the generated session data somewhere.
Different Storage Engines
-------------------------
Pyrogram offers two different types of storage engines: a **File Storage** and a **Memory Storage**.
These engines are well integrated in the framework and require a minimal effort to set up. Here's how they work:
File Storage
^^^^^^^^^^^^
This is the most common storage engine. It is implemented by using **SQLite**, which will store the session details.
The database will be saved to disk as a single portable file and is designed to efficiently store and retrieve
data whenever they are needed.
To use this type of engine, simply pass any name of your choice to the ``name`` parameter of the
:obj:`~pyrogram.Client` constructor, as usual:
.. code-block:: python
from pyrogram import Client
async with Client("my_account") as app:
print(await app.get_me())
Once you successfully log in (either with a user or a bot identity), a session file will be created and saved to disk as
``my_account.session``. Any subsequent client restart will make Pyrogram search for a file named that way and the
session database will be automatically loaded.
Memory Storage
^^^^^^^^^^^^^^
In case you don't want to have any session file saved to disk, you can use an in-memory storage by passing True to the
``in_memory`` parameter of the :obj:`~pyrogram.Client` constructor:
.. code-block:: python
from pyrogram import Client
async with Client("my_account", in_memory=True) as app:
print(await app.get_me())
This storage engine is still backed by SQLite, but the database exists purely in memory. This means that, once you stop
a client, the entire database is discarded and the session details used for logging in again will be lost forever.
Session Strings
---------------
In case you want to use an in-memory storage, but also want to keep access to the session you created, call
:meth:`~pyrogram.Client.export_session_string` anytime before stopping the client...
.. code-block:: python
from pyrogram import Client
async with Client("my_account", in_memory=True) as app:
print(await app.export_session_string())
...and save the resulting string. You can use this string by passing it as Client argument the next time you want to
login using the same session; the storage used will still be in-memory:
.. code-block:: python
from pyrogram import Client
session_string = "...ZnUIFD8jsjXTb8g_vpxx48k1zkov9sapD-tzjz-S4WZv70M..."
async with Client("my_account", session_string=session_string) as app:
print(await app.get_me())
Session strings are useful when you want to run authorized Pyrogram clients on platforms where their ephemeral
filesystems makes it harder for a file-based storage engine to properly work as intended.

View File

@ -1,88 +0,0 @@
Synchronous Usage
=================
Pyrogram is an asynchronous framework and as such is subject to the asynchronous rules. It can, however, run in
synchronous mode (also known as non-asynchronous or sync/non-async for short). This mode exists mainly as a convenience
way for invoking methods without the need of ``async``/``await`` keywords and the extra boilerplate, but **it's not the
intended way to use the framework**.
You can use Pyrogram in this synchronous mode when you want to write something short and contained without the
async boilerplate or in case you want to combine Pyrogram with other libraries that are not async.
.. warning::
You have to be very careful when using the framework in its synchronous, non-native form, especially when combined
with other non-async libraries because thread blocking operations that clog the asynchronous event loop underneath
will make the program run erratically.
.. contents:: Contents
:backlinks: none
:depth: 1
:local:
-----
Synchronous Invocations
-----------------------
The following is a standard example of running asynchronous functions with Python's asyncio.
Pyrogram is being used inside the main function with its asynchronous interface.
.. code-block:: python
import asyncio
from pyrogram import Client
async def main():
app = Client("my_account")
async with app:
await app.send_message("me", "Hi!")
asyncio.run(main())
To run Pyrogram synchronously, use the non-async context manager as shown in the following example.
As you can see, the non-async example becomes less cluttered.
.. code-block:: python
from pyrogram import Client
app = Client("my_account")
with app:
app.send_message("me", "Hi!")
Synchronous handlers
--------------------
You can also have synchronous handlers; you only need to define the callback function without using ``async def`` and
invoke API methods by not placing ``await`` in front of them. Mixing ``def`` and ``async def`` handlers together is also
possible.
.. code-block:: python
@app.on_message()
async def handler1(client, message):
await message.forward("me")
@app.on_edited_message()
def handler2(client, message):
message.forward("me")
uvloop usage
------------
When using Pyrogram in its synchronous mode combined with uvloop, you need to call ``uvloop.install()`` before importing
Pyrogram.
.. code-block:: python
import uvloop
uvloop.install()
from pyrogram import Client
...

View File

@ -1,41 +0,0 @@
Test Servers
============
If you wish to test your application in a separate environment, Pyrogram is able to authorize your account into
Telegram's test servers without hassle. All you need to do is start a new session (e.g.: "my_account_test") using
``test_mode=True``:
.. code-block:: python
from pyrogram import Client
async with Client("my_account_test", test_mode=True) as app:
print(await app.get_me())
.. note::
If this is the first time you login into test servers, you will be asked to register your account first.
Accounts registered on test servers reside in a different, parallel instance of a Telegram server.
.. contents:: Contents
:backlinks: none
:depth: 1
:local:
-----
Test Mode in Official Apps
--------------------------
You can also login yourself into test servers using official desktop apps, such as Telegram Web and Telegram Desktop:
- **Telegram Web**: Login here: https://web.telegram.org/?test=1
- **Telegram Desktop**: Hold ``Alt+Shift`` and right click on "Add account", then choose "Test server".
Test Numbers
------------
Beside normal numbers, the test environment allows you to login with reserved test numbers.
Valid phone numbers follow the pattern ``99966XYYYY``, where ``X`` is the DC number (1 to 3) and ``YYYY`` are random
numbers. Users with such numbers always get ``XXXXX`` or ``XXXXXX`` as the confirmation code (the DC number, repeated
five or six times).

View File

@ -1,243 +0,0 @@
Text Formatting
===============
.. role:: strike
:class: strike
.. role:: underline
:class: underline
.. role:: bold-underline
:class: bold-underline
.. role:: strike-italic
:class: strike-italic
Pyrogram uses a custom Markdown dialect for text formatting which adds some unique features that make writing styled
texts easier in both Markdown and HTML. You can send sophisticated text messages and media captions using a
variety of decorations that can also be nested in order to combine multiple styles together.
.. contents:: Contents
:backlinks: none
:depth: 1
:local:
-----
Basic Styles
------------
When formatting your messages, you can choose between Markdown-style, HTML-style or both (default). The following is a
list of the basic styles currently supported by Pyrogram.
- **bold**
- *italic*
- :strike:`strike`
- :underline:`underline`
- spoiler
- `text URL <https://pyrogram.org>`_
- `user text mention <tg://user?id=123456789>`_
- ``inline fixed-width code``
- .. code-block:: text
pre-formatted
fixed-width
code block
Markdown Style
--------------
To strictly use this mode, pass :obj:`~pyrogram.enums.ParseMode.MARKDOWN` to the *parse_mode* parameter when using
:meth:`~pyrogram.Client.send_message`. Use the following syntax in your message:
.. code-block:: text
**bold**
__italic__
--underline--
~~strike~~
||spoiler||
[text URL](https://pyrogram.org/)
[text user mention](tg://user?id=123456789)
`inline fixed-width code`
```
pre-formatted
fixed-width
code block
```
**Example**:
.. code-block:: python
from pyrogram import enums
await app.send_message(
"me",
(
"**bold**, "
"__italic__, "
"--underline--, "
"~~strike~~, "
"||spoiler||, "
"[URL](https://pyrogram.org), "
"`code`, "
"```"
"for i in range(10):\n"
" print(i)"
"```"
),
parse_mode=enums.ParseMode.MARKDOWN
)
HTML Style
----------
To strictly use this mode, pass :obj:`~pyrogram.enums.HTML` to the *parse_mode* parameter when using
:meth:`~pyrogram.Client.send_message`. The following tags are currently supported:
.. code-block:: text
<b>bold</b>, <strong>bold</strong>
<i>italic</i>, <em>italic</em>
<u>underline</u>
<s>strike</s>, <del>strike</del>, <strike>strike</strike>
<spoiler>spoiler</spoiler>
<a href="https://pyrogram.org/">text URL</a>
<a href="tg://user?id=123456789">inline mention</a>
<code>inline fixed-width code</code>
<emoji id="12345678901234567890">🔥</emoji>
<pre>
pre-formatted
fixed-width
code block
</pre>
**Example**:
.. code-block:: python
from pyrogram import enums
await app.send_message(
"me",
(
"<b>bold</b>, "
"<i>italic</i>, "
"<u>underline</u>, "
"<s>strike</s>, "
"<spoiler>spoiler</spoiler>, "
"<a href=\"https://pyrogram.org/\">URL</a>, "
"<code>code</code>\n\n"
"<pre>"
"for i in range(10):\n"
" print(i)"
"</pre>"
),
parse_mode=enums.ParseMode.HTML
)
.. note::
All ``<``, ``>`` and ``&`` symbols that are not a part of a tag or an HTML entity must be replaced with the
corresponding HTML entities (``<`` with ``&lt;``, ``>`` with ``&gt;`` and ``&`` with ``&amp;``). You can use this
snippet to quickly escape those characters:
.. code-block:: python
import html
text = "<my text>"
text = html.escape(text)
print(text)
.. code-block:: text
&lt;my text&gt;
Different Styles
----------------
By default, when ignoring the *parse_mode* parameter, both Markdown and HTML styles are enabled together.
This means you can combine together both syntaxes in the same text:
.. code-block:: python
await app.send_message("me", "**bold**, <i>italic</i>")
Result:
**bold**, *italic*
If you don't like this behaviour you can always choose to only enable either Markdown or HTML in strict mode by passing
:obj:`~pyrogram.enums.MARKDOWN` or :obj:`~pyrogram.enums.HTML` as argument to the *parse_mode* parameter.
.. code-block:: python
from pyrogram import enums
await app.send_message("me", "**bold**, <i>italic</i>", parse_mode=enums.ParseMode.MARKDOWN)
await app.send_message("me", "**bold**, <i>italic</i>", parse_mode=enums.ParseMode.HTML)
Result:
**bold**, <i>italic</i>
\*\*bold**, *italic*
In case you want to completely turn off the style parser, simply pass :obj:`~pyrogram.enums.DISABLED` to *parse_mode*.
The text will be sent as-is.
.. code-block:: python
from pyrogram import enums
await app.send_message("me", "**bold**, <i>italic</i>", parse_mode=enums.ParseMode.DISABLED)
Result:
\*\*bold**, <i>italic</i>
Nested and Overlapping Entities
-------------------------------
You can also style texts with more than one decoration at once by nesting entities together. For example, you can send
a text message with both :bold-underline:`bold and underline` styles, or a text that has both :strike-italic:`italic and
strike` styles, and you can still combine both Markdown and HTML together.
Here there are some example texts you can try sending:
**Markdown**:
- ``**bold, --underline--**``
- ``**bold __italic --underline ~~strike~~--__**``
- ``**bold __and** italic__``
**HTML**:
- ``<b>bold, <u>underline</u></b>``
- ``<b>bold <i>italic <u>underline <s>strike</s></u></i></b>``
- ``<b>bold <i>and</b> italic</i>``
**Combined**:
- ``--you can combine <i>HTML</i> with **Markdown**--``
- ``**and also <i>overlap** --entities</i> this way--``

View File

@ -1,114 +0,0 @@
Using Filters
=============
So far we've seen :doc:`how to register a callback function <../start/updates>` that executes every time an update comes
from the server, but there's much more than that to come.
Here we'll discuss about :obj:`~pyrogram.filters`. Filters enable a fine-grain control over what kind of
updates are allowed or not to be passed in your callback functions, based on their inner details.
.. contents:: Contents
:backlinks: none
:depth: 1
:local:
-----
Single Filters
--------------
Let's start right away with a simple example:
- This example will show you how to **only** handle messages containing a :class:`~pyrogram.types.Sticker` object and
ignore any other message. Filters are passed as the first argument of the decorator:
.. code-block:: python
from pyrogram import filters
@app.on_message(filters.sticker)
async def my_handler(client, message):
print(message)
- or, without decorators. Here filters are passed as the second argument of the handler constructor; the first is the
callback function itself:
.. code-block:: python
from pyrogram import filters
from pyrogram.handlers import MessageHandler
async def my_handler(client, message):
print(message)
app.add_handler(MessageHandler(my_handler, filters.sticker))
Combining Filters
-----------------
Filters can be used in a more advanced way by inverting and combining more filters together using bitwise
operators ``~``, ``&`` and ``|``:
- Use ``~`` to invert a filter (behaves like the ``not`` operator).
- Use ``&`` and ``|`` to merge two filters (behave like ``and``, ``or`` operators respectively).
Here are some examples:
- Message is a **text** message **or** a **photo**.
.. code-block:: python
@app.on_message(filters.text | filters.photo)
async def my_handler(client, message):
print(message)
- Message is a **sticker** **and** is coming from a **channel or** a **private** chat.
.. code-block:: python
@app.on_message(filters.sticker & (filters.channel | filters.private))
async def my_handler(client, message):
print(message)
Advanced Filters
----------------
Some filters, like :meth:`~pyrogram.filters.command` or :meth:`~pyrogram.filters.regex`
can also accept arguments:
- Message is either a */start* or */help* **command**.
.. code-block:: python
@app.on_message(filters.command(["start", "help"]))
async def my_handler(client, message):
print(message)
- Message is a **text** message or a media **caption** matching the given **regex** pattern.
.. code-block:: python
@app.on_message(filters.regex("pyrogram"))
async def my_handler(client, message):
print(message)
More handlers using different filters can also live together.
.. code-block:: python
@app.on_message(filters.command("start"))
async def start_command(client, message):
print("This is the /start command")
@app.on_message(filters.command("help"))
async def help_command(client, message):
print("This is the /help command")
@app.on_message(filters.chat("PyrogramChat"))
async def from_pyrogramchat(client, message):
print("New message in @PyrogramChat")

View File

@ -1,19 +0,0 @@
Voice Calls
===========
Both private voice calls and group voice calls are currently supported by third-party, external libraries that integrate
with Pyrogram.
Libraries
---------
There are currently two main libraries (with very similar names) you can use:
1. https://github.com/pytgcalls/pytgcalls
2. https://github.com/MarshalX/tgcalls
Older implementations
---------------------
An older implementation of Telegram voice calls can be found at https://github.com/bakatrouble/pylibtgvoip (currently
outdated due to the deprecation of the Telegram VoIP library used underneath).