Various improvements

This commit is contained in:
Dan 2022-01-07 10:18:51 +01:00
parent 80d0966691
commit 8c8288412f
135 changed files with 842 additions and 1422 deletions

3
.github/FUNDING.yml vendored
View File

@ -1,2 +1,3 @@
github: delivrance github: delivrance
custom: https://docs.pyrogram.org/support liberapay: delivrance
open_collective: pyrogram

View File

@ -1,6 +1,6 @@
--- ---
name: Bug Report name: Bug Report
about: Create a bug report affecting the library or the documentation about: Create a bug report affecting the framework or the documentation
--- ---
<!-- WARNING: Ignoring or altering this template could lead to the issue being closed as invalid --> <!-- WARNING: Ignoring or altering this template could lead to the issue being closed as invalid -->

View File

@ -2,7 +2,7 @@ blank_issues_enabled: false
contact_links: contact_links:
- name: Ask Pyrogram related questions - name: Ask Pyrogram related questions
url: https://stackoverflow.com/questions/tagged/pyrogram url: https://stackoverflow.com/questions/tagged/pyrogram
about: This place is for issues about Pyrogram. If you'd like to ask a question, please do so at StackOverflow. about: This place is only for reporting issues about Pyrogram. You can ask questions at StackOverflow.
- name: Join the Telegram community - name: Join the Telegram channel
url: https://t.me/pyrogram url: https://t.me/pyrogram
about: Join the official channel to stay tuned for news and updates. about: Join the official channel and stay tuned for news, updates and announcements.

View File

@ -7,7 +7,7 @@ labels: "enhancement"
<!-- WARNING: Ignoring or altering this template could lead to the issue being closed as invalid --> <!-- WARNING: Ignoring or altering this template could lead to the issue being closed as invalid -->
## Checklist ## Checklist
- [ ] I believe the idea is awesome and would benefit the library. - [ ] I believe the idea is awesome and would benefit the framework.
- [ ] I have searched in the issue tracker for similar requests, including closed ones. - [ ] I have searched in the issue tracker for similar requests, including closed ones.
## Description ## Description

View File

@ -1,6 +1,6 @@
<p align="center"> <p align="center">
<a href="https://github.com/pyrogram/pyrogram"> <a href="https://github.com/pyrogram/pyrogram">
<img src="https://i.imgur.com/BOgY9ai.png" alt="Pyrogram"> <img src="https://docs.pyrogram.org/_static/pyrogram.png" alt="Pyrogram" width="128">
</a> </a>
<br> <br>
<b>Telegram MTProto API Framework for Python</b> <b>Telegram MTProto API Framework for Python</b>
@ -9,17 +9,19 @@
Documentation Documentation
</a> </a>
<a href="https://github.com/pyrogram/pyrogram/releases"> <a href="https://docs.pyrogram.org/releases">
Releases Releases
</a> </a>
<a href="https://t.me/Pyrogram"> <a href="https://t.me/pyrogram">
Community News
</a> </a>
</p> </p>
## Pyrogram ## Pyrogram
> Elegant, modern and asynchronous Telegram MTProto API framework in Python for users and bots
``` python ``` python
from pyrogram import Client, filters from pyrogram import Client, filters
@ -28,34 +30,33 @@ app = Client("my_account")
@app.on_message(filters.private) @app.on_message(filters.private)
async def hello(client, message): async def hello(client, message):
await message.reply_text(f"Hello {message.from_user.mention}") await message.reply("Hello from Pyrogram!")
app.run() app.run()
``` ```
**Pyrogram** is a modern, elegant and easy-to-use [Telegram](https://telegram.org/) client library framework written **Pyrogram** is a modern, elegant and asynchronous [MTProto API](https://docs.pyrogram.org/topics/mtproto-vs-botapi)
from the ground up in Python and C. It enables you to easily create custom Telegram client applications for both user framework. It enables you to easily interact with the main Telegram API through a user account (custom client) or a bot
and bot identities (bot API alternative) via the [MTProto API](https://docs.pyrogram.org/topics/mtproto-vs-botapi). identity (bot API alternative) using Python.
### Features ### Support
- **Easy**: You can install Pyrogram with pip and start building your applications right away. If you'd like to support Pyrogram, you can consider:
- **Elegant**: Low-level details are abstracted and re-presented in a much nicer and easier way.
- **Fast**: Crypto parts are boosted up by [TgCrypto](https://github.com/pyrogram/tgcrypto), a high-performance library - [Become a GitHub sponsor](https://github.com/sponsors/delivrance).
written in pure C. - [Become a LiberaPay patron](https://liberapay.com/delivrance).
- **Asynchronous**: Allows both synchronous and asynchronous models to fit all usage needs. - [Become an OpenCollective backer](https://opencollective.com/pyrogram>).
- **Documented**: API methods, types and public interfaces are all [well documented](https://docs.pyrogram.org).
### Key Features
- **Ready**: Install Pyrogram with pip and start building your applications right away.
- **Easy**: Makes the Telegram API simple and intuitive, while still allowing advanced usages.
- **Elegant**: Low-level details are abstracted and re-presented in a more convenient way.
- **Fast**: Boosted up by [TgCrypto](https://github.com/pyrogram/tgcrypto), a high-performance crypto library written in pure C.
- **Type-hinted**: Types and methods are all type-hinted, enabling excellent editor support. - **Type-hinted**: Types and methods are all type-hinted, enabling excellent editor support.
- **Updated**, to make use of the latest Telegram API version and features. - **Async**: Fully asynchronous (also usable synchronously if wanted, for convenience).
- **Bot API-like**: Similar to the Bot API in its simplicity, but much more powerful and detailed. - **Powerful**: Full access to Telegram's API to execute any official client action and more.
- **Pluggable**: The Smart Plugin system allows to write components with minimal boilerplate code.
- **Comprehensive**: Execute any advanced action an official client is able to do, and even more.
### Requirements
- Python 3.6 or higher.
- A [Telegram API key](https://docs.pyrogram.org/intro/setup#api-keys).
### Installing ### Installing
@ -65,11 +66,6 @@ pip3 install pyrogram
### Resources ### Resources
- The docs contain lots of resources to help you get started with Pyrogram: https://docs.pyrogram.org. - Check out the docs at https://docs.pyrogram.org to learn more about Pyrogram, get started right
- Seeking extra help? Come join and ask our community: https://t.me/pyrogram. away and discover more in-depth material for building your client applications.
- For other kind of inquiries, you can send a [message](https://t.me/haskell) or an [e-mail](mailto:dan@pyrogram.org). - Join the official channel at https://t.me/pyrogram and stay tuned for news, updates and announcements.
### Copyright & License
- Copyright (C) 2017-2021 Dan <<https://github.com/delivrance>>
- Licensed under the terms of the [GNU Lesser General Public License v3 or later (LGPLv3+)](COPYING.lesser)

View File

@ -158,6 +158,7 @@ def pyrogram_api():
send_venue send_venue
send_contact send_contact
send_cached_media send_cached_media
send_reaction
edit_message_text edit_message_text
edit_message_caption edit_message_caption
edit_message_media edit_message_media
@ -180,7 +181,9 @@ def pyrogram_api():
retract_vote retract_vote
send_dice send_dice
search_messages search_messages
search_messages_count
search_global search_global
search_global_count
download_media download_media
get_discussion_message get_discussion_message
""", """,
@ -284,6 +287,7 @@ def pyrogram_api():
send_game send_game
set_game_score set_game_score
get_game_high_scores get_game_high_scores
set_bot_commands
""", """,
authorization=""" authorization="""
Authorization Authorization
@ -360,6 +364,7 @@ def pyrogram_api():
ChatEvent ChatEvent
ChatEventFilter ChatEventFilter
ChatMemberUpdated ChatMemberUpdated
ChatJoinRequest
Dialog Dialog
Restriction Restriction
""", """,
@ -384,6 +389,7 @@ def pyrogram_api():
Poll Poll
PollOption PollOption
Dice Dice
Reaction
VoiceChatScheduled VoiceChatScheduled
VoiceChatStarted VoiceChatStarted
VoiceChatEnded VoiceChatEnded
@ -420,6 +426,8 @@ def pyrogram_api():
InlineQueryResultArticle InlineQueryResultArticle
InlineQueryResultPhoto InlineQueryResultPhoto
InlineQueryResultAnimation InlineQueryResultAnimation
InlineQueryResultAudio
InlineQueryResultVideo
ChosenInlineResult ChosenInlineResult
""", """,
input_message_content=""" input_message_content="""

View File

@ -1,12 +1,11 @@
Bound Methods Bound Methods
============= =============
Some Pyrogram types define what are called bound methods. Bound methods are functions attached to a class which are Some Pyrogram types define what are called bound methods. Bound methods are functions attached to a type which are
accessed via an instance of that class. They make it even easier to call specific methods by automatically inferring accessed via an instance of that type. They make it even easier to call specific methods by automatically inferring
some of the required arguments. some of the required arguments.
.. code-block:: python .. code-block:: python
:emphasize-lines: 8
from pyrogram import Client from pyrogram import Client
@ -15,7 +14,7 @@ some of the required arguments.
@app.on_message() @app.on_message()
def hello(client, message) def hello(client, message)
message.reply_text("hi") message.reply("hi")
app.run() app.run()

View File

@ -1,18 +1,17 @@
Available Methods Available Methods
================= =================
This page is about Pyrogram methods. All the methods listed here are bound to a :class:`~pyrogram.Client` instance, This page is about Pyrogram methods. All the methods listed here are bound to a :class:`~pyrogram.Client` instance.
except for :meth:`~pyrogram.idle()`, which is a special function that can be found in the main package directly. Some other utility functions can instead be found in the main package directly.
.. code-block:: python .. code-block:: python
:emphasize-lines: 6
from pyrogram import Client from pyrogram import Client
app = Client("my_account") app = Client("my_account")
with app: with app:
app.send_message("haskell", "hi") app.send_message("me", "hi")
.. contents:: Contents .. contents:: Contents
:backlinks: none :backlinks: none

View File

@ -6,7 +6,6 @@ Unless required as argument to a client method, most of the types don't need to
are only returned by other methods. You also don't need to import them, unless you want to type-hint your variables. are only returned by other methods. You also don't need to import them, unless you want to type-hint your variables.
.. code-block:: python .. code-block:: python
:emphasize-lines: 1
from pyrogram.types import User, Message, ... from pyrogram.types import User, Message, ...

View File

@ -62,6 +62,7 @@ CHAT_NOT_MODIFIED The chat settings (title, permissions, photo, etc..) were not
CHAT_RESTRICTED The chat is restricted and cannot be used CHAT_RESTRICTED The chat is restricted and cannot be used
CHAT_SEND_INLINE_FORBIDDEN You cannot use inline bots to send messages in this chat CHAT_SEND_INLINE_FORBIDDEN You cannot use inline bots to send messages in this chat
CHAT_TITLE_EMPTY The chat title is empty CHAT_TITLE_EMPTY The chat title is empty
CHAT_TOO_BIG The chat is too big for this action
CODE_EMPTY The provided code is empty CODE_EMPTY The provided code is empty
CODE_HASH_INVALID The provided code hash invalid CODE_HASH_INVALID The provided code hash invalid
CODE_INVALID The provided code is invalid (i.e. from email) CODE_INVALID The provided code is invalid (i.e. from email)

1 id message
62 CHAT_RESTRICTED The chat is restricted and cannot be used
63 CHAT_SEND_INLINE_FORBIDDEN You cannot use inline bots to send messages in this chat
64 CHAT_TITLE_EMPTY The chat title is empty
65 CHAT_TOO_BIG The chat is too big for this action
66 CODE_EMPTY The provided code is empty
67 CODE_HASH_INVALID The provided code hash invalid
68 CODE_INVALID The provided code is invalid (i.e. from email)

View File

@ -2,7 +2,7 @@ id message
ACTIVE_USER_REQUIRED The method is only available to already activated users ACTIVE_USER_REQUIRED The method is only available to already activated users
AUTH_KEY_INVALID The key is invalid AUTH_KEY_INVALID The key is invalid
AUTH_KEY_PERM_EMPTY The method is unavailable for temporary authorization key, not bound to permanent AUTH_KEY_PERM_EMPTY The method is unavailable for temporary authorization key, not bound to permanent
AUTH_KEY_UNREGISTERED The key is not registered in the system AUTH_KEY_UNREGISTERED The key is not registered in the system. Delete your session file and login again
SESSION_EXPIRED The authorization has expired SESSION_EXPIRED The authorization has expired
SESSION_PASSWORD_NEEDED The two-step verification is enabled and a password is required SESSION_PASSWORD_NEEDED The two-step verification is enabled and a password is required
SESSION_REVOKED The authorization has been invalidated, because of the user terminating all sessions SESSION_REVOKED The authorization has been invalidated, because of the user terminating all sessions

1 id message
2 ACTIVE_USER_REQUIRED The method is only available to already activated users
3 AUTH_KEY_INVALID The key is invalid
4 AUTH_KEY_PERM_EMPTY The method is unavailable for temporary authorization key, not bound to permanent
5 AUTH_KEY_UNREGISTERED The key is not registered in the system The key is not registered in the system. Delete your session file and login again
6 SESSION_EXPIRED The authorization has expired
7 SESSION_PASSWORD_NEEDED The two-step verification is enabled and a password is required
8 SESSION_REVOKED The authorization has been invalidated, because of the user terminating all sessions

View File

@ -1,8 +0,0 @@
# Pyrogram Docs
- Install requirements.
- Install `pandoc` and `latexmk`.
- HTML: `make html`
- PDF: `make latexpdf`
TODO: Explain better

View File

@ -1,5 +1,5 @@
sphinx sphinx
sphinx_rtd_theme sphinx_rtd_theme==1.0.0
sphinx_copybutton sphinx_copybutton
pypandoc pypandoc
requests requests

View File

@ -1,7 +0,0 @@
User-agent: *
Allow: /
Disallow: /old*
Sitemap: https://docs.pyrogram.org/sitemap.xml

View File

@ -1,84 +0,0 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2021 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 shutil
from datetime import datetime
from pathlib import Path
import pypandoc
import requests
URL = "https://api.github.com/repos/pyrogram/pyrogram/releases"
DEST = Path("../source/releases")
INTRO = """
Release Notes
=============
Release notes for Pyrogram releases will describe what's new in each version, and will also make you aware of any
backwards-incompatible changes made in that version.
When upgrading to a new version of Pyrogram, you will need to check all the breaking changes in order to find
incompatible code in your application, but also to take advantage of new features and improvements.
**Contents**
""".lstrip("\n")
shutil.rmtree(DEST, ignore_errors=True)
DEST.mkdir(parents=True)
releases = requests.get(URL).json()
with open(DEST / "index.rst", "w") as index:
index.write(INTRO)
tags = []
for release in releases:
tag = release["tag_name"]
title = release["name"]
name = title.split(" - ")[1]
date = datetime.strptime(
release["published_at"],
"%Y-%m-%dT%H:%M:%SZ"
).strftime("%b %d, %Y")
body = pypandoc.convert_text(
release["body"].replace(r"\r\n", "\n"),
"rst",
format="markdown_github",
extra_args=["--wrap=none"]
)
tarball_url = release["tarball_url"]
zipball_url = release["zipball_url"]
index.write("- :doc:`{} <{}>`\n".format(title, tag))
tags.append(tag)
with open(DEST / "{}.rst".format(tag), "w") as page:
page.write("Pyrogram " + tag + "\n" + "=" * (len(tag) + 9) + "\n\n")
page.write("\t\tReleased on " + str(date) + "\n\n")
page.write("- :download:`Source Code (zip) <{}>`\n".format(zipball_url))
page.write("- :download:`Source Code (tar.gz) <{}>`\n\n".format(tarball_url))
page.write(name + "\n" + "-" * len(name) + "\n\n")
page.write(body + "\n\n")
index.write("\n.. toctree::\n :hidden:\n\n")
index.write("\n".join(" {}".format(tag) for tag in tags))

View File

@ -1,81 +0,0 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2021 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 datetime
import os
canonical = "https://docs.pyrogram.org/"
dirs = {
".": ("weekly", 1.0),
"intro": ("weekly", 0.9),
"start": ("weekly", 0.9),
"api": ("weekly", 0.8),
"topics": ("weekly", 0.8),
"releases": ("weekly", 0.8),
"telegram": ("weekly", 0.6)
}
def now():
return datetime.datetime.today().strftime("%Y-%m-%d")
with open("sitemap.xml", "w") as f:
f.write('<?xml version="1.0" encoding="utf-8"?>\n')
f.write('<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n')
urls = []
def search(path):
try:
for j in os.listdir(path):
search(f"{path}/{j}")
except NotADirectoryError:
if not path.endswith(".rst"):
return
path = path.split("/")[2:]
if path[0].endswith(".rst"):
folder = "."
else:
folder = path[0]
path = f"{canonical}{'/'.join(path)}"[:-len(".rst")]
if path.endswith("index"):
path = path[:-len("index")]
urls.append((path, now(), *dirs[folder]))
search("../source")
urls.sort(key=lambda x: x[3], reverse=True)
for i in urls:
f.write(f" <url>\n")
f.write(f" <loc>{i[0]}</loc>\n")
f.write(f" <lastmod>{i[1]}</lastmod>\n")
f.write(f" <changefreq>{i[2]}</changefreq>\n")
f.write(f" <priority>{i[3]}</priority>\n")
f.write(f" </url>\n")
f.write("</urlset>")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

View File

@ -8,7 +8,6 @@ found starting from this page.
This page is about the Client class, which exposes high-level methods for an easy access to the API. This page is about the Client class, which exposes high-level methods for an easy access to the API.
.. code-block:: python .. code-block:: python
:emphasize-lines: 1-3
from pyrogram import Client from pyrogram import Client

View File

@ -1,16 +1,12 @@
Decorators Decorators
========== ==========
While still being methods bound to the :class:`~pyrogram.Client` class, decorators are of a special kind and thus
deserve a dedicated page.
Decorators are able to register callback functions for handling updates in a much easier and cleaner way compared to 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 :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 :meth:`~pyrogram.Client.add_handler` automatically. All you need to do is adding the decorators on top of your
functions. functions.
.. code-block:: python .. code-block:: python
:emphasize-lines: 6
from pyrogram import Client from pyrogram import Client

View File

@ -5,7 +5,6 @@ Handlers are used to instruct Pyrogram about which kind of updates you'd like to
For a much more convenient way of registering callback functions have a look at :doc:`Decorators <decorators>` instead. For a much more convenient way of registering callback functions have a look at :doc:`Decorators <decorators>` instead.
.. code-block:: python .. code-block:: python
:emphasize-lines: 2, 11
from pyrogram import Client from pyrogram import Client
from pyrogram.handlers import MessageHandler from pyrogram.handlers import MessageHandler

View File

@ -29,35 +29,37 @@ from pygments.styles.friendly import FriendlyStyle
FriendlyStyle.background_color = "#f3f2f1" FriendlyStyle.background_color = "#f3f2f1"
project = "Pyrogram" project = "Pyrogram"
copyright = f"2017-{datetime.now().year}, Dan" copyright = f"2017-present, Dan"
author = "Dan" author = "Dan"
version = ".".join(__version__.split(".")[:-1])
extensions = [ extensions = [
"sphinx.ext.autodoc", "sphinx.ext.autodoc",
"sphinx.ext.napoleon", "sphinx.ext.napoleon",
"sphinx.ext.autosummary", "sphinx.ext.autosummary",
"sphinx_copybutton", "sphinx_copybutton"
"sphinx_tabs.tabs"
] ]
master_doc = "index" master_doc = "index"
source_suffix = ".rst" source_suffix = ".rst"
autodoc_member_order = "bysource" autodoc_member_order = "bysource"
version = __version__ templates_path = ["_resources/templates"]
release = version html_copy_source = False
templates_path = ["_templates"]
napoleon_use_rtype = False napoleon_use_rtype = False
napoleon_use_param = False
pygments_style = "friendly" pygments_style = "friendly"
copybutton_prompt_text = "$ " copybutton_prompt_text = "$ "
suppress_warnings = ["image.not_readable"]
html_title = "Pyrogram Documentation" html_title = "Pyrogram Documentation"
html_theme = "sphinx_rtd_theme" html_theme = "sphinx_rtd_theme"
html_static_path = ["_static"] html_static_path = ["_resources/static"]
html_show_sourcelink = True html_show_sourcelink = True
html_show_copyright = False html_show_copyright = False
html_theme_options = { html_theme_options = {
@ -65,17 +67,15 @@ html_theme_options = {
"collapse_navigation": True, "collapse_navigation": True,
"sticky_navigation": False, "sticky_navigation": False,
"logo_only": True, "logo_only": True,
"display_version": True, "display_version": False,
"style_external_links": True "style_external_links": True
} }
napoleon_use_param = False html_logo = "_resources/static/img/pyrogram.png"
html_favicon = "_resources/static/img/favicon.ico"
html_logo = "_images/pyrogram.png"
html_favicon = "_images/favicon.ico"
latex_engine = "xelatex" latex_engine = "xelatex"
latex_logo = "_images/pyrogram.png" latex_logo = "_resources/static/img/pyrogram.png"
latex_elements = { latex_elements = {
"pointsize": "12pt", "pointsize": "12pt",

View File

@ -1,407 +0,0 @@
Pyrogram FAQ
============
.. role:: strike
:class: strike
This FAQ page provides answers to common questions about Pyrogram and, to some extent, Telegram in general.
.. tip::
If you think something interesting could be added here, feel free to propose it by opening a `Feature Request`_.
.. contents:: Contents
:backlinks: none
:depth: 1
:local:
-----
What is Pyrogram?
-----------------
**Pyrogram** is an elegant, easy-to-use Telegram_ client library and framework written from the ground up in Python and
C. It enables you to easily create custom applications for both user and bot identities (bot API alternative) via the
:doc:`MTProto API <topics/mtproto-vs-botapi>` with the Python programming language.
.. _Telegram: https://telegram.org
Where does the name come from?
------------------------------
The name "Pyrogram" is composed by **pyro**, which comes from the Greek word *πῦρ (pyr)*, meaning fire, and **gram**,
from *Telegram*. The word *pyro* itself is built from *Python*, **py** for short, and the suffix **ro** to come up with
the word *fire*, which also inspired the project logo.
How old is Pyrogram?
--------------------
Pyrogram was first released on December 12, 2017. The actual work on the framework began roughly three months prior to the
initial public release on `GitHub`_.
.. _GitHub: https://github.com/pyrogram/pyrogram
Why Pyrogram?
-------------
- **Easy**: You can install Pyrogram with pip and start building your applications right away.
- **Elegant**: Low-level details are abstracted and re-presented in a much nicer and easier way.
- **Fast**: Crypto parts are boosted up by TgCrypto_, a high-performance library written in pure C.
- **Asynchronous**: Allows both synchronous and asynchronous models to fit all usage needs.
- **Documented**: API methods, types and public interfaces are all well documented.
- **Type-hinted**: Types and methods are all type-hinted, enabling excellent editor support.
- **Updated**, to make use of the latest Telegram API version and features.
- **Bot API-like**: Similar to the Bot API in its simplicity, but much more powerful and detailed.
- **Pluggable**: The :doc:`Smart Plugin <topics/smart-plugins>` system allows to write components with minimal
boilerplate code.
- **Comprehensive**: Execute any :doc:`advanced action <topics/advanced-usage>` an official client is able to do, and
even more.
.. _TgCrypto: https://github.com/pyrogram/tgcrypto
Why is Pyrogram defined as both Client Library and Framework?
-------------------------------------------------------------
Simply because it falls in both categories, depending on how you use it.
Pyrogram as a client library makes it easy and intuitive accessing the Telegram API by offering idiomatic Python code
that is generated or hand-written. Low-level details and client-server communication protocols are handled under the
hood. Pyrogram acts as a client library when *you call* its methods and use its types in a batch application that
executes a set of instructions.
Pyrogram as a framework makes it easy to handle live events by allowing you to register event handlers that will be
executed as soon as they arrive from the server side. Pyrogram acts as a framework when it's Pyrogram itself that
*calls your code*, that is, your registered event handlers. Such applications are usually started and left online
indefinitely, until you decide to stop them.
What can MTProto do more than the Bot API?
------------------------------------------
For a detailed answer, please refer to the :doc:`MTProto vs. Bot API <topics/mtproto-vs-botapi>` page.
Why do I need an API key for bots?
----------------------------------
Requests against the official bot API endpoint are made via JSON/HTTP, but are handled by an intermediate server
application that implements the MTProto protocol -- just like Pyrogram -- and uses its own API key, which is always
required, but hidden to the public.
.. figure:: https://i.imgur.com/WvwBoZo.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.
Can I use Webhooks?
-------------------
Lots of people ask this question because they are used to the bot API, but things are different in Pyrogram!
There is no webhook in Pyrogram, simply because there is no HTTP involved, by default. 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 simply sit down and wait for the server to send updates by itself
the very moment they are available (server push).
Can I use the same file_id across different accounts?
-----------------------------------------------------
No, Telegram doesn't allow this.
File ids are personal and bound to a specific account; 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, like this
one: ``CAADBAADyg4AAvLQYAEYD4F7vcZ43AI``.
Can I use Bot API's file_id values in Pyrogram?
-----------------------------------------------
Yes! All file ids you take or might have taken from the Bot API are 100% compatible and re-usable in Pyrogram.
The opposite is also valid, you can take any file id generated by Pyrogram and re-use in the Bot API.
Can I use multiple clients at once on the same account?
-------------------------------------------------------
Yes, you can. Both user and bot accounts are able to run multiple sessions in parallel (up to 10 per account). However,
you must pay attention and not use the *same* exact session in more than one client at the same time. In other words:
- Avoid copying your session file: even if you rename the file, the copied sessions will still point to a specific one
stored in the server.
- Make sure that only one instance of your script runs, using your session file.
If you -- even accidentally -- fail to do so, all the previous session copies will immediately stop receiving updates
and eventually the server will start throwing the error ``[406 AUTH_KEY_DUPLICATED]``, inviting you to login again.
Why is that so? Because the server has recognized two identical sessions are running in two different locations, and
concludes it could possibly be due to a cloned/stolen device. Having the session terminated in such occasions will
protect the user's privacy.
So, the only correct way to run multiple clients on the same account is authorizing your account (either user or bot)
from the beginning every time, and use one separate session for each parallel client you are going to use.
I started a client and nothing happens!
---------------------------------------
If you are connecting from Russia, China or Iran :doc:`you need a proxy <topics/proxy>`, because Telegram could be
partially or totally blocked in those countries. More information about this block can be found at
`Wikipedia <https://en.wikipedia.org/wiki/Blocking_Telegram_in_Russia>`_.
Another possible cause might be network issues, either yours or Telegram's. To confirm this, add the following code on
the top of your script and run it again. You should see some error mentioning a socket timeout or an unreachable network
in a bunch of seconds:
.. code-block:: python
import logging
logging.basicConfig(level=logging.INFO)
Another way to confirm you aren't able to connect to Telegram is by pinging the IP addresses below and see whether ping
fails or not.
What are the IP addresses of Telegram Data Centers?
---------------------------------------------------
The Telegram cloud is currently composed by a decentralized, multi-DC infrastructure (currently 5 DCs, each of which can
work independently) spread in 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
Thanks to `@FrayxRulez <https://t.me/tgbetachat/104921>`_ for telling about alias DCs.
I want to migrate my account from DCX to DCY.
---------------------------------------------
This question is often asked by people who find their account(s) always being connected to DC1 - USA (for example), but
are connecting from a place far away (e.g DC4 - Europe), thus resulting in slower interactions when using the API
because of the great physical distance between the user and its 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, based on the phone number origin.
Even though Telegram `documentations <https://core.telegram.org/api/datacenter#user-migration>`_ state the server might
decide to automatically migrate a user in case of prolonged usages from a distant, unusual location and albeit this
mechanism is also `confirmed <https://twitter.com/telegram/status/427131446655197184>`_ to exist by Telegram itself,
it's currently not possible to have your account migrated, in any way, simply because the feature was once planned but
not yet implemented.
Thanks to `@gabriel <https://t.me/AnotherGroup/217699>`_ for confirming the feature was not implemented yet.
Why is my client reacting slowly in supergroups?
------------------------------------------------
This issue affects only some supergroups or only some members within the same supergroup. Mostly, it affects supergroups
whose creator's account (and thus the supergroup itself) lives inside a **different DC**, far away from yours, but could
also depend on where a member is connecting from.
Because of how Telegram works internally, every single message you receive from and send to other members 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 its DC to the creator's DC and finally to your DC. This process
will inevitably take its time.
To confirm this theory and see it by yourself, you can test in a supergroup where you are sure all parties live
inside the same DC. In this case the responses will be faster.
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.
Thanks to `@Manuel15 <https://t.me/PyrogramChat/76990>`_ for the priority list.
I keep getting PEER_ID_INVALID error!
-------------------------------------
The error in question is ``[400 PEER_ID_INVALID]``, and could mean several things:
- The chat id you tried to use is simply wrong, double check it.
- 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
(either a forward or a mention in the message text) or obtaining the dialogs list.
Code hangs when I 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 safely continue. In other words, 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.
UnicodeEncodeError: '<encoding>' 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 a
better terminal altogether.
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 exceeds 20 MB in size. In such cases, your only option is to download the media yourself and upload from your
local machine.
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.
It could also 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 do 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.
sqlite3.OperationalError: unable to open database file
------------------------------------------------------
Stackoverflow to the rescue: https://stackoverflow.com/questions/4636970
sqlite3.InterfaceError: Error binding parameter 0
-------------------------------------------------
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")
My verification code expires immediately!
-----------------------------------------
That is because you likely shared it across any of your Telegram chats. Yes, that's right: the server keeps scanning the
messages you send and if an active verification code is found it will immediately expire, automatically.
The reason behind this is to protect unaware users from giving their account access to any potential scammer, but if you
legitimately want to share your account(s) verification codes, consider scrambling them, e.g. ``12345````1-2-3-4-5``.
How can avoid Flood Waits?
--------------------------
Long story short: make less requests, and remember that the API is designed to be used by official apps, by real people;
anything above normal usage could be limited.
This question is being asked quite a lot of times, but the bottom line is that nobody knows the exact limits and it's
unlikely that such information will be ever disclosed, because otherwise people could easily circumvent them and defeat
their whole purpose.
Do also note that Telegram wants to be a safe and reliable place and that limits exist to protect itself from abuses.
Having said that, here's some insights about limits:
- They are tuned by Telegram based on real people usage and can change anytime.
- Some limits are be applied to single sessions, some others apply to the whole account.
- Limits vary based on methods and the arguments passed to methods. For example: log-ins are expensive and thus have
stricter limits; replying to a user command could cause a flood wait in case the user starts flooding, but
such limit will only be applied to that particular chat (i.e.: other users are not affected).
- You can catch Flood Wait exceptions in your code and wait the required seconds before continuing, this way:
.. code-block:: python
import time
from pyrogram.errors import FloodWait
try:
... # Your code
except FloodWait as e:
time.sleep(e.x) # Wait "x" seconds before continuing
More info about error handling can be found `here <start/errors>`_.
My account has been deactivated/limited!
----------------------------------------
First of all, you should understand that Telegram wants to be a safe place for people to stay in, and to pursue this
goal there are automatic protection systems running to prevent flood and spam, as well as a moderation team of humans
who review reports.
.. centered:: Pyrogram is a tool at your commands; it only does what you tell it to do, the rest is up to you.
Having said that, here's a list of what Telegram definitely doesn't like:
- Flood, abusing the API.
- Spam, sending unsolicited messages or adding people to unwanted groups and channels.
- Virtual/VoIP and cheap real numbers, because they are relatively easy to get and likely used for spam/flood.
And thanks to `@koteeq <https://t.me/koteeq>`_, here's a good explanation of how, probably, the system works:
.. raw:: html
<script
async src="https://telegram.org/js/telegram-widget.js?5"
data-telegram-post="PyrogramChat/69424"
data-width="100%">
</script>
<br><br>
However, you might be right, and your account was deactivated/limited without any good reason. This could happen because
of mistakes by either the automatic systems or a moderator. In such cases you can kindly email Telegram at
recover@telegram.org, contact `@smstelegram`_ on Twitter or use `this form`_.
Are there any secret easter eggs?
---------------------------------
Yes. If you found one, `let me know`_!
.. _let me know: https://t.me/pyrogram
.. _@smstelegram: https://twitter.com/smstelegram
.. _this form: https://telegram.org/support
.. _Bug Report: https://github.com/pyrogram/pyrogram/issues/new?labels=bug&template=bug_report.md
.. _Feature Request: https://github.com/pyrogram/pyrogram/issues/new?labels=enhancement&template=feature_request.md

View File

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

View File

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

View File

@ -0,0 +1,23 @@
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 time
from pyrogram.errors import FloodWait
...
try:
... # Your code
except FloodWait as e:
await asyncio.sleep(e.x) # Wait "x" seconds before continuing
...
More info about error handling can be found :doc:`here <../start/errors>`.

View File

@ -0,0 +1,9 @@
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).

47
docs/source/faq/index.rst Normal file
View File

@ -0,0 +1,47 @@
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:`why-is-the-event-handler-triggered-twice-or-more`
- :doc:`sqlite3-operationalerror-database-is-locked`
- :doc:`sqlite3-interfaceerror-error-binding-parameter`
- :doc:`socket-send-raised-exception-oserror-timeouterror`
- :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
why-is-the-event-handler-triggered-twice-or-more
sqlite3-operationalerror-database-is-locked
sqlite3-interfaceerror-error-binding-parameter
socket-send-raised-exception-oserror-timeouterror
how-to-avoid-flood-waits
the-account-has-been-limited-deactivated

View File

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

View File

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

View File

@ -0,0 +1,10 @@
socket.send() raised exception, OSError(), TimeoutError()
=========================================================
If you get this error chances are you are blocking the event loop for too long.
In general, it means you are executing thread-blocking code that prevents the event loop from
running properly. For example:
- You are using ``time.sleep()`` instead of ``asyncio.sleep()``.
- You are running processing loops that take too much time to complete.
- You are reading/writing files to disk that take too much time to complete.

View File

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

View File

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

View File

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

View File

@ -0,0 +1,7 @@
UnicodeEncodeError: '...' codec can't encode ...
================================================
Where ``<encoding>`` might be *ascii*, *cp932*, *charmap* or anything else other than *utf-8*. This error usually
shows up when you try to print something and has very little to do with Pyrogram itself as it is strictly related to
your own terminal. To fix it, either find a way to change the encoding settings of your terminal to UTF-8 or switch to
another terminal altogether.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,28 @@
Why is the event handler called twice or more?
==============================================
The event handler is being called twice or more because one or more message edit events have arrived.
By default, Pyrogram listens to both new and edit message events inside ``on_message`` handlers. To prevent edit events
from calling the handler, use the "not edited" filter ``~filters.edited``.
For example:
.. code-block:: python
...
@app.on_message(... & ~filters.edited)
async def handler(client, message):
...
Or, avoid handling any edited message altogether this way:
.. code-block:: python
...
@app.on_message(filters.edited)
async def edited(client, message):
pass
... # other handlers

View File

@ -1,86 +0,0 @@
Pyrogram Glossary
=================
This page contains a list of common words with brief explanations related to Pyrogram and, to some extent, Telegram in
general. Some words may as well link to dedicated articles in case the topic is covered in a more detailed fashion.
.. tip::
If you think something interesting could be added here, feel free to propose it by opening a `Feature Request`_.
.. contents:: Contents
:backlinks: none
:depth: 1
:local:
-----
Terms
-----
.. glossary::
:sorted:
API
Application Programming Interface: a set of methods, protocols and tools that make it easier to develop programs
by providing useful building blocks to the developer.
API key
A secret code used to authenticate and/or authorize a specific application to Telegram in order for it to
control how the API is being used, for example, to prevent abuses of the API.
:doc:`More on API keys <intro/setup>`.
DC
Also known as *data center*, is a place where lots of computer systems are housed and used together in order to
achieve high quality and availability for services.
RPC
Acronym for Remote Procedure Call, that is, a function which gets executed at some remote place (i.e. Telegram
server) and not in your local machine.
RPCError
An error caused by an RPC which must be returned in place of the successful result in order to let the caller
know something went wrong. :doc:`More on RPCError <start/errors>`.
MTProto
The name of the custom-made, open and encrypted protocol by Telegram, implemented in Pyrogram.
:doc:`More on MTProto <topics/mtproto-vs-botapi>`.
MTProto API
The Telegram main API Pyrogram makes use of, which is able to connect both users and normal bots to Telegram
using MTProto as application layer protocol and execute any method Telegram provides from its public TL-schema.
:doc:`More on MTProto API <topics/mtproto-vs-botapi>`.
Bot API
The Telegram Bot API that is able to only connect normal bots only to Telegram using HTTP as application layer
protocol and allows to execute a sub-set of the main Telegram API.
:doc:`More on Bot API <topics/mtproto-vs-botapi>`.
Pyrogrammer
A developer that uses Pyrogram to build Telegram applications.
Userbot
Also known as *user bot* or *ubot* for short, is a user logged in by third-party Telegram libraries --- such as
Pyrogram --- to automate some behaviours, like sending messages or reacting to text commands or any other event.
Not to be confused with *bot*, that is, a normal Telegram bot created by `@BotFather <https://t.me/botfather>`_.
Session
Also known as *login session*, is a strictly personal piece of data created and held by both parties
(client and server) which is used to grant permission into a single account without having to start a new
authorization process from scratch.
Callback
Also known as *callback function*, is a user-defined generic function that *can be* registered to and then
called-back by the framework when specific events occurs.
Handler
An object that wraps around a callback function that is *actually meant* to be registered into the framework,
which will then be able to handle a specific kind of events, such as a new incoming message, for example.
:doc:`More on Handlers <start/updates>`.
Decorator
Also known as *function decorator*, in Python, is a callable object that is used to modify another function.
Decorators in Pyrogram are used to automatically register callback functions for handling updates.
:doc:`More on Decorators <start/updates>`.
.. _Feature Request: https://github.com/pyrogram/pyrogram/issues/new?labels=enhancement&template=feature_request.md

View File

@ -5,7 +5,8 @@ Welcome to Pyrogram
<div align="center"> <div align="center">
<a href="/"> <a href="/">
<div><img src="_static/pyrogram.png" alt="Pyrogram Logo" width="420"></div> <div class="pyrogram-logo-index"><img src="_static/pyrogram.png" alt="Pyrogram"></div>
<div class="pyrogram-text pyrogram-text-index">Pyrogram</div>
</a> </a>
</div> </div>
@ -14,15 +15,15 @@ Welcome to Pyrogram
<br> <br>
<a href="https://github.com/pyrogram/pyrogram"> <a href="https://github.com/pyrogram/pyrogram">
Source Code Development
</a> </a>
<a href="https://github.com/pyrogram/pyrogram/releases"> <a href="https://docs.pyrogram.org/releases">
Releases Releases
</a> </a>
<a href="https://t.me/Pyrogram"> <a href="https://t.me/pyrogram">
Community News
</a> </a>
</p> </p>
@ -35,16 +36,34 @@ Welcome to Pyrogram
@app.on_message(filters.private) @app.on_message(filters.private)
async def hello(client, message): async def hello(client, message):
await message.reply_text(f"Hello {message.from_user.mention}") await message.reply("Hello from Pyrogram!")
app.run() app.run()
**Pyrogram** is a modern, elegant and easy-to-use Telegram_ framework written from the ground up in Python and C. **Pyrogram** is a modern, elegant and asynchronous :doc:`MTProto API <topics/mtproto-vs-botapi>` framework.
It enables you to easily create custom apps for both user and bot identities (bot API alternative) via the It enables you to easily interact with the main Telegram API through a user account (custom client) or a bot identity
:doc:`MTProto API <topics/mtproto-vs-botapi>`. (bot API alternative) using Python.
.. _Telegram: https://telegram.org 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>`_.
Key Features
------------
- **Ready**: Install Pyrogram with pip and start building your applications right away.
- **Easy**: Makes the Telegram API simple and intuitive, while still allowing advanced usages.
- **Elegant**: Low-level details are abstracted and re-presented in a more convenient way.
- **Fast**: Boosted up by :doc:`TgCrypto <topics/tgcrypto>`, a high-performance crypto library written in pure C.
- **Type-hinted**: Types and methods are all type-hinted, enabling excellent editor support.
- **Async**: Fully asynchronous (also usable synchronously if wanted, for convenience).
- **Powerful**: Full access to Telegram's API to execute any official client action and more.
How the Documentation is Organized How the Documentation is Organized
---------------------------------- ----------------------------------
@ -53,18 +72,11 @@ Contents are organized into sections composed of self-contained topics which can
following them in order using the :guilabel:`Next` button at the end of each page. Here below you can, instead, find a following them in order using the :guilabel:`Next` button at the end of each page. Here below you can, instead, find a
list of the most relevant pages for a quick access. list of the most relevant pages for a quick access.
.. admonition :: Cloud Credits
:class: tip
If you need a cloud server to host your applications, we recommend using **Hetzner Cloud**. Sign up with
`this link <https://hetzner.cloud/?ref=9CyT92gZEINU>`_ to get €20 in cloud credits and help support Pyrogram as
well.
First Steps First Steps
^^^^^^^^^^^ ^^^^^^^^^^^
.. hlist:: .. hlist::
:columns: 2 :columns: 1
- :doc:`Quick Start <intro/quickstart>`: Overview to get you started quickly. - :doc:`Quick Start <intro/quickstart>`: Overview to get you started quickly.
- :doc:`Calling Methods <start/invoking>`: How to call Pyrogram's methods. - :doc:`Calling Methods <start/invoking>`: How to call Pyrogram's methods.
@ -75,7 +87,7 @@ API Reference
^^^^^^^^^^^^^ ^^^^^^^^^^^^^
.. hlist:: .. hlist::
:columns: 2 :columns: 1
- :doc:`Pyrogram Client <api/client>`: Reference details about the Client class. - :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 Methods <api/methods/index>`: List of available high-level methods.
@ -86,16 +98,12 @@ Meta
^^^^ ^^^^
.. hlist:: .. hlist::
:columns: 2 :columns: 1
- :doc:`Pyrogram FAQ <faq>`: Answers to common Pyrogram questions. - :doc:`Pyrogram FAQ <faq/index>`: Answers to common Pyrogram questions.
- :doc:`Pyrogram Glossary <glossary>`: List of words with brief explanations.
- :doc:`Support Pyrogram <support>`: Ways to show your appreciation. - :doc:`Support Pyrogram <support>`: Ways to show your appreciation.
- :doc:`About the License <license>`: Information about the Project license.
- :doc:`Release Notes <releases/index>`: Release notes for Pyrogram releases. - :doc:`Release Notes <releases/index>`: Release notes for Pyrogram releases.
Last updated on |today|
.. toctree:: .. toctree::
:hidden: :hidden:
:caption: Introduction :caption: Introduction
@ -136,14 +144,13 @@ Last updated on |today|
topics/more-on-updates topics/more-on-updates
topics/config-file topics/config-file
topics/smart-plugins topics/smart-plugins
topics/session-settings topics/client-settings
topics/tgcrypto topics/tgcrypto
topics/storage-engines topics/storage-engines
topics/text-formatting topics/text-formatting
topics/serializing topics/serializing
topics/proxy topics/proxy
topics/scheduling topics/scheduling
topics/bots-interaction
topics/mtproto-vs-botapi topics/mtproto-vs-botapi
topics/debugging topics/debugging
topics/test-servers topics/test-servers
@ -154,10 +161,8 @@ Last updated on |today|
:hidden: :hidden:
:caption: Meta :caption: Meta
faq faq/index
glossary
support support
license
releases/index releases/index
.. toctree:: .. toctree::

View File

@ -1,16 +1,9 @@
Install Guide Install Guide
============= =============
Being a modern Python library, **Pyrogram** requires Python 3.6+ to be installed in your system. 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. We recommend using the latest versions of both Python 3 and pip.
- Get **Python 3** from https://www.python.org/downloads/ (or with your package manager).
- Get **pip** by following the instructions at https://pip.pypa.io/en/latest/installing/.
.. important::
Pyrogram supports **Python 3** only, starting from version 3.6. **PyPy** is supported too.
.. contents:: Contents .. contents:: Contents
:backlinks: none :backlinks: none
:depth: 1 :depth: 1
@ -36,12 +29,7 @@ Install Pyrogram
Bleeding Edge Bleeding Edge
------------- -------------
Pyrogram is always evolving, although new releases on PyPI are published only when enough changes are added, but this You can install the development version from the git ``master`` branch using this command:
doesn't mean you can't try new features right now!
In case you'd like to try out the latest Pyrogram features, the `GitHub repo`_ is always kept updated with new changes;
you can install the development version straight from the ``master`` branch using this command (note "master.zip" in
the link):
.. code-block:: text .. code-block:: text
@ -57,6 +45,6 @@ If no error shows up you are good to go.
>>> import pyrogram >>> import pyrogram
>>> pyrogram.__version__ >>> pyrogram.__version__
'|version|' 'x.y.z'
.. _`Github repo`: http://github.com/pyrogram/pyrogram .. _`Github repo`: http://github.com/pyrogram/pyrogram

View File

@ -1,49 +1,54 @@
Quick Start Quick Start
=========== ===========
The next few steps serve as a quick start for all new :term:`Pyrogrammers <Pyrogrammer>` that want to see Pyrogram in The next few steps serve as a quick start to see Pyrogram in action as fast as possible.
action as fast as possible. Let's go!
Get Pyrogram Real Fast 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``. 1. Install Pyrogram with ``pip3 install -U pyrogram``.
2. Get your own Telegram API key from https://my.telegram.org/apps. 2. Get your own Telegram API key from https://my.telegram.org/apps.
3. Open your best text editor and paste the following: 3. Open the text editor of your choice and paste the following:
.. code-block:: python .. code-block:: python
import asyncio
from pyrogram import Client from pyrogram import Client
api_id = 12345 api_id = 12345
api_hash = "0123456789abcdef0123456789abcdef" api_hash = "0123456789abcdef0123456789abcdef"
with Client("my_account", api_id, api_hash) as app: async def main():
app.send_message("me", "Greetings from **Pyrogram**!") 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. 4. Replace *api_id* and *api_hash* values with your own.
5. Save the file as ``pyro.py``. 5. Save the file as ``hello.py``.
6. Run the script with ``python3 pyro.py`` 6. Run the script with ``python3 hello.py``
7. Follow the instructions on your terminal to login. 7. Follow the instructions on your terminal to login.
8. Watch Pyrogram send a message to yourself. 8. Watch Pyrogram send a message to yourself.
9. Join our `community`_.
10. Say, "hi!".
Enjoy the API Enjoy the API
------------- -------------
That was just a quick overview that barely scratched the surface! 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
In the next few pages of the introduction, we'll take a much more in-depth look of what we have just done above. we have just done above.
Feeling eager to continue? You can take a shortcut to :doc:`Calling Methods <../start/invoking>` and come back later to If you are feeling eager to continue you can take a shortcut to :doc:`Calling Methods <../start/invoking>` and come back
learn some more details. later to learn some more details.
.. _community: https://t.me/Pyrogram .. _community: https://t.me/Pyrogram

View File

@ -2,7 +2,7 @@ Project Setup
============= =============
We have just :doc:`installed Pyrogram <install>`. In this page we'll discuss what you need to do in order to set up a We have just :doc:`installed Pyrogram <install>`. In this page we'll discuss what you need to do in order to set up a
project with the library. Let's see how it's done. project with the framework.
.. contents:: Contents .. contents:: Contents
:backlinks: none :backlinks: none
@ -17,18 +17,13 @@ API Keys
The very first step requires you to obtain a valid Telegram API key (API id/hash pair): The very first step requires you to obtain a valid Telegram API key (API id/hash pair):
#. Visit https://my.telegram.org/apps and log in with your Telegram Account. #. Visit https://my.telegram.org/apps and log in with your Telegram Account.
#. Fill out the form to register a new Telegram application. #. 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**. #. Done. The API key consists of two parts: **api_id** and **api_hash**. Keep it secret.
.. important::
The API key is personal and must be kept secret.
.. note:: .. note::
The API key is unique for each user, but defines a token for a Telegram *application* you are going to build. This The API key defines a token for a Telegram *application* you are going to build.
means that you are able to authorize multiple users (and bots too) to access the Telegram database through the This means that you are able to authorize multiple users or bots with a single API key.
MTProto API by a single API key.
Configuration Configuration
------------- -------------
@ -36,9 +31,9 @@ Configuration
Having the API key from the previous step in handy, we can now begin to configure a Pyrogram project. Having the API key from the previous step in handy, we can now begin to configure a Pyrogram project.
There are two ways to do so, and you can choose what fits better for you: There are two ways to do so, and you can choose what fits better for you:
- First option (recommended): create a new ``config.ini`` file next to your main script, copy-paste the following and - First option: create a new ``config.ini`` file next to your main script, copy-paste the following and
replace the **api_id** and **api_hash** values with your own. This is the preferred method because allows you to replace the *api_id* and *api_hash* values with your own. This method allows you to keep your credentials out of
keep your credentials out of your code without having to deal with how to load them: your code without having to deal with how to load them.
.. code-block:: ini .. code-block:: ini
@ -47,8 +42,7 @@ There are two ways to do so, and you can choose what fits better for you:
api_hash = 0123456789abcdef0123456789abcdef api_hash = 0123456789abcdef0123456789abcdef
- Alternatively, you can pass your API key to Pyrogram by simply using the *api_id* and *api_hash* parameters of the - Alternatively, you can pass your API key to Pyrogram by simply using the *api_id* and *api_hash* parameters of the
Client class. This way you can have full control on how to store and load your credentials (e.g., you can load the Client class. This way you can have full control on how to store and load your credentials:
credentials from the environment variables and directly pass the values into Pyrogram):
.. code-block:: python .. code-block:: python

View File

@ -1,16 +0,0 @@
About the License
=================
.. image:: https://www.gnu.org/graphics/lgplv3-with-text-154x68.png
:align: left
Pyrogram is free software and is currently licensed under the terms of the
`GNU Lesser General Public License v3 or later (LGPLv3+)`_. In short: you may use, redistribute and/or modify it
provided that modifications are described and licensed for free under LGPLv3+.
In other words: you can use and integrate Pyrogram into your code (either open source, under the same or a different
license, or even proprietary) for any purpose whatsoever without being required to release the source code of your
applications. Derivative works, including modifications to the library itself can only be redistributed under the same
LGPLv3+ license.
.. _GNU Lesser General Public License v3 or later (LGPLv3+): https://github.com/pyrogram/pyrogram/blob/develop/COPYING.lesser

View File

@ -26,23 +26,20 @@ the :meth:`~pyrogram.Client.run` method:
app = Client("my_account") app = Client("my_account")
app.run() app.run()
This starts an interactive shell asking you to input your **phone number** (including your `Country Code`_) and the This starts an interactive shell asking you to input your **phone number**, including your `Country Code`_ (the plus
**phone code** you will receive in your devices that are already authorized or via SMS: ``+`` 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 .. code-block:: text
Enter phone number: +39********** Enter phone number: +1-123-456-7890
Is "+39**********" correct? (y/n): y Is "+1-123-456-7890" correct? (y/n): y
Enter phone code: 32768 Enter phone code: 12345
Logged in successfully as Dan Logged in successfully
After successfully authorizing yourself, a new file called ``my_account.session`` will be created allowing Pyrogram to 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 will be loaded again when you restart your app, and as long as you execute API calls with your identity. This file is personal and will be loaded again when you restart your app, and as
keep the session alive, Pyrogram won't ask you again to enter your phone number. long as you keep the session alive, Pyrogram won't ask you again to enter your phone number.
.. important::
Your ``*.session`` file is personal and must be kept secret.
.. note:: .. note::

View File

@ -1,8 +1,8 @@
Error Handling Error Handling
============== ==============
Errors are inevitable when working with the API, and they can be correctly handled with ``try...except`` blocks in order Errors can be correctly handled with ``try...except`` blocks in order to control the behaviour of your application.
to control the behaviour of your application. Pyrogram errors all live inside the ``errors`` package: Pyrogram errors all live inside the ``errors`` package:
.. code-block:: python .. code-block:: python
@ -25,10 +25,10 @@ This error is raised every time a method call against Telegram's API was unsucce
from pyrogram.errors import RPCError from pyrogram.errors import RPCError
.. warning:: .. note::
It must be noted that catching this error is bad practice, especially when no feedback is given (i.e. by Avoid catching this error everywhere, especially when no feedback is given (i.e. by logging/printing the full error
logging/printing the full error traceback), because it makes it impossible to understand what went wrong. traceback), because it makes it impossible to understand what went wrong.
Error Categories Error Categories
---------------- ----------------
@ -84,9 +84,6 @@ whole category of errors and be sure to also handle these unknown errors.
In case a whole class of errors is unknown (that is, an error code that is unknown), Pyrogram will raise a special In case a whole class of errors is unknown (that is, an error code that is unknown), Pyrogram will raise a special
``520 UnknownError`` exception. ``520 UnknownError`` exception.
In both cases, Pyrogram will log them in the ``unknown_errors.txt`` file. Users are invited to report
these unknown errors in the `discussion group <https://t.me/pyrogram>`_.
Errors with Values Errors with Values
------------------ ------------------

View File

@ -19,7 +19,7 @@ like send_audio(), send_document(), send_location(), etc...
with app: with app:
app.send_message( app.send_message(
"haskell", # Edit this "me", # Edit this
"This is a ReplyKeyboardMarkup example", "This is a ReplyKeyboardMarkup example",
reply_markup=ReplyKeyboardMarkup( reply_markup=ReplyKeyboardMarkup(
[ [
@ -33,7 +33,7 @@ like send_audio(), send_document(), send_location(), etc...
) )
app.send_message( app.send_message(
"haskell", # Edit this "me", # Edit this
"This is a InlineKeyboardMarkup example", "This is a InlineKeyboardMarkup example",
reply_markup=InlineKeyboardMarkup( reply_markup=InlineKeyboardMarkup(
[ [

View File

@ -15,7 +15,7 @@ It uses the ``@on_message`` decorator to register a ``MessageHandler`` and appli
@app.on_message(filters.text & filters.private) @app.on_message(filters.text & filters.private)
def echo(client, message): def echo(client, message):
message.reply_text(message.text) message.reply(message.text)
app.run() # Automatically start() and idle() app.run() # Automatically start() and idle()

View File

@ -13,9 +13,3 @@ This example demonstrates a basic API usage
with app: with app:
# Send a message, Markdown is enabled by default # Send a message, Markdown is enabled by default
app.send_message("me", "Hi there! I'm using **Pyrogram**") app.send_message("me", "Hi there! I'm using **Pyrogram**")
# Send a location
app.send_location("me", 51.500729, -0.124583)
# Send a sticker
app.send_sticker("me", "CAADBAADzg4AAvLQYAEz_x2EOgdRwBYE")

View File

@ -26,7 +26,6 @@ It uses the @on_inline_query decorator to register an InlineQueryHandler.
), ),
url="https://docs.pyrogram.org/intro/install", url="https://docs.pyrogram.org/intro/install",
description="How to install Pyrogram", description="How to install Pyrogram",
thumb_url="https://i.imgur.com/JyxrStE.png",
reply_markup=InlineKeyboardMarkup( reply_markup=InlineKeyboardMarkup(
[ [
[InlineKeyboardButton( [InlineKeyboardButton(
@ -43,7 +42,6 @@ It uses the @on_inline_query decorator to register an InlineQueryHandler.
), ),
url="https://docs.pyrogram.org/start/invoking", url="https://docs.pyrogram.org/start/invoking",
description="How to use Pyrogram", description="How to use Pyrogram",
thumb_url="https://i.imgur.com/JyxrStE.png",
reply_markup=InlineKeyboardMarkup( reply_markup=InlineKeyboardMarkup(
[ [
[InlineKeyboardButton( [InlineKeyboardButton(

View File

@ -11,8 +11,8 @@ This example shows how to query an inline bot (as user).
app = Client("my_account") app = Client("my_account")
with app: with app:
# Get bot results for "Fuzz Universe" from the inline bot @vid # Get bot results for "hello" from the inline bot @vid
bot_results = app.get_inline_bot_results("vid", "Fuzz Universe") bot_results = app.get_inline_bot_results("vid", "hello")
# Send the first result (bot_results.results[0]) to your own chat (Saved Messages) # Send the first result (bot_results.results[0]) to your own chat (Saved Messages)
app.send_inline_bot_result("me", bot_results.query_id, bot_results.results[0].id) app.send_inline_bot_result("me", bot_results.query_id, bot_results.results[0].id)

View File

@ -8,7 +8,7 @@ to make it only work for specific messages in a specific chat.
from pyrogram import Client, emoji, filters from pyrogram import Client, emoji, filters
TARGET = "PyrogramChat" # Target chat. Can also be a list of multiple chat ids/usernames TARGET = -100123456789 # Target chat. Can also be a list of multiple chat ids/usernames
MENTION = "[{}](tg://user?id={})" # User mention markup MENTION = "[{}](tg://user?id={})" # User mention markup
MESSAGE = "{} Welcome to [Pyrogram](https://docs.pyrogram.org/)'s group chat {}!" # Welcome message MESSAGE = "{} Welcome to [Pyrogram](https://docs.pyrogram.org/)'s group chat {}!" # Welcome message

View File

@ -2,7 +2,7 @@ Calling Methods
=============== ===============
At this point, we have successfully :doc:`installed Pyrogram <../intro/install>` and :doc:`authorized <auth>` our 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 library. It's time to start playing with the API! account; we are now aiming towards the core of the framework.
.. contents:: Contents .. contents:: Contents
:backlinks: none :backlinks: none
@ -22,44 +22,53 @@ Making API method calls with Pyrogram is very simple. Here's a basic example we
app = Client("my_account") app = Client("my_account")
with app: async def main():
app.send_message("me", "Hi!") async with app:
await app.send_message("me", "Hi!")
Basic step-by-step app.run(main())
^^^^^^^^^^^^^^^^^^
#. Let's begin by importing the Client class: Step-by-step
^^^^^^^^^^^^
#. Let's begin by importing the Client class.
.. code-block:: python .. code-block:: python
from pyrogram import Client from pyrogram import Client
#. Now instantiate a new Client object, "my_account" is a session name of your choice: #. Now instantiate a new Client object, "my_account" is a session name of your choice.
.. code-block:: python .. code-block:: python
app = Client("my_account") app = Client("my_account")
#. The ``with`` context manager is a shortcut for starting, executing and stopping the Client: #. Async methods can't be executed at the top level, because they must be inside 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 .. code-block:: python
with app: async def main():
async with app:
await app.send_message("me", "Hi!")
#. Now, you can call any method you like: #. Finally, we tell Python to schedule our ``main()`` async function by using Pyrogram's :meth:`~pyrogram.Client.run`
method.
.. code-block:: python .. code-block:: python
app.send_message("me", "Hi!") app.run(main())
Context Manager Context Manager
--------------- ---------------
The ``with`` statement starts a context manager used as a shortcut to automatically call :meth:`~pyrogram.Client.start` The ``async with`` statement starts a context manager, which is used as a shortcut for starting, executing and stopping
and :meth:`~pyrogram.Client.stop`, which are methods required for Pyrogram to work properly. The context manager does the Client, asynchronously. It does so by automatically calling :meth:`~pyrogram.Client.start` and
also gracefully stop the client, even in case of unhandled exceptions in your code. :meth:`~pyrogram.Client.stop` in a more convenient way which also gracefully stops the client, even in case of
unhandled exceptions in your code.
This is how Pyrogram looks without the context manager: Below there's the same example as above, but without the use of the context manager:
.. code-block:: python .. code-block:: python
@ -67,15 +76,39 @@ This is how Pyrogram looks without the context manager:
app = Client("my_account") app = Client("my_account")
app.start() async def main():
app.send_message("me", "Hi!") await app.start()
app.stop() await app.send_message("me", "Hi!")
await app.stop()
Asynchronous Calls 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())
Synchronous Calls
------------------ ------------------
In case you want Pyrogram to run asynchronously (e.g.: if you are using third party libraries that require you to call Pyrogram is an asynchronous framework, but it also provides a convenience way for calling methods without the need
them with ``await``), use the asynchronous context manager: of async/await keywords and the extra boilerplate. In case you want Pyrogram to run synchronously, simply use the
synchronous context manager:
.. code-block:: python .. code-block:: python
@ -83,37 +116,9 @@ them with ``await``), use the asynchronous context manager:
app = Client("my_account") app = Client("my_account")
async def main(): with app:
async with app: app.send_message("me", "Hi!")
await app.send_message("me", "Hi!")
app.run(main()) As you can see, the non-async example becomes less cluttered. Use Pyrogram in this non-asynchronous way only when you
want to write something without the boilerplate or in case you want to combine Pyrogram with other libraries that are
Asynchronous step-by-step not async.
^^^^^^^^^^^^^^^^^^^^^^^^^
#. Import the Client class and create an instance:
.. code-block:: python
from pyrogram import Client
app = Client("my_account")
#. Async methods can't normally be executed at the top level, because they must be inside an async-defined function;
here we define one and put our code inside; the context manager is also being used differently in asyncio and
method calls require the await keyword:
.. 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, which in turn will execute Pyrogram's methods.
Using :meth:`~pyrogram.Client.run` this way is a friendly alternative for the much more verbose
``asyncio.get_event_loop().run_until_complete(main())``:
.. code-block:: python
app.run(main())

View File

@ -1,8 +1,8 @@
Handling Updates Handling Updates
================ ================
Calling :doc:`API methods <invoking>` sequentially is cool, but how to react when, for example, a new message arrives? Calling :doc:`API methods <invoking>` sequentially is one way to use Pyrogram, but how to react when, for example, a
This page deals with updates and how to handle such events in Pyrogram. Let's have a look at how they work. new message arrives? This page deals with updates and how to handle such events in Pyrogram.
.. contents:: Contents .. contents:: Contents
:backlinks: none :backlinks: none
@ -14,10 +14,9 @@ This page deals with updates and how to handle such events in Pyrogram. Let's ha
Defining Updates Defining Updates
---------------- ----------------
First, let's define what are these updates. As hinted already, updates are simply events that happen in your Telegram As hinted already, updates are simply events that happen in your Telegram account (incoming messages, new members join,
account (incoming messages, new members join, bot button presses, etc...), which are meant to notify you about a new bot button presses, etc.), which are meant to notify you about a new specific state that has changed. These updates are
specific state that has changed. These updates are handled by registering one or more callback functions in your app handled by registering one or more callback functions in your app using :doc:`Handlers <../api/handlers>`.
using :doc:`Handlers <../api/handlers>`.
Each handler deals with a specific event and once a matching update arrives from Telegram, your registered callback 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. function will be called back by the framework and its body executed.
@ -40,50 +39,51 @@ The most elegant way to register a message handler is by using the :meth:`~pyrog
app = Client("my_account") app = Client("my_account")
@app.on_message() @app.on_message()
def my_handler(client, message): async def my_handler(client, message):
message.forward("me") await message.forward("me")
app.run() app.run()
The defined function ``my_handler``, which accepts the two arguments *(client, message)*, will be the function that gets 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. executed every time a new message arrives.
Asynchronous handlers 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``.
Synchronous handlers
^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^
You can also have asynchronous handlers; you only need to define the callback function using ``async def`` and call API You can also have synchronous handlers; you only need to define the callback function without using ``async def`` and
methods by placing ``await`` in front of them: call API methods by not placing ``await`` in front of them:
.. code-block:: python .. code-block:: python
@app.on_message() @app.on_message()
async def my_handler(client, message): def my_handler(client, message):
await message.forward("me") message.forward("me")
.. note:: .. note::
You can mix ``def`` and ``async def`` handlers as much as you need, Pyrogram will still work concurrently and You can mix ``def`` and ``async def`` handlers as much as you like, Pyrogram will still work concurrently and
efficiently regardless of what you choose. efficiently regardless of what you choose. However, it is recommended to use Pyrogram in its native, asynchronous
form at all times, unless you want to write something without the boilerplate or in case you want to combine
Pyrogram with other libraries that are not async.
Using add_handler() Using add_handler()
^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
The :meth:`~pyrogram.Client.add_handler` method takes any handler instance that wraps around your defined callback 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 (or in case, function and registers it in your Client. It is useful in case you want to programmatically add handlers.
for some reason, you don't like to use decorators).
.. code-block:: python .. code-block:: python
from pyrogram import Client from pyrogram import Client
from pyrogram.handlers import MessageHandler from pyrogram.handlers import MessageHandler
async def my_function(client, message):
def my_function(client, message): await message.forward("me")
message.forward("me")
app = Client("my_account") app = Client("my_account")
@ -92,12 +92,12 @@ for some reason, you don't like to use decorators).
app.run() app.run()
The same about asynchronous handlers applies for :meth:`~pyrogram.Client.add_handler`: The same about synchronous handlers applies for :meth:`~pyrogram.Client.add_handler`:
.. code-block:: python .. code-block:: python
async def my_function(client, message): def my_function(client, message):
await message.forward("me") message.forward("me")
.. note:: .. note::

View File

@ -1,66 +1,62 @@
Support Pyrogram Support Pyrogram
================ ================
As a developer, you probably understand that "open source" doesn't mean "free work". If you wish to tip me for Pyrogram .. raw:: html
-- or any of my `other works`_ -- you can do so by the ways shown below. Your appreciation means a lot and helps
staying motivated!
--- `Dan`_ <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/fork"
data-icon="octicon-repo-forked" data-size="large" data-show-count="true"
aria-label="Fork pyrogram/pyrogram on GitHub">Fork</a>
<a class="github-button"
href="https://github.com/pyrogram/pyrogram"
data-icon="octicon-star" data-size="large"
data-show-count="true" aria-label="Star pyrogram/pyrogram on GitHub">Star</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:
----- -----
Star GitHub Sponsor
---- --------------
Pyrogram is free and open source software, and thus powered by your love and support! If you like the project and have `Become a GitHub sponsor <https://github.com/sponsors/delivrance>`_.
found it to be useful, give Pyrogram a `Star on GitHub`_.
.. raw:: html .. raw:: html
<a class="github-button" href="https://github.com/pyrogram/pyrogram" data-size="large" data-show-count="true" aria-label="Star pyrogram/pyrogram on GitHub">Star</a> <a class="github-button"
<br><br> href="https://github.com/sponsors/delivrance"
data-icon="octicon-heart"
data-size="large"
aria-label="Sponsor @delivrance on GitHub">
Sponsor</a>
----- -----
Sponsor LiberaPay Patron
------- ----------------
You can become a GitHub sponsor: `Become a LiberaPay patron <https://liberapay.com/delivrance>`_.
.. raw:: html .. raw:: html
<iframe <script src="https://liberapay.com/delivrance/widgets/button.js"></script>
src="https://github.com/sponsors/delivrance/button"
title="Sponsor delivrance"
height="40" width="120"
style="border: 0px; padding-top: 5px; margin-top: -5px">
</iframe>
----- -----
Donate OpenCollective Backer
------ ---------------------
You can donate via PayPal using the button below: `Become an OpenCollective backer <https://opencollective.com/pyrogram>`_
.. raw:: html .. raw:: html
<form action="https://www.paypal.com/donate" method="post" target="_top"> <script src="https://opencollective.com/pyrogram/banner.js"></script>
<input type="hidden" name="hosted_button_id" value="WMKAVFE47XEML" />
<input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif" border="0" name="submit" title="PayPal - The safer, easier way to pay online!" alt="Donate with PayPal button" />
<img alt="" border="0" src="https://www.paypal.com/en_IT/i/scr/pixel.gif" width="1" height="1" />
</form>
-----
Cloud Credits
-------------
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 and help support Pyrogram and
my `other projects`_.
.. _Star on GitHub: https://github.com/pyrogram/pyrogram
.. _other projects: https://github.com/delivrance
.. _other works: https://github.com/delivrance
.. _Dan: https://t.me/haskell

View File

@ -1,9 +1,8 @@
Advanced Usage Advanced Usage
============== ==============
Pyrogram's API, which consists of well documented convenience :doc:`methods <../api/methods/index>` and facade Pyrogram's API -- which consists of well documented :doc:`methods <../api/methods/index>` and
:doc:`types <../api/types/index>`, exists to provide a much easier interface to the undocumented and often confusing :doc:`types <../api/types/index>` -- exists to provide an easier interface to the more complex Telegram API.
Telegram API.
In this section, you'll be shown the alternative way of communicating with Telegram using Pyrogram: the main "raw" 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. Telegram API with its functions and types.
@ -21,25 +20,18 @@ 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 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`. 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 really confusing, mainly because people don't realize soon enough they As already hinted, raw functions and types can be less convenient. This section will therefore explain some pitfalls to
accept *only* the right types and that all required parameters must be filled in. This section will therefore explain take into consideration when working with the raw API.
some pitfalls to take into consideration when working with the raw API.
.. hint:: .. tip::
Every available high-level methods in Pyrogram is built on top of these raw functions. Every available high-level method in Pyrogram is built on top of these raw functions.
Nothing stops you from using the raw functions only, but they are rather complex and
:doc:`plenty of them <../api/methods/index>` are already re-implemented by providing a much simpler and cleaner
interface which is very similar to the Bot API (yet much more powerful).
If you think a raw function should be wrapped and added as a high-level method, feel free to ask in our Community_!
Invoking Functions Invoking Functions
^^^^^^^^^^^^^^^^^^ ------------------
Unlike the :doc:`methods <../api/methods/index>` found in Pyrogram's API, which can be called in the usual simple way, 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 and are more complex. 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>` 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 live in their respective packages (and sub-packages): ``pyrogram.raw.functions``, ``pyrogram.raw.types``. They all exist
@ -61,12 +53,12 @@ Here's some examples:
with Client("my_account") as app: with Client("my_account") as app:
app.send( app.send(
functions.account.UpdateProfile( functions.account.UpdateProfile(
first_name="Dan", last_name="Tès", first_name="First Name", last_name="Last Name",
about="Bio written from Pyrogram" about="New bio text"
) )
) )
- Disable links to your account when someone forwards your messages: - Set online/offline status:
.. code-block:: python .. code-block:: python
@ -74,14 +66,13 @@ Here's some examples:
from pyrogram.raw import functions, types from pyrogram.raw import functions, types
with Client("my_account") as app: with Client("my_account") as app:
app.send( # Set online status
functions.account.SetPrivacy( app.send(functions.account.UpdateStatus(offline=False))
key=types.PrivacyKeyForwards(),
rules=[types.InputPrivacyValueDisallowAll()]
)
)
- Invite users to your channel/supergroup: # Set offline status
app.send(functions.account.UpdateStatus(offline=True))
- Get chat info:
.. code-block:: python .. code-block:: python
@ -89,21 +80,18 @@ Here's some examples:
from pyrogram.raw import functions, types from pyrogram.raw import functions, types
with Client("my_account") as app: with Client("my_account") as app:
app.send( r = app.send(
functions.channels.InviteToChannel( functions.channels.GetFullChannel(
channel=app.resolve_peer(123456789), # ID or Username channel=app.resolve_peer("username")
users=[ # The users you want to invite
app.resolve_peer(23456789), # By ID
app.resolve_peer("username"), # By username
app.resolve_peer("+393281234567"), # By phone number
]
) )
) )
print(r)
Chat IDs Chat IDs
^^^^^^^^ --------
The way Telegram works makes it impossible to directly send a message to a user or a chat by using their IDs only. 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 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. sending messages with IDs only thanks to cached access hashes.
@ -112,18 +100,17 @@ Whenever an InputPeer is needed you must pass one of these:
- :class:`~pyrogram.raw.types.InputPeerUser` - Users - :class:`~pyrogram.raw.types.InputPeerUser` - Users
- :class:`~pyrogram.raw.types.InputPeerChat` - Basic Chats - :class:`~pyrogram.raw.types.InputPeerChat` - Basic Chats
- :class:`~pyrogram.raw.types.InputPeerChannel` - Either Channels or Supergroups - :class:`~pyrogram.raw.types.InputPeerChannel` - Channels & Supergroups
But you don't necessarily have to manually instantiate each object because, luckily for you, Pyrogram already provides 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 :meth:`~pyrogram.Client.resolve_peer` as a convenience utility method that returns the correct InputPeer
by accepting a peer ID only. 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 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. all positive within their respective raw types.
Things are different when working with Pyrogram's API because having them in the same space can theoretically lead to 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 (as well as the official Bot API) uses a slightly different representation for each collisions, and that's why Pyrogram uses a slightly different representation for each kind of ID.
kind of ID.
For example, given the ID *123456789*, here's how Pyrogram can tell entities apart: For example, given the ID *123456789*, here's how Pyrogram can tell entities apart:

View File

@ -1,50 +0,0 @@
Bots Interaction
================
Users can interact with other bots via plain text messages as well as inline queries.
.. contents:: Contents
:backlinks: none
:depth: 1
:local:
-----
Inline Bots
-----------
- If a bot accepts inline queries, you can call it by using
:meth:`~pyrogram.Client.get_inline_bot_results` to get the list of its inline results for a query:
.. code-block:: python
# Get bot results for "Fuzz Universe" from the inline bot @vid
bot_results = app.get_inline_bot_results("vid", "Fuzz Universe")
.. figure:: https://i.imgur.com/IAqLs54.png
:width: 90%
:align: center
:figwidth: 60%
``get_inline_bot_results()`` is the equivalent action of writing ``@vid Fuzz Universe`` and getting the
results list.
- After you retrieved the bot results, you can use
:meth:`~pyrogram.Client.send_inline_bot_result` to send a chosen result to any chat:
.. code-block:: python
# Send the first result to your own chat
app.send_inline_bot_result(
"me",
bot_results.query_id,
bot_results.results[0].id
)
.. figure:: https://i.imgur.com/wwxr7B7.png
:width: 90%
:align: center
:figwidth: 60%
``send_inline_bot_result()`` is the equivalent action of choosing a result from the list and sending it
to a chat.

View File

@ -1,24 +1,12 @@
Session Settings Client Settings
================ ===============
As you may probably know, Telegram allows users (and bots) having more than one session (authorizations) registered You can control the way your client appears in the Active Sessions menu of an official client by changing some client
in the system at the same time. settings. By default you will see something like the following:
Briefly explaining, sessions are simply new logins in your account. They can be reviewed in the settings of an official - Device Model: ``CPython x.y.z``
app (or by invoking :class:`~pyrogram.api.functions.account.GetAuthorizations` with Pyrogram). They - Application: ``Pyrogram x.y.z``
store some useful information such as the client who's using them and from which country and IP address. - System Version: ``Linux x.y.z``
.. figure:: https://i.imgur.com/YaqtMLO.png
:width: 600
:align: center
**A Pyrogram session running on Linux, Python 3.7.**
That's how a session looks like on the Android app, showing the three main pieces of information.
- ``app_version``: **Pyrogram 0.13.0**
- ``device_model``: **CPython 3.7.2**
- ``system_version``: **Linux 4.15.0-23-generic**
.. contents:: Contents .. contents:: Contents
:backlinks: none :backlinks: none

View File

@ -15,7 +15,7 @@ Introduction
------------ ------------
The idea behind using a configuration file is to help keeping your code free of private settings information such as The idea behind using a configuration file is to help keeping your code free of private settings information such as
the API Key and Proxy, without having you to even deal with how to load such settings. The configuration file, usually the API Key and Proxy, without having you to deal with how to load such settings. The configuration file, usually
referred as ``config.ini`` file, is automatically loaded from the root of your working directory; all you need to do is referred as ``config.ini`` file, is automatically loaded from the root of your working directory; all you need to do is
fill in the necessary parts. fill in the necessary parts.
@ -23,7 +23,7 @@ fill in the necessary parts.
The configuration file is optional, but recommended. If, for any reason, you prefer not to use it, there's always an The configuration file is optional, but recommended. If, for any reason, you prefer not to use it, there's always an
alternative way to configure Pyrogram via Client's parameters. Doing so, you can have full control on how to store alternative way to configure Pyrogram via Client's parameters. Doing so, you can have full control on how to store
and load your settings (e.g.: from environment variables). and load your settings.
Settings specified via Client's parameter have higher priority and will override any setting stored in the Settings specified via Client's parameter have higher priority and will override any setting stored in the
configuration file. configuration file.

View File

@ -87,7 +87,7 @@ Finally, the filter usage remains the same:
Filters with Arguments Filters with Arguments
---------------------- ----------------------
A much cooler filter would be one that accepts "pyrogram" or any other string as argument at usage time. 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 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 first argument of the callback function, which is a reference to the filter object itself holding the extra data passed
via named arguments. via named arguments.

View File

@ -2,7 +2,7 @@ Debugging
========= =========
When working with the API, chances are you'll stumble upon bugs, get stuck and start wondering how to continue. Nothing When working with the API, chances are you'll stumble upon bugs, get stuck and start wondering how to continue. Nothing
to actually worry about -- that's normal -- and luckily for you, Pyrogram provides some commodities to help you in this. to actually worry about since Pyrogram provides some commodities to help you in this.
.. contents:: Contents .. contents:: Contents
:backlinks: none :backlinks: none
@ -27,8 +27,8 @@ Consider the following code:
.. code-block:: python .. code-block:: python
dan = app.get_users("haskell") me = app.get_users("me")
print(dan) # User print(me) # User
This will show a JSON representation of the object returned by :meth:`~pyrogram.Client.get_users`, which is a 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: :class:`~pyrogram.types.User` instance, in this case. The output on your terminal will be something similar to this:
@ -36,9 +36,9 @@ This will show a JSON representation of the object returned by :meth:`~pyrogram.
.. code-block:: json .. code-block:: json
{ {
"_": "pyrogram.User", "_": "User",
"id": 23122162, "id": 123456789,
"is_self": false, "is_self": true,
"is_contact": false, "is_contact": false,
"is_mutual_contact": false, "is_mutual_contact": false,
"is_deleted": false, "is_deleted": false,
@ -46,19 +46,13 @@ This will show a JSON representation of the object returned by :meth:`~pyrogram.
"is_verified": false, "is_verified": false,
"is_restricted": false, "is_restricted": false,
"is_support": false, "is_support": false,
"is_scam": false, "first_name": "Pyrogram",
"first_name": "Dan",
"status": {
"_": "pyrogram.UserStatus",
"user_id": 23122162,
"recently": true
},
"username": "haskell",
"language_code": "en",
"photo": { "photo": {
"_": "pyrogram.ChatPhoto", "_": "ChatPhoto",
"small_file_id": "AQADBAAD8tBgAQAEJjCxGgAEo5IBAAIC", "small_file_id": "AbCdE...EdCbA",
"big_file_id": "AQADBAAD8tBgAQAEJjCxGgAEpZIBAAEBAg" "small_photo_unique_id": "VwXyZ...ZyXwV",
"big_file_id": "AbCdE...EdCbA",
"big_photo_unique_id": "VwXyZ...ZyXwV"
} }
} }
@ -69,37 +63,23 @@ Accessing Attributes
-------------------- --------------------
Even though you see a JSON output, it doesn't mean we are dealing with dictionaries; in fact, all Pyrogram types are Even though you see a JSON output, it doesn't mean we are dealing with dictionaries; in fact, all Pyrogram types are
full-fledged Python objects and the correct way to access any attribute of them is by using the dot notation ``.``: fully-fledged Python objects and the correct way to access any attribute of them is by using the dot notation ``.``:
.. code-block:: python .. code-block:: python
dan_photo = dan.photo photo = me.photo
print(dan_photo) # ChatPhoto print(photo) # ChatPhoto
.. code-block:: json .. code-block:: json
{ {
"_": "pyrogram.ChatPhoto", "_": "ChatPhoto",
"small_file_id": "AQADBAAD8tBgAQAEJjCxGgAEo5IBAAIC", "small_file_id": "AbCdE...EdCbA",
"big_file_id": "AQADBAAD8tBgAQAEJjCxGgAEpZIBAAEBAg" "small_photo_unique_id": "VwXyZ...ZyXwV",
"big_file_id": "AbCdE...EdCbA",
"big_photo_unique_id": "VwXyZ...ZyXwV"
} }
However, the bracket notation ``[]`` is also supported, but its usage is discouraged:
.. warning::
Bracket notation in Python is not commonly used for getting/setting object attributes. While it works for Pyrogram
objects, it might not work for anything else and you should not rely on this.
.. code-block:: python
dan_photo_big = dan["photo"]["big_file_id"]
print(dan_photo_big) # str
.. code-block:: text
AQADBAAD8tBgAQAEJjCxGgAEpZIBAAEBAg
Checking an Object's Type Checking an Object's Type
------------------------- -------------------------
@ -111,8 +91,8 @@ error. The correct way to get the object type is by using the built-in function
.. code-block:: python .. code-block:: python
dan_status = dan.status status = me.status
print(type(dan_status)) print(type(status))
.. code-block:: text .. code-block:: text
@ -125,8 +105,8 @@ And to check if an object is an instance of a given class, you use the built-in
from pyrogram.types import UserStatus from pyrogram.types import UserStatus
dan_status = dan.status status = me.status
print(isinstance(dan_status, UserStatus)) print(isinstance(status, UserStatus))
.. code-block:: text .. code-block:: text

View File

@ -24,7 +24,6 @@ group. Dispatching groups hold one or more handlers and are processed sequential
For example, take these two handlers: For example, take these two handlers:
.. code-block:: python .. code-block:: python
:emphasize-lines: 1, 6
@app.on_message(filters.text | filters.sticker) @app.on_message(filters.text | filters.sticker)
def text_or_sticker(client, message): def text_or_sticker(client, message):

View File

@ -1,10 +1,10 @@
MTProto vs. Bot API MTProto vs. Bot API
=================== ===================
Pyrogram is a framework that acts as a fully-fledged Telegram client based on MTProto, and this very feature makes it Pyrogram is a framework written from the ground up that acts as a fully-fledged Telegram client based on the MTProto
already superior to, what is usually called, the official Bot API, in many respects. This page will therefore show you API. This means that Pyrogram is able to execute any official client and bot API action and more. This page will
why Pyrogram might be a better choice for your project by comparing the two APIs, but first, let's make it clear what therefore show you why Pyrogram might be a better choice for your project by comparing the two APIs, but first, let's
actually is the MTProto and the Bot API. make it clear what actually is the MTProto and the Bot API.
.. contents:: Contents .. contents:: Contents
:backlinks: none :backlinks: none
@ -19,10 +19,11 @@ What is the MTProto API?
`MTProto`_, took alone, is the name of the custom-made, open and encrypted communication protocol created by Telegram `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. 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 as a whole. This API The MTProto API on the other hand, is what people for convenience call the main Telegram API in order to distinguish it
is able to authorize both users and bots and is built on top of the MTProto encryption protocol by means of from the Bot API. The main Telegram API is able to authorize both users and bots and is built on top of the MTProto
`binary data serialized`_ in a specific way, as described by the `TL language`_, and delivered using UDP, TCP or even encryption protocol by means of `binary data serialized`_ in a specific way, as described by the `TL language`_, and
HTTP as transport-layer protocol. 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 .. _MTProto: https://core.telegram.org/mtproto
.. _binary data serialized: https://core.telegram.org/mtproto/serialize .. _binary data serialized: https://core.telegram.org/mtproto/serialize
@ -31,12 +32,12 @@ HTTP as transport-layer protocol.
What is the Bot API? What is the Bot API?
-------------------- --------------------
The `Bot API`_ is an HTTP(S) interface for building normal bots using a sub-set of the main MTProto API. Bots are special The `Bot API`_ is an HTTP(S) interface for building normal bots using a sub-set of the main Telegram API. Bots are
accounts that are authorized via tokens instead of phone numbers. The Bot API is built yet again on top of the main special accounts that are authorized via tokens instead of phone numbers. The Bot API is built yet again on top of the
Telegram API, but runs on an intermediate server application that in turn communicates with the actual Telegram servers main Telegram API, but runs on an intermediate server application that in turn communicates with the actual Telegram
using MTProto. servers using MTProto.
.. figure:: https://i.imgur.com/WvwBoZo.png .. figure:: //_static/img/mtproto-vs-bot-api.png
:align: center :align: center
.. _Bot API: https://core.telegram.org/bots/api .. _Bot API: https://core.telegram.org/bots/api
@ -44,8 +45,8 @@ using MTProto.
Advantages of the MTProto API Advantages of the MTProto API
----------------------------- -----------------------------
Here is a list of all the advantages in using MTProto-based libraries -- such as Pyrogram -- instead of the official Here is a non-exhaustive list of all the advantages in using MTProto-based libraries -- such as Pyrogram -- instead of
HTTP Bot API. Using Pyrogram you can: the official HTTP Bot API. Using Pyrogram you can:
.. hlist:: .. hlist::
:columns: 1 :columns: 1
@ -69,7 +70,7 @@ HTTP Bot API. Using Pyrogram you can:
.. hlist:: .. hlist::
:columns: 1 :columns: 1
- :guilabel:`+` **Run multiple sessions at once, up to 10 per account (either bot or user)** - :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 - :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. bot again in a parallel connection.

View File

@ -14,8 +14,8 @@ non-asynchronous contexts. For more detailed information, you can visit and lear
----- -----
Using ``apscheduler`` Using apscheduler
--------------------- -----------------
- Install with ``pip3 install apscheduler`` - Install with ``pip3 install apscheduler``
- Documentation: https://apscheduler.readthedocs.io - Documentation: https://apscheduler.readthedocs.io

View File

@ -24,8 +24,7 @@ If you want a nicely formatted, human readable JSON representation of any object
... ...
with app: with app:
r = app.get_chat("haskell") r = app.get_chat("me")
print(str(r)) print(str(r))
.. tip:: .. tip::
@ -48,7 +47,7 @@ as the process requires the package to be in scope.
... ...
with app: with app:
r = app.get_chat("haskell") r = app.get_chat("me")
print(repr(r)) print(repr(r))
print(eval(repr(r)) == r) # True print(eval(repr(r)) == r) # True

View File

@ -1,9 +1,9 @@
Smart Plugins Smart Plugins
============= =============
Pyrogram embeds a **smart**, lightweight yet powerful plugin system that is meant to further simplify the organization 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 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**. different Pyrogram applications with minimal boilerplate code.
.. tip:: .. tip::
@ -74,8 +74,8 @@ after importing your modules, like this:
This is already nice and doesn't add *too much* boilerplate code, but things can get boring still; you have to 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 manually ``import``, manually :meth:`~pyrogram.Client.add_handler` and manually instantiate each
:class:`~pyrogram.handlers.MessageHandler` object because **you can't use those cool decorators** for your :class:`~pyrogram.handlers.MessageHandler` object because you can't use decorators for your functions.
functions. So, what if you could? Smart Plugins solve this issue by taking care of handlers registration automatically. So, what if you could? Smart Plugins solve this issue by taking care of handlers registration automatically.
Using Smart Plugins Using Smart Plugins
------------------- -------------------
@ -91,7 +91,6 @@ Setting up your Pyrogram project to accommodate Smart Plugins is pretty straight
This is the same example application as shown above, written using the Smart Plugin system. This is the same example application as shown above, written using the Smart Plugin system.
.. code-block:: text .. code-block:: text
:emphasize-lines: 2, 3
myproject/ myproject/
plugins/ plugins/
@ -102,7 +101,6 @@ Setting up your Pyrogram project to accommodate Smart Plugins is pretty straight
- ``plugins/handlers.py`` - ``plugins/handlers.py``
.. code-block:: python .. code-block:: python
:emphasize-lines: 4, 9
from pyrogram import Client, filters from pyrogram import Client, filters
@ -164,7 +162,7 @@ found inside each module will be, instead, loaded in the order they are defined,
.. note:: .. note::
Remember: there can be at most one handler, within a group, dealing with a specific update. Plugins with overlapping 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. Learn more at :doc:`More on Updates <more-on-updates>`. 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 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`` exclude) and in which exact order to load plugins. The way to do this is to make use of ``include`` and ``exclude``
@ -300,32 +298,30 @@ In the previous section we've explained how to specify which plugins to load and
starts. Here we'll show, instead, how to unload and load again a previously registered plugin at runtime. 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 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 ``handler`` attribute pointing to a tuple of 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. *(handler: Handler, group: int)* is attached to the function object itself.
- ``plugins/handlers.py`` - ``plugins/handlers.py``
.. code-block:: python .. code-block:: python
:emphasize-lines: 5, 6
@Client.on_message(filters.text & filters.private) @Client.on_message(filters.text & filters.private)
def echo(client, message): def echo(client, message):
message.reply(message.text) message.reply(message.text)
print(echo) print(echo)
print(echo.handler) print(echo.handlers)
- Printing ``echo`` will show something like ``<function echo at 0x10e3b6598>``. - Printing ``echo`` will show something like ``<function echo at 0x10e3b6598>``.
- Printing ``echo.handler`` will reveal the handler, that is, a tuple containing the actual handler and the group it - Printing ``echo.handlers`` will reveal the handlers, that is, a list of tuples containing the actual handlers and
was registered on ``(<MessageHandler object at 0x10e3abc50>, 0)``. the groups they were registered on ``[(<MessageHandler object at 0x10e3abc50>, 0)]``.
Unloading 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 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* special attribute preceded by the :meth:`~pyrogram.Client.remove_handler` Client's method with your function's *handler* instance:
star ``*`` operator as argument. Example:
- ``main.py`` - ``main.py``
@ -333,16 +329,19 @@ star ``*`` operator as argument. Example:
from plugins.handlers import echo from plugins.handlers import echo
... handlers = echo.handlers
app.remove_handler(*echo.handler) 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 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: exactly what is needed. The same could have been achieved with:
.. code-block:: python .. code-block:: python
handler, group = echo.handler handlers = echo.handlers
handler, group = handlers[0]
app.remove_handler(handler, group) app.remove_handler(handler, group)
Loading Loading
@ -359,4 +358,7 @@ using :meth:`~pyrogram.Client.add_handler` instead. Example:
... ...
app.add_handler(*echo.handler) handlers = echo.handlers
for h in handlers:
app.add_handler(*h)

View File

@ -18,28 +18,18 @@ Persisting Sessions
In order to make a client reconnect successfully between restarts, that is, without having to start a new 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. authorization process from scratch each time, Pyrogram needs to store the generated session data somewhere.
Other useful data being stored is peers' cache. In short, peers are all those entities you can chat with, such as users
or bots, basic groups, but also channels and supergroups. Because of how Telegram works, a unique pair of **id** and
**access_hash** is needed to contact a peer. This, plus other useful info such as the peer type, is what is stored
inside a session storage.
So, if you ever wondered how is Pyrogram able to contact peers just by asking for their ids, it's because of this very
reason: the peer *id* is looked up in the internal database and the available *access_hash* is retrieved, which is then
used to correctly invoke API methods.
Different Storage Engines Different Storage Engines
------------------------- -------------------------
Let's now talk about how Pyrogram actually stores all the relevant data. Pyrogram offers two different types of storage Pyrogram offers two different types of storage engines: a **File Storage** and a **Memory Storage**.
engines: a **File Storage** and a **Memory Storage**. These engines are well integrated in the library and require a These engines are well integrated in the framework and require a minimal effort to set up. Here's how they work:
minimal effort to set up. Here's how they work:
File Storage File Storage
^^^^^^^^^^^^ ^^^^^^^^^^^^
This is the most common storage engine. It is implemented by using **SQLite**, which will store the session and peers This is the most common storage engine. It is implemented by using **SQLite**, which will store the session details.
details. The database will be saved to disk as a single portable file and is designed to efficiently store and retrieve The database will be saved to disk as a single portable file and is designed to efficiently store and retrieve
peers whenever they are needed. data whenever they are needed.
To use this type of engine, simply pass any name of your choice to the ``session_name`` parameter of the To use this type of engine, simply pass any name of your choice to the ``session_name`` parameter of the
:obj:`~pyrogram.Client` constructor, as usual: :obj:`~pyrogram.Client` constructor, as usual:
@ -68,8 +58,8 @@ session name "**:memory:**" to the ``session_name`` parameter of the :obj:`~pyro
with Client(":memory:") as app: with Client(":memory:") as app:
print(app.get_me()) print(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 This storage engine is still backed by SQLite, but the database exists purely in memory. This means that, once you stop
client, the entire database is discarded and the session details used for logging in again will be lost forever. a client, the entire database is discarded and the session details used for logging in again will be lost forever.
Session Strings Session Strings
--------------- ---------------
@ -84,8 +74,8 @@ In case you want to use an in-memory storage, but also want to keep access to th
with Client(":memory:") as app: with Client(":memory:") as app:
print(app.export_session_string()) print(app.export_session_string())
...and save the resulting (quite long) string somewhere. You can use this string as session name the next time you want ...and save the resulting string. You can use this string as session name the next time you want to login
to login using the same session; the storage used will still be completely in-memory: using the same session; the storage used will still be in-memory:
.. code-block:: python .. code-block:: python
@ -96,11 +86,5 @@ to login using the same session; the storage used will still be completely in-me
with Client(session_string) as app: with Client(session_string) as app:
print(app.get_me()) print(app.get_me())
Session strings are useful when you want to run authorized Pyrogram clients on platforms like Session strings are useful when you want to run authorized Pyrogram clients on platforms where their ephemeral
`Heroku <https://www.heroku.com/>`_, where their ephemeral filesystems makes it much harder for a file-based storage filesystems makes it harder for a file-based storage engine to properly work as intended.
engine to properly work as intended.
But, why is the session string so long? Can't it be shorter? No, it can't. The session string already packs the bare
minimum data Pyrogram needs to successfully reconnect to an authorized session, and the 2048-bits auth key is the major
contributor to the overall length. Needless to say that this string, as well as any other session storage, represent
strictly personal data. Keep them safe.

View File

@ -15,8 +15,7 @@ Telegram's test servers without hassle. All you need to do is start a new sessio
.. note:: .. note::
If this is the first time you login into test servers, you will be asked to register your account first. If this is the first time you login into test servers, you will be asked to register your account first.
Don't worry about your contacts and chats, they will be kept untouched inside the production environment; Accounts registered on test servers reside in a different, parallel instance of a Telegram server.
accounts authorized on test servers reside in a different, parallel instance of a Telegram database.
.. contents:: Contents .. contents:: Contents
:backlinks: none :backlinks: none
@ -28,19 +27,15 @@ Telegram's test servers without hassle. All you need to do is start a new sessio
Test Mode in Official Apps Test Mode in Official Apps
-------------------------- --------------------------
You can also login yourself into test servers using official desktop apps, such as Webogram and TDesktop: You can also login yourself into test servers using official desktop apps, such as Telegram Web and Telegram Desktop:
- **Webogram**: Login here: https://web.telegram.org/?test=1 - **Telegram Web**: Login here: https://web.telegram.org/?test=1
- **TDesktop**: Hold ``Alt+Shift`` and right click on "Add account", then choose "Test server". - **Telegram Desktop**: Hold ``Alt+Shift`` and right click on "Add account", then choose "Test server".
Test Numbers Test Numbers
------------ ------------
Beside normal numbers, the test environment allows you to login with reserved 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 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`` as the confirmation code (the DC number, repeated five times). numbers. Users with such numbers always get ``XXXXX`` or ``XXXXXX`` as the confirmation code (the DC number, repeated
five or six times).
.. important::
Do not store any important or private information in such test users' accounts; anyone can make use of the
simplified authorization mechanism and login at any time.

View File

@ -14,7 +14,7 @@ Text Formatting
:class: strike-italic :class: strike-italic
Pyrogram uses a custom Markdown dialect for text formatting which adds some unique features that make writing styled 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 great 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. variety of decorations that can also be nested in order to combine multiple styles together.
.. contents:: Contents .. contents:: Contents
@ -36,7 +36,7 @@ list of the basic styles currently supported by Pyrogram.
- :underline:`underline` - :underline:`underline`
- spoiler - spoiler
- `text URL <https://pyrogram.org>`_ - `text URL <https://pyrogram.org>`_
- `user text mention <https://t.me/haskell>`_ - `user text mention <tg://user?id=123456789>`_
- ``inline fixed-width code`` - ``inline fixed-width code``
- .. code-block:: text - .. code-block:: text
@ -66,9 +66,9 @@ To strictly use this mode, pass "markdown" to the *parse_mode* parameter when us
||spoiler|| ||spoiler||
[text URL](https://docs.pyrogram.org/) [text URL](https://pyrogram.org/)
[text user mention](tg://user?id=23122162) [text user mention](tg://user?id=123456789)
`inline fixed-width code` `inline fixed-width code`
@ -83,14 +83,13 @@ To strictly use this mode, pass "markdown" to the *parse_mode* parameter when us
.. code-block:: python .. code-block:: python
app.send_message( app.send_message(
"haskell", "me",
( (
"**bold**, " "**bold**, "
"__italic__, " "__italic__, "
"--underline--, " "--underline--, "
"~~strike~~, " "~~strike~~, "
"||spoiler||, " "||spoiler||, "
"[mention](tg://user?id=23122162), "
"[URL](https://pyrogram.org), " "[URL](https://pyrogram.org), "
"`code`, " "`code`, "
"```" "```"
@ -119,9 +118,9 @@ The following tags are currently supported:
<spoiler>spoiler</spoiler> <spoiler>spoiler</spoiler>
<a href="http://docs.pyrogram.org/">text URL</a> <a href="https://pyrogram.org/">text URL</a>
<a href="tg://user?id=23122162">inline mention</a> <a href="tg://user?id=123456789">inline mention</a>
<code>inline fixed-width code</code> <code>inline fixed-width code</code>
@ -136,14 +135,13 @@ The following tags are currently supported:
.. code-block:: python .. code-block:: python
app.send_message( app.send_message(
"haskell", "me",
( (
"<b>bold</b>, " "<b>bold</b>, "
"<i>italic</i>, " "<i>italic</i>, "
"<u>underline</u>, " "<u>underline</u>, "
"<s>strike</s>, " "<s>strike</s>, "
"<spoiler>spoiler</spoiler>, " "<spoiler>spoiler</spoiler>, "
"<a href=\"tg://user?id=23122162\">mention</a>, "
"<a href=\"https://pyrogram.org/\">URL</a>, " "<a href=\"https://pyrogram.org/\">URL</a>, "
"<code>code</code>\n\n" "<code>code</code>\n\n"
"<pre>" "<pre>"
@ -181,7 +179,7 @@ This means you can combine together both syntaxes in the same text:
.. code-block:: python .. code-block:: python
app.send_message("haskell", "**bold**, <i>italic</i>") app.send_message("me", "**bold**, <i>italic</i>")
Result: Result:
@ -192,8 +190,8 @@ If you don't like this behaviour you can always choose to only enable either Mar
.. code-block:: .. code-block::
app.send_message("haskell", "**bold**, <i>italic</i>", parse_mode="markdown") app.send_message("me", "**bold**, <i>italic</i>", parse_mode="markdown")
app.send_message("haskell", "**bold**, <i>italic</i>", parse_mode="html") app.send_message("me", "**bold**, <i>italic</i>", parse_mode="html")
Result: Result:
@ -206,7 +204,7 @@ as-is.
.. code-block:: python .. code-block:: python
app.send_message("haskell", "**bold**, <i>italic</i>", parse_mode=None) app.send_message("me", "**bold**, <i>italic</i>", parse_mode=None)
Result: Result:

View File

@ -1,11 +1,11 @@
Fast Crypto Fast Crypto
=========== ===========
Pyrogram's speed can be *dramatically* boosted up by TgCrypto_, a high-performance, easy-to-install Telegram Crypto Pyrogram's speed can be boosted up by TgCrypto_, a high-performance, easy-to-install cryptography library specifically
Library specifically written in C for Pyrogram [1]_ as a Python extension. written in C for Pyrogram as a Python extension.
TgCrypto is a replacement for the much slower PyAES and implements the crypto algorithms Telegram requires, namely TgCrypto is a replacement for a slower Python-only alternative and implements the cryptographic algorithms Telegram
**AES-IGE 256 bit** (used in MTProto v2.0) and **AES-CTR 256 bit** (used for CDN encrypted files). requires, namely: AES-256-IGE, AES-256-CTR and AES-256-CBC.
Installation Installation
------------ ------------
@ -14,10 +14,10 @@ Installation
$ pip3 install -U tgcrypto $ pip3 install -U tgcrypto
.. note:: Being a C extension for Python, TgCrypto is an optional but *highly recommended* dependency; when TgCrypto is .. note:: When TgCrypto is not detected in your system, Pyrogram will automatically fall back to a slower Python-only
not detected in your system, Pyrogram will automatically fall back to PyAES and will show you a warning. implementation and will show you a warning.
The reason about being an optional package is that TgCrypto requires some extra system tools in order to be compiled. The reason about being an optional package is that TgCrypto requires extra system tools in order to be compiled.
The errors you receive when trying to install TgCrypto are system dependent, but also descriptive enough to understand The errors you receive when trying to install TgCrypto are system dependent, but also descriptive enough to understand
what you should do next: what you should do next:
@ -27,6 +27,3 @@ what you should do next:
- **Termux**: Install ``clang`` package. - **Termux**: Install ``clang`` package.
.. _TgCrypto: https://github.com/pyrogram/tgcrypto .. _TgCrypto: https://github.com/pyrogram/tgcrypto
.. [1] Although TgCrypto is intended for Pyrogram, it is shipped as a standalone package and can thus be used for
other Python projects too.

View File

@ -19,16 +19,15 @@ Single Filters
Let's start right away with a simple example: Let's start right away with a simple example:
- This example will show you how to **only** handle messages containing an :class:`~pyrogram.Audio` object and - 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: ignore any other message. Filters are passed as the first argument of the decorator:
.. code-block:: python .. code-block:: python
:emphasize-lines: 4
from pyrogram import filters from pyrogram import filters
@app.on_message(filters.audio) @app.on_message(filters.sticker)
def my_handler(client, message): def my_handler(client, message):
print(message) print(message)
@ -36,7 +35,6 @@ Let's start right away with a simple example:
callback function itself: callback function itself:
.. code-block:: python .. code-block:: python
:emphasize-lines: 9
from pyrogram import filters from pyrogram import filters
from pyrogram.handlers import MessageHandler from pyrogram.handlers import MessageHandler
@ -46,12 +44,12 @@ Let's start right away with a simple example:
print(message) print(message)
app.add_handler(MessageHandler(my_handler, filters.audio)) app.add_handler(MessageHandler(my_handler, filters.sticker))
Combining Filters Combining Filters
----------------- -----------------
Filters can also be used in a more advanced way by inverting and combining more filters together using bitwise Filters can be used in a more advanced way by inverting and combining more filters together using bitwise
operators ``~``, ``&`` and ``|``: operators ``~``, ``&`` and ``|``:
- Use ``~`` to invert a filter (behaves like the ``not`` operator). - Use ``~`` to invert a filter (behaves like the ``not`` operator).

View File

@ -1,8 +1,8 @@
Voice Calls Voice Calls
=========== ===========
Both private voice calls and group voice calls are currently supported by third-party libraries that integrate with Both private voice calls and group voice calls are currently supported by third-party, external libraries that integrate
Pyrogram. with Pyrogram.
Libraries Libraries
--------- ---------

View File

@ -32,6 +32,7 @@ from pathlib import Path
from typing import Union, List, Optional from typing import Union, List, Optional
import pyrogram import pyrogram
from pyrogram import __version__, __license__
from pyrogram import raw from pyrogram import raw
from pyrogram import utils from pyrogram import utils
from pyrogram.crypto import aes from pyrogram.crypto import aes
@ -281,6 +282,10 @@ class Client(Methods, Scaffold):
if self.bot_token: if self.bot_token:
return await self.sign_in_bot(self.bot_token) return await self.sign_in_bot(self.bot_token)
print(f"Welcome to Pyrogram (version {__version__})")
print(f"Pyrogram is free software and comes with ABSOLUTELY NO WARRANTY. Licensed\n"
f"under the terms of the {__license__}.\n")
while True: while True:
try: try:
if not self.phone_number: if not self.phone_number:
@ -428,7 +433,6 @@ class Client(Methods, Scaffold):
Example: Example:
.. code-block:: python .. code-block:: python
:emphasize-lines: 10,14,18,22
from pyrogram import Client from pyrogram import Client
@ -436,23 +440,23 @@ class Client(Methods, Scaffold):
with app: with app:
# Default combined mode: Markdown + HTML # Default combined mode: Markdown + HTML
app.send_message("haskell", "1. **markdown** and <i>html</i>") app.send_message("me", "1. **markdown** and <i>html</i>")
# Force Markdown-only, HTML is disabled # Force Markdown-only, HTML is disabled
app.set_parse_mode("markdown") app.set_parse_mode("markdown")
app.send_message("haskell", "2. **markdown** and <i>html</i>") app.send_message("me", "2. **markdown** and <i>html</i>")
# Force HTML-only, Markdown is disabled # Force HTML-only, Markdown is disabled
app.set_parse_mode("html") app.set_parse_mode("html")
app.send_message("haskell", "3. **markdown** and <i>html</i>") app.send_message("me", "3. **markdown** and <i>html</i>")
# Disable the parser completely # Disable the parser completely
app.set_parse_mode(None) app.set_parse_mode(None)
app.send_message("haskell", "4. **markdown** and <i>html</i>") app.send_message("me", "4. **markdown** and <i>html</i>")
# Bring back the default combined mode # Bring back the default combined mode
app.set_parse_mode() app.set_parse_mode()
app.send_message("haskell", "5. **markdown** and <i>html</i>") app.send_message("me", "5. **markdown** and <i>html</i>")
""" """
self.parse_mode = parse_mode self.parse_mode = parse_mode

View File

@ -17,7 +17,6 @@
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>. # along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import logging import logging
from typing import Optional from typing import Optional
from .tcp import TCP from .tcp import TCP

View File

@ -17,9 +17,9 @@
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>. # along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import logging import logging
from typing import Optional
from binascii import crc32 from binascii import crc32
from struct import pack, unpack from struct import pack, unpack
from typing import Optional
from .tcp import TCP from .tcp import TCP

View File

@ -17,8 +17,8 @@
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>. # along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import logging import logging
from typing import Optional
from struct import pack, unpack from struct import pack, unpack
from typing import Optional
from .tcp import TCP from .tcp import TCP

View File

@ -18,8 +18,8 @@
import logging import logging
import os import os
from typing import Optional
from struct import pack, unpack from struct import pack, unpack
from typing import Optional
from pyrogram.crypto import aes from pyrogram.crypto import aes
from .tcp import TCP from .tcp import TCP

View File

@ -39,7 +39,7 @@ class RPCError(Exception):
is_unknown: bool = False, is_unknown: bool = False,
is_signed: bool = False is_signed: bool = False
): ):
super().__init__("[{}{} {}]: {} {}".format( super().__init__("Telegram says: [{}{} {}] - {} {}".format(
"-" if is_signed else "", "-" if is_signed else "",
self.CODE, self.CODE,
self.ID or self.NAME, self.ID or self.NAME,

View File

@ -934,12 +934,3 @@ class chat(Filter, set):
and message.from_user and message.from_user
and message.from_user.is_self and message.from_user.is_self
and not message.outgoing))) and not message.outgoing)))
# region dan_filter
async def dan_filter(_, __, m: Message):
return bool(m.from_user and m.from_user.id == 23122162)
dan = create(dan_filter)
# endregion

View File

@ -23,8 +23,8 @@ from .get_inline_bot_results import GetInlineBotResults
from .request_callback_answer import RequestCallbackAnswer from .request_callback_answer import RequestCallbackAnswer
from .send_game import SendGame from .send_game import SendGame
from .send_inline_bot_result import SendInlineBotResult from .send_inline_bot_result import SendInlineBotResult
from .set_game_score import SetGameScore
from .set_bot_commands import SetBotCommands from .set_bot_commands import SetBotCommands
from .set_game_score import SetGameScore
class Bots( class Bots(

View File

@ -47,8 +47,8 @@ class GetChatMember(Scaffold):
Example: Example:
.. code-block:: python .. code-block:: python
dan = app.get_chat_member("pyrogramchat", "haskell") member = app.get_chat_member(chat_id, "me")
print(dan) print(member)
""" """
chat = await self.resolve_peer(chat_id) chat = await self.resolve_peer(chat_id)
user = await self.resolve_peer(user_id) user = await self.resolve_peer(user_id)

View File

@ -57,16 +57,17 @@ class GetChatMembers(Scaffold):
offset (``int``, *optional*): offset (``int``, *optional*):
Sequential number of the first member to be returned. Sequential number of the first member to be returned.
Only applicable to supergroups and channels. Defaults to 0 [1]_. Only applicable to supergroups and channels. Defaults to 0.
limit (``int``, *optional*): limit (``int``, *optional*):
Limits the number of members to be retrieved. Limits the number of members to be retrieved.
Only applicable to supergroups and channels. Only applicable to supergroups and channels.
Defaults to 200, which is also the maximum server limit allowed per method call. Defaults to 200.
query (``str``, *optional*): query (``str``, *optional*):
Query string to filter members based on their display names and usernames. Query string to filter members based on their display names and usernames.
Only applicable to supergroups and channels. Defaults to "" (empty string) [2]_. Only applicable to supergroups and channels. Defaults to "" (empty string).
A query string is applicable only for *"all"*, *"banned"* and *"restricted"* filters only
filter (``str``, *optional*): filter (``str``, *optional*):
Filter used to select the kind of members you want to retrieve. Only applicable for supergroups Filter used to select the kind of members you want to retrieve. Only applicable for supergroups
@ -80,11 +81,6 @@ class GetChatMembers(Scaffold):
Only applicable to supergroups and channels. Only applicable to supergroups and channels.
Defaults to *"recent"*. Defaults to *"recent"*.
.. [1] Server limit: on supergroups, you can get up to 10,000 members for a single query and up to 200 members
on channels.
.. [2] A query string is applicable only for *"all"*, *"banned"* and *"restricted"* filters only.
Returns: Returns:
List of :obj:`~pyrogram.types.ChatMember`: On success, a list of chat members is returned. List of :obj:`~pyrogram.types.ChatMember`: On success, a list of chat members is returned.
@ -95,13 +91,13 @@ class GetChatMembers(Scaffold):
.. code-block:: python .. code-block:: python
# Get first 200 recent members # Get first 200 recent members
app.get_chat_members("pyrogramchat") app.get_chat_members(chat_id)
# Get all administrators # Get all administrators
app.get_chat_members("pyrogramchat", filter="administrators") app.get_chat_members(chat_id, filter="administrators")
# Get all bots # Get all bots
app.get_chat_members("pyrogramchat", filter="bots") app.get_chat_members(chat_id, filter="bots")
""" """
peer = await self.resolve_peer(chat_id) peer = await self.resolve_peer(chat_id)

View File

@ -42,7 +42,7 @@ class GetChatMembersCount(Scaffold):
Example: Example:
.. code-block:: python .. code-block:: python
count = app.get_chat_members_count("pyrogramchat") count = app.get_chat_members_count(chat_id)
print(count) print(count)
""" """
peer = await self.resolve_peer(chat_id) peer = await self.resolve_peer(chat_id)

View File

@ -16,7 +16,6 @@
# 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/>.
from string import ascii_lowercase
from typing import Union, AsyncGenerator, Optional from typing import Union, AsyncGenerator, Optional
from pyrogram import raw from pyrogram import raw
@ -33,10 +32,6 @@ class Filters:
ADMINISTRATORS = "administrators" ADMINISTRATORS = "administrators"
QUERIES = [""] + [str(i) for i in range(10)] + list(ascii_lowercase)
QUERYABLE_FILTERS = (Filters.ALL, Filters.BANNED, Filters.RESTRICTED)
class IterChatMembers(Scaffold): class IterChatMembers(Scaffold):
async def iter_chat_members( async def iter_chat_members(
self, self,
@ -57,11 +52,11 @@ class IterChatMembers(Scaffold):
limit (``int``, *optional*): limit (``int``, *optional*):
Limits the number of members to be retrieved. Limits the number of members to be retrieved.
By default, no limit is applied and all members are returned [1]_.
query (``str``, *optional*): query (``str``, *optional*):
Query string to filter members based on their display names and usernames. Query string to filter members based on their display names and usernames.
Defaults to "" (empty string) [2]_. Defaults to "" (empty string).
A query string is applicable only for *"all"*, *"banned"* and *"restricted"* filters only.
filter (``str``, *optional*): filter (``str``, *optional*):
Filter used to select the kind of members you want to retrieve. Only applicable for supergroups Filter used to select the kind of members you want to retrieve. Only applicable for supergroups
@ -74,11 +69,6 @@ class IterChatMembers(Scaffold):
*"administrators"* - chat administrators only. *"administrators"* - chat administrators only.
Defaults to *"recent"*. Defaults to *"recent"*.
.. [1] Server limit: on supergroups, you can get up to 10,000 members for a single query and up to 200 members
on channels.
.. [2] A query string is applicable only for *"all"*, *"banned"* and *"restricted"* filters only.
Returns: Returns:
``Generator``: A generator yielding :obj:`~pyrogram.types.ChatMember` objects. ``Generator``: A generator yielding :obj:`~pyrogram.types.ChatMember` objects.
@ -86,28 +76,22 @@ class IterChatMembers(Scaffold):
.. code-block:: python .. code-block:: python
# Iterate though all chat members # Iterate though all chat members
for member in app.iter_chat_members("pyrogramchat"): for member in app.iter_chat_members(chat_id):
print(member.user.first_name) print(member.user.first_name)
# Iterate though all administrators # Iterate though all administrators
for member in app.iter_chat_members("pyrogramchat", filter="administrators"): for member in app.iter_chat_members(chat_id, filter="administrators"):
print(member.user.first_name) print(member.user.first_name)
# Iterate though all bots # Iterate though all bots
for member in app.iter_chat_members("pyrogramchat", filter="bots"): for member in app.iter_chat_members(chat_id, filter="bots"):
print(member.user.first_name) print(member.user.first_name)
""" """
current = 0 current = 0
yielded = set() yielded = set()
queries = [query] if query else QUERIES
total = limit or (1 << 31) - 1 total = limit or (1 << 31) - 1
limit = min(200, total) limit = min(200, total)
resolved_chat_id = await self.resolve_peer(chat_id) resolved_chat_id = await self.resolve_peer(chat_id)
if filter not in QUERYABLE_FILTERS:
queries = [""]
for q in queries:
offset = 0 offset = 0
while True: while True:
@ -115,7 +99,7 @@ class IterChatMembers(Scaffold):
chat_id=chat_id, chat_id=chat_id,
offset=offset, offset=offset,
limit=limit, limit=limit,
query=q, query=query,
filter=filter filter=filter
) )

View File

@ -52,7 +52,7 @@ class SetAdministratorTitle(Scaffold):
Example: Example:
.. code-block:: python .. code-block:: python
app.set_administrator_title(chat_id, user_id, "ฅ^•ﻌ•^ฅ") app.set_administrator_title(chat_id, user_id, "Admin Title")
""" """
chat_id = await self.resolve_peer(chat_id) chat_id = await self.resolve_peer(chat_id)
user_id = await self.resolve_peer(user_id) user_id = await self.resolve_peer(user_id)

View File

@ -45,10 +45,10 @@ class SetSlowMode(Scaffold):
.. code-block:: python .. code-block:: python
# Set slow mode to 60 seconds # Set slow mode to 60 seconds
app.set_slow_mode("pyrogramchat", 60) app.set_slow_mode(chat_id, 60)
# Disable slow mode # Disable slow mode
app.set_slow_mode("pyrogramchat", None) app.set_slow_mode(chat_id, None)
""" """
await self.send( await self.send(

View File

@ -38,7 +38,7 @@ class AddContact(Scaffold):
user_id (``int`` | ``str``): user_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the target user. Unique identifier (int) or username (str) of the target user.
first_name (``str``, *optional*): first_name (``str``):
User's first name. User's first name.
last_name (``str``, *optional*): last_name (``str``, *optional*):

View File

@ -43,9 +43,9 @@ class ImportContacts(Scaffold):
from pyrogram.types import InputPhoneContact from pyrogram.types import InputPhoneContact
app.import_contacts([ app.import_contacts([
InputPhoneContact("39123456789", "Foo"), InputPhoneContact("+1-123-456-7890", "Foo"),
InputPhoneContact("38987654321", "Bar"), InputPhoneContact("+1-456-789-0123", "Bar"),
InputPhoneContact("01234567891", "Baz")]) InputPhoneContact("+1-789-012-3456", "Baz")])
""" """
imported_contacts = await self.send( imported_contacts = await self.send(
raw.functions.contacts.ImportContacts( raw.functions.contacts.ImportContacts(

View File

@ -16,7 +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/>.
from typing import Union, List, Optional from typing import Union, List
from pyrogram import types, utils, raw from pyrogram import types, utils, raw
from pyrogram.scaffold import Scaffold from pyrogram.scaffold import Scaffold
@ -103,7 +103,8 @@ class CopyMediaGroup(Scaffold):
**await self.parser.parse( **await self.parser.parse(
captions[i] if isinstance(captions, list) and i < len(captions) and captions[i] else captions[i] if isinstance(captions, list) and i < len(captions) and captions[i] else
captions if isinstance(captions, str) and i == 0 else captions if isinstance(captions, str) and i == 0 else
message.caption if message.caption and message.caption != "None" and not type(captions) is str else "") message.caption if message.caption and message.caption != "None" and not type(
captions) is str else "")
) )
) )

View File

@ -92,7 +92,7 @@ class DownloadMedia(Scaffold):
app.download_media(message) app.download_media(message)
# Download from file id # Download from file id
app.download_media("CAADBAADzg4AAvLQYAEz_x2EOgdRwBYE") app.download_media(message.photo.file_id)
# Keep track of the progress while downloading # Keep track of the progress while downloading
def progress(current, total): def progress(current, total):

View File

@ -67,7 +67,6 @@ class ForwardMessages(Scaffold):
Example: Example:
.. code-block:: python .. code-block:: python
:emphasize-lines: 2,5
# Forward a single message # Forward a single message
app.forward_messages("me", "pyrogram", 20) app.forward_messages("me", "pyrogram", 20)

View File

@ -72,13 +72,13 @@ class GetHistory(Scaffold):
.. code-block:: python .. code-block:: python
# Get the last 100 messages of a chat # Get the last 100 messages of a chat
app.get_history("pyrogramchat") app.get_history(chat_id)
# Get the last 3 messages of a chat # Get the last 3 messages of a chat
app.get_history("pyrogramchat", limit=3) app.get_history(chat_id, limit=3)
# Get 3 messages after skipping the first 5 # Get 3 messages after skipping the first 5
app.get_history("pyrogramchat", offset=5, limit=3) app.get_history(chat_id, offset=5, limit=3)
""" """
offset_id = offset_id or (1 if reverse else 0) offset_id = offset_id or (1 if reverse else 0)

View File

@ -48,7 +48,7 @@ class GetHistoryCount(Scaffold):
Example: Example:
.. code-block:: python .. code-block:: python
app.get_history_count("pyrogramchat") app.get_history_count(chat_id)
""" """
r = await self.send( r = await self.send(

View File

@ -66,7 +66,7 @@ class GetMediaGroup(Scaffold):
# There can be maximum 10 items in a media group. # There can be maximum 10 items in a media group.
# The if/else condition to fix the problem of getting correct `media_group_id` when it has `message_id` less then 10. # The if/else condition to fix the problem of getting correct `media_group_id` when it has `message_id` less then 10.
media_group_id = messages[9].media_group_id if len(messages) == 19 else messages[message_id-1].media_group_id media_group_id = messages[9].media_group_id if len(messages) == 19 else messages[message_id - 1].media_group_id
if media_group_id is None: if media_group_id is None:
raise ValueError("The message doesn't belong to a media group") raise ValueError("The message doesn't belong to a media group")

View File

@ -71,10 +71,10 @@ class GetMessages(Scaffold):
.. code-block:: python .. code-block:: python
# Get one message # Get one message
app.get_messages("pyrogramchat", 51110) app.get_messages(chat_id, 12345)
# Get more than one message (list of messages) # Get more than one message (list of messages)
app.get_messages("pyrogramchat", [44625, 51110]) app.get_messages(chat_id, [12345, 12346])
# Get message by ignoring any replied-to message # Get message by ignoring any replied-to message
app.get_messages(chat_id, message_id, replies=0) app.get_messages(chat_id, message_id, replies=0)

View File

@ -47,10 +47,10 @@ class ReadHistory(Scaffold):
.. code-block:: python .. code-block:: python
# Mark the whole chat as read # Mark the whole chat as read
app.read_history("pyrogramlounge") app.read_history(chat_id)
# Mark messages as read only up to the given message id # Mark messages as read only up to the given message id
app.read_history("pyrogramlounge", 123456) app.read_history(chat_id, 123456)
""" """
peer = await self.resolve_peer(chat_id) peer = await self.resolve_peer(chat_id)

Some files were not shown because too many files have changed in this diff Show More