mirror of
https://github.com/TeamPGM/pyrogram.git
synced 2024-11-23 07:19:36 +00:00
Update docs
This commit is contained in:
parent
0e68bf35b7
commit
8077eb4130
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,8 +1,10 @@
|
||||
# Development
|
||||
docs
|
||||
*.session
|
||||
config.ini
|
||||
main.py
|
||||
unknown_errors.txt
|
||||
.DS_Store
|
||||
|
||||
# Pyrogram generated code
|
||||
pyrogram/errors/exceptions/
|
||||
|
28
Makefile
28
Makefile
@ -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
|
@ -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 constructor_docs:
|
||||
constructor_docs = constructor_docs["desc"]
|
||||
else:
|
||||
constructor_docs = "Telegram API type."
|
||||
|
||||
if docstring_args:
|
||||
docstring += " Parameters:\n " + "\n ".join(docstring_args)
|
||||
docstring += constructor_docs + "\n"
|
||||
docstring += f"\n Constructor of :obj:`~pyrogram.raw.base.{c.qualtype}`."
|
||||
else:
|
||||
docstring += " **No parameters required.**"
|
||||
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 "
|
||||
|
||||
|
4
compiler/docs/template/bound-methods.rst
vendored
4
compiler/docs/template/bound-methods.rst
vendored
@ -19,10 +19,6 @@ some of the required arguments.
|
||||
|
||||
app.run()
|
||||
|
||||
.. contents:: Contents
|
||||
:backlinks: none
|
||||
:local:
|
||||
|
||||
-----
|
||||
|
||||
.. currentmodule:: pyrogram.types
|
||||
|
4
compiler/docs/template/methods.rst
vendored
4
compiler/docs/template/methods.rst
vendored
@ -14,10 +14,6 @@ the main package directly.
|
||||
with app:
|
||||
app.send_message("me", "hi")
|
||||
|
||||
.. contents:: Contents
|
||||
:backlinks: none
|
||||
:local:
|
||||
|
||||
-----
|
||||
|
||||
.. currentmodule:: pyrogram.Client
|
||||
|
2
compiler/docs/template/toctree.txt
vendored
2
compiler/docs/template/toctree.txt
vendored
@ -4,4 +4,6 @@
|
||||
.. module:: {module}
|
||||
|
||||
.. toctree::
|
||||
:titlesonly:
|
||||
|
||||
{entities}
|
4
compiler/docs/template/types.rst
vendored
4
compiler/docs/template/types.rst
vendored
@ -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
|
||||
|
@ -1,4 +0,0 @@
|
||||
sphinx
|
||||
sphinx_rtd_theme==1.0.0
|
||||
sphinx_copybutton
|
||||
sphinx-autobuild
|
@ -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()
|
@ -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()
|
@ -1,8 +0,0 @@
|
||||
ChatAction
|
||||
==========
|
||||
|
||||
.. autoclass:: pyrogram.enums.ChatAction()
|
||||
:members:
|
||||
|
||||
.. raw:: html
|
||||
:file: ./cleanup.html
|
@ -1,8 +0,0 @@
|
||||
ChatEventAction
|
||||
===============
|
||||
|
||||
.. autoclass:: pyrogram.enums.ChatEventAction()
|
||||
:members:
|
||||
|
||||
.. raw:: html
|
||||
:file: ./cleanup.html
|
@ -1,8 +0,0 @@
|
||||
ChatMemberStatus
|
||||
================
|
||||
|
||||
.. autoclass:: pyrogram.enums.ChatMemberStatus()
|
||||
:members:
|
||||
|
||||
.. raw:: html
|
||||
:file: ./cleanup.html
|
@ -1,8 +0,0 @@
|
||||
ChatMembersFilter
|
||||
=================
|
||||
|
||||
.. autoclass:: pyrogram.enums.ChatMembersFilter()
|
||||
:members:
|
||||
|
||||
.. raw:: html
|
||||
:file: ./cleanup.html
|
@ -1,8 +0,0 @@
|
||||
ChatType
|
||||
========
|
||||
|
||||
.. autoclass:: pyrogram.enums.ChatType()
|
||||
:members:
|
||||
|
||||
.. raw:: html
|
||||
:file: ./cleanup.html
|
@ -1,8 +0,0 @@
|
||||
MessageEntityType
|
||||
=================
|
||||
|
||||
.. autoclass:: pyrogram.enums.MessageEntityType()
|
||||
:members:
|
||||
|
||||
.. raw:: html
|
||||
:file: ./cleanup.html
|
@ -1,8 +0,0 @@
|
||||
MessageMediaType
|
||||
================
|
||||
|
||||
.. autoclass:: pyrogram.enums.MessageMediaType()
|
||||
:members:
|
||||
|
||||
.. raw:: html
|
||||
:file: ./cleanup.html
|
@ -1,8 +0,0 @@
|
||||
MessageServiceType
|
||||
==================
|
||||
|
||||
.. autoclass:: pyrogram.enums.MessageServiceType()
|
||||
:members:
|
||||
|
||||
.. raw:: html
|
||||
:file: ./cleanup.html
|
@ -1,8 +0,0 @@
|
||||
MessagesFilter
|
||||
==============
|
||||
|
||||
.. autoclass:: pyrogram.enums.MessagesFilter()
|
||||
:members:
|
||||
|
||||
.. raw:: html
|
||||
:file: ./cleanup.html
|
@ -1,8 +0,0 @@
|
||||
NextCodeType
|
||||
============
|
||||
|
||||
.. autoclass:: pyrogram.enums.NextCodeType()
|
||||
:members:
|
||||
|
||||
.. raw:: html
|
||||
:file: ./cleanup.html
|
@ -1,8 +0,0 @@
|
||||
ParseMode
|
||||
=========
|
||||
|
||||
.. autoclass:: pyrogram.enums.ParseMode()
|
||||
:members:
|
||||
|
||||
.. raw:: html
|
||||
:file: ./cleanup.html
|
@ -1,8 +0,0 @@
|
||||
PollType
|
||||
========
|
||||
|
||||
.. autoclass:: pyrogram.enums.PollType()
|
||||
:members:
|
||||
|
||||
.. raw:: html
|
||||
:file: ./cleanup.html
|
@ -1,8 +0,0 @@
|
||||
SentCodeType
|
||||
============
|
||||
|
||||
.. autoclass:: pyrogram.enums.SentCodeType()
|
||||
:members:
|
||||
|
||||
.. raw:: html
|
||||
:file: ./cleanup.html
|
@ -1,8 +0,0 @@
|
||||
UserStatus
|
||||
==========
|
||||
|
||||
.. autoclass:: pyrogram.enums.UserStatus()
|
||||
:members:
|
||||
|
||||
.. raw:: html
|
||||
:file: ./cleanup.html
|
@ -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>
|
@ -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
|
@ -1,7 +0,0 @@
|
||||
400 - BadRequest
|
||||
----------------
|
||||
|
||||
.. csv-table::
|
||||
:file: ../../../../compiler/errors/source/400_BAD_REQUEST.tsv
|
||||
:delim: tab
|
||||
:header-rows: 1
|
@ -1,7 +0,0 @@
|
||||
420 - Flood
|
||||
-----------
|
||||
|
||||
.. csv-table::
|
||||
:file: ../../../../compiler/errors/source/420_FLOOD.tsv
|
||||
:delim: tab
|
||||
:header-rows: 1
|
@ -1,7 +0,0 @@
|
||||
403 - Forbidden
|
||||
---------------
|
||||
|
||||
.. csv-table::
|
||||
:file: ../../../../compiler/errors/source/403_FORBIDDEN.tsv
|
||||
:delim: tab
|
||||
:header-rows: 1
|
@ -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
|
@ -1,7 +0,0 @@
|
||||
500 - InternalServerError
|
||||
-------------------------
|
||||
|
||||
.. csv-table::
|
||||
:file: ../../../../compiler/errors/source/500_INTERNAL_SERVER_ERROR.tsv
|
||||
:delim: tab
|
||||
:header-rows: 1
|
@ -1,7 +0,0 @@
|
||||
406 - NotAcceptable
|
||||
-------------------
|
||||
|
||||
.. csv-table::
|
||||
:file: ../../../../compiler/errors/source/406_NOT_ACCEPTABLE.tsv
|
||||
:delim: tab
|
||||
:header-rows: 1
|
@ -1,7 +0,0 @@
|
||||
303 - SeeOther
|
||||
--------------
|
||||
|
||||
.. csv-table::
|
||||
:file: ../../../../compiler/errors/source/303_SEE_OTHER.tsv
|
||||
:delim: tab
|
||||
:header-rows: 1
|
@ -1,7 +0,0 @@
|
||||
401 - Unauthorized
|
||||
------------------
|
||||
|
||||
.. csv-table::
|
||||
:file: ../../../../compiler/errors/source/401_UNAUTHORIZED.tsv
|
||||
:delim: tab
|
||||
:header-rows: 1
|
@ -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:
|
@ -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()
|
@ -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}
|
||||
"""
|
||||
}
|
@ -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)
|
@ -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.
|
@ -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>`.
|
@ -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).
|
@ -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
|
@ -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.
|
@ -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.
|
@ -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.
|
@ -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")
|
@ -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.
|
@ -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
|
||||
|
@ -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.
|
@ -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.
|
@ -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.
|
||||
|
@ -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.
|
@ -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
|
@ -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.
|
@ -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.
|
@ -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
|
@ -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
|
@ -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
|
@ -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()
|
@ -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
|
||||
...
|
@ -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())
|
@ -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()
|
@ -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()
|
@ -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())
|
@ -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())
|
@ -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())
|
@ -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())
|
@ -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
|
@ -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()
|
@ -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()
|
@ -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())
|
@ -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()
|
@ -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())
|
@ -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)
|
@ -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()
|
@ -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>
|
@ -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
|
@ -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",
|
||||
)
|
@ -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
|
||||
...
|
@ -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>
|
@ -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
|
@ -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
|
@ -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()
|
@ -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()
|
@ -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.
|
@ -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)
|
@ -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
|
@ -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.
|
@ -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
|
||||
|
||||
...
|
@ -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).
|
@ -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 ``<``, ``>`` with ``>`` and ``&`` with ``&``). 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
|
||||
|
||||
<my text>
|
||||
|
||||
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--``
|
@ -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")
|
@ -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).
|
Loading…
Reference in New Issue
Block a user