mirror of
https://github.com/pyrogram/pyrogram
synced 2025-08-22 18:07:21 +00:00
Update docs
This commit is contained in:
parent
0e68bf35b7
commit
8077eb4130
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,8 +1,10 @@
|
|||||||
# Development
|
# Development
|
||||||
|
docs
|
||||||
*.session
|
*.session
|
||||||
config.ini
|
config.ini
|
||||||
main.py
|
main.py
|
||||||
unknown_errors.txt
|
unknown_errors.txt
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
# Pyrogram generated code
|
# Pyrogram generated code
|
||||||
pyrogram/errors/exceptions/
|
pyrogram/errors/exceptions/
|
||||||
|
28
Makefile
28
Makefile
@ -1,53 +1,33 @@
|
|||||||
VENV := venv
|
VENV := venv
|
||||||
PYTHON := $(VENV)/bin/python
|
PYTHON := $(VENV)/bin/python
|
||||||
|
HOST = $(shell ifconfig | grep "inet " | tail -1 | cut -d\ -f2)
|
||||||
|
|
||||||
RM := rm -rf
|
RM := rm -rf
|
||||||
|
|
||||||
.PHONY: venv build docs
|
.PHONY: venv clean-build clean-api clean api build
|
||||||
|
|
||||||
venv:
|
venv:
|
||||||
$(RM) $(VENV)
|
$(RM) $(VENV)
|
||||||
python3 -m venv $(VENV)
|
python3 -m venv $(VENV)
|
||||||
$(PYTHON) -m pip install -U pip wheel setuptools
|
$(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)"
|
@echo "Created venv with $$($(PYTHON) --version)"
|
||||||
|
|
||||||
clean-build:
|
clean-build:
|
||||||
$(RM) *.egg-info build dist
|
$(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:
|
clean-api:
|
||||||
$(RM) pyrogram/errors/exceptions pyrogram/raw/all.py pyrogram/raw/base pyrogram/raw/functions pyrogram/raw/types
|
$(RM) pyrogram/errors/exceptions pyrogram/raw/all.py pyrogram/raw/base pyrogram/raw/functions pyrogram/raw/types
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
make clean-build
|
make clean-build
|
||||||
make clean-docs
|
|
||||||
make clean-api
|
make clean-api
|
||||||
|
|
||||||
api:
|
api:
|
||||||
cd compiler/api && ../../$(PYTHON) compiler.py
|
cd compiler/api && ../../$(PYTHON) compiler.py
|
||||||
cd compiler/errors && ../../$(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:
|
build:
|
||||||
make clean-build
|
make clean
|
||||||
make clean-api
|
|
||||||
$(PYTHON) setup.py sdist
|
$(PYTHON) setup.py sdist
|
||||||
$(PYTHON) setup.py bdist_wheel
|
$(PYTHON) setup.py bdist_wheel
|
@ -16,6 +16,7 @@
|
|||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
@ -59,6 +60,16 @@ namespaces_to_types = {}
|
|||||||
namespaces_to_constructors = {}
|
namespaces_to_constructors = {}
|
||||||
namespaces_to_functions = {}
|
namespaces_to_functions = {}
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open("docs.json") as f:
|
||||||
|
docs = json.load(f)
|
||||||
|
except FileNotFoundError:
|
||||||
|
docs = {
|
||||||
|
"type": {},
|
||||||
|
"constructor": {},
|
||||||
|
"method": {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class Combinator(NamedTuple):
|
class Combinator(NamedTuple):
|
||||||
section: str
|
section: str
|
||||||
@ -149,7 +160,7 @@ def remove_whitespaces(source: str) -> str:
|
|||||||
return "\n".join(lines)
|
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 in CORE_TYPES:
|
||||||
if t == "long":
|
if t == "long":
|
||||||
return "``int`` ``64-bit``"
|
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":
|
elif t == "TLObject" or t == "X":
|
||||||
return "Any object from :obj:`~pyrogram.raw.types`"
|
return "Any object from :obj:`~pyrogram.raw.types`"
|
||||||
elif t == "!X":
|
elif t == "!X":
|
||||||
return "Any method from :obj:`~pyrogram.raw.functions`"
|
return "Any function from :obj:`~pyrogram.raw.functions`"
|
||||||
elif t.lower().startswith("vector"):
|
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:
|
else:
|
||||||
return f":obj:`{t} <pyrogram.raw.base.{t}>`"
|
return f":obj:`{t} <pyrogram.raw.base.{t}>`"
|
||||||
|
|
||||||
@ -183,10 +194,7 @@ def get_references(t: str, kind: str):
|
|||||||
raise ValueError("Invalid kind")
|
raise ValueError("Invalid kind")
|
||||||
|
|
||||||
if t:
|
if t:
|
||||||
return "\n ".join(
|
return "\n ".join(t), len(t)
|
||||||
f"- :obj:`{i} <pyrogram.raw.functions.{i}>`"
|
|
||||||
for i in t
|
|
||||||
), len(t)
|
|
||||||
|
|
||||||
return None, 0
|
return None, 0
|
||||||
|
|
||||||
@ -315,17 +323,33 @@ def start(format: bool = False):
|
|||||||
|
|
||||||
constructors = sorted(types_to_constructors[qualtype])
|
constructors = sorted(types_to_constructors[qualtype])
|
||||||
constr_count = len(constructors)
|
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"
|
type_docs = docs["type"].get(qualtype, None)
|
||||||
docstring += f" Constructors:\n .. hlist::\n :columns: 2\n\n {items}"
|
|
||||||
|
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")
|
references, ref_count = get_references(qualtype, "types")
|
||||||
|
|
||||||
if references:
|
if references:
|
||||||
docstring += f"\n\n See Also:\n This object can be returned by " \
|
docstring += f"\n\n Functions:\n This object can be returned by " \
|
||||||
f"{ref_count} method{'s' if ref_count > 1 else ''}:" \
|
f"{ref_count} function{'s' if ref_count > 1 else ''}.\n\n" \
|
||||||
f"\n\n .. hlist::\n :columns: 2\n\n " + references
|
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:
|
with open(dir_path / f"{snake(module)}.py", "w") as f:
|
||||||
f.write(
|
f.write(
|
||||||
@ -359,41 +383,67 @@ def start(format: bool = False):
|
|||||||
docstring = ""
|
docstring = ""
|
||||||
docstring_args = []
|
docstring_args = []
|
||||||
|
|
||||||
|
if c.section == "functions":
|
||||||
|
combinator_docs = docs["method"]
|
||||||
|
else:
|
||||||
|
combinator_docs = docs["constructor"]
|
||||||
|
|
||||||
for i, arg in enumerate(sorted_args):
|
for i, arg in enumerate(sorted_args):
|
||||||
arg_name, arg_type = arg
|
arg_name, arg_type = arg
|
||||||
is_optional = FLAGS_RE.match(arg_type)
|
is_optional = FLAGS_RE.match(arg_type)
|
||||||
flag_number = is_optional.group(1) if is_optional else -1
|
flag_number = is_optional.group(1) if is_optional else -1
|
||||||
arg_type = arg_type.split("?")[-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(
|
docstring_args.append(
|
||||||
"{}{}: {}".format(
|
"{} ({}{}):\n {}\n".format(
|
||||||
arg_name,
|
arg_name,
|
||||||
" (optional)".format(flag_number) if is_optional else "",
|
get_docstring_arg_type(arg_type),
|
||||||
get_docstring_arg_type(arg_type, is_pyrogram_type=c.namespace == "pyrogram")
|
", *optional*".format(flag_number) if is_optional else "",
|
||||||
|
arg_docs
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if c.section == "types":
|
if c.section == "types":
|
||||||
docstring += f"This object is a constructor of the base type :obj:`~pyrogram.raw.base.{c.qualtype}`.\n\n"
|
constructor_docs = docs["constructor"].get(c.qualname, None)
|
||||||
else:
|
|
||||||
docstring += f"Telegram API method.\n\n"
|
|
||||||
|
|
||||||
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 += constructor_docs + "\n"
|
||||||
docstring += " Parameters:\n " + "\n ".join(docstring_args)
|
docstring += f"\n Constructor of :obj:`~pyrogram.raw.base.{c.qualtype}`."
|
||||||
else:
|
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":
|
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:
|
else:
|
||||||
references, count = get_references(c.qualname, "constructors")
|
references, count = get_references(c.qualname, "constructors")
|
||||||
|
|
||||||
if references:
|
if references:
|
||||||
docstring += f"\n\n See Also:\n This object can be returned by " \
|
docstring += f"\n Functions:\n This object can be returned by " \
|
||||||
f"{count} method{'s' if count > 1 else ''}:" \
|
f"{count} function{'s' if count > 1 else ''}.\n\n" \
|
||||||
f"\n\n .. hlist::\n :columns: 2\n\n " + references
|
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 "
|
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()
|
app.run()
|
||||||
|
|
||||||
.. contents:: Contents
|
|
||||||
:backlinks: none
|
|
||||||
:local:
|
|
||||||
|
|
||||||
-----
|
-----
|
||||||
|
|
||||||
.. currentmodule:: pyrogram.types
|
.. 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:
|
with app:
|
||||||
app.send_message("me", "hi")
|
app.send_message("me", "hi")
|
||||||
|
|
||||||
.. contents:: Contents
|
|
||||||
:backlinks: none
|
|
||||||
:local:
|
|
||||||
|
|
||||||
-----
|
-----
|
||||||
|
|
||||||
.. currentmodule:: pyrogram.Client
|
.. currentmodule:: pyrogram.Client
|
||||||
|
2
compiler/docs/template/toctree.txt
vendored
2
compiler/docs/template/toctree.txt
vendored
@ -4,4 +4,6 @@
|
|||||||
.. module:: {module}
|
.. module:: {module}
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
|
:titlesonly:
|
||||||
|
|
||||||
{entities}
|
{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: ...``.
|
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
|
.. 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…
x
Reference in New Issue
Block a user