2
0
mirror of https://github.com/pyrogram/pyrogram synced 2025-08-28 12:57:52 +00:00

Merge pull request #51 from pyrogram/new-api

API Revamp
This commit is contained in:
Dan 2018-04-18 15:53:42 +02:00 committed by GitHub
commit f47ee057e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
78 changed files with 3434 additions and 1291 deletions

View File

@ -1,158 +1,79 @@
|header|
Table of Contents
=================
Pyrogram |twitter|
==================
- `About`_
.. code-block:: python
- `Features`_
from pyrogram import Client, Filters
- `Requirements`_
- `Getting Started`_
- `Installation`_
- `Configuration`_
- `Usage`_
- `Documentation`_
- `Contribution`_
- `Feedback`_
- `License`_
app = Client("my_account")
About
=====
@app.on_message(Filters.private)
def hello(client, message):
client.send_message(
message.chat.id, "Hello {}".format(message.from_user.first_name))
app.start()
app.idle()
**Pyrogram** is a brand new Telegram_ Client Library written from the ground up in Python and C. It can be used for building
custom Telegram applications in Python that interact with the MTProto API as both User and Bot.
custom Telegram applications that interact with the MTProto API as both User and Bot.
Features
--------
- **Easy to setup**: Pyrogram can be easily installed using pip and requires very few lines of code to get started with.
- **Easy to use**: Pyrogram provides idiomatic, clean and readable Python code making the Telegram API simple to use.
- **High-level**: Pyrogram automatically handles all the low-level details of communication with Telegram servers.
- **Updated**: Pyrogram makes use of the latest Telegram MTProto API version, currently Layer 76.
- **Fast**: Pyrogram critical parts are boosted up by `TgCrypto`_, a high-performance Crypto Library written in pure C.
- **Documented**: Pyrogram API methods are documented and resemble the well established Telegram Bot API,
thus offering a familiar look to Bot developers.
- **Full API support**: Beside the simple Bot API-like methods, Pyrogram also provides an easy access to every single
Telegram MTProto API method allowing you to programmatically execute any action an official client is able to do, and more.
- **Easy to use**: You can easily install Pyrogram using pip and start building your app right away.
- **High-level**: The low-level details of MTProto are abstracted and automatically handled.
- **Fast**: Crypto parts are boosted up by TgCrypto_, a high-performance library written in pure C.
- **Updated** to the latest Telegram API version, currently Layer 76 running on MTProto 2.0.
- **Documented**: Pyrogram API methods are documented and resemble the Telegram Bot API.
- **Full API**, allowing to execute any advanced action an official client is able to do, and more.
Requirements
------------
- Python 3.4 or higher.
- A `Telegram API key`_.
- A Telegram API key.
Installing
----------
.. code:: shell
pip3 install pyrogram
Getting Started
===============
---------------
Installation
- The Docs contain lots of resources to help you getting started with Pyrogram: https://docs.pyrogram.ml.
- Reading Examples_ in this repository is also a good way for learning how things work.
- Seeking extra help? Don't be shy, come join and ask our Community_!
- For other requests you can send an Email_ or a Message_.
Contributing
------------
- You can install and upgrade Pyrogram using pip:
.. code:: shell
$ pip3 install --upgrade pyrogram
Configuration
-------------
- Create a new ``config.ini`` file at the root of your working directory, copy-paste
the following and replace the **api_id** and **api_hash** values with `your own`_:
.. code:: ini
[pyrogram]
api_id = 12345
api_hash = 0123456789abcdef0123456789abcdef
Usage
-----
- And here is how Pyrogram looks like:
.. code:: python
from pyrogram import Client
client = Client("example")
client.start()
client.send_message("me", "Hi there! I'm using Pyrogram")
client.stop()
That's all you need for getting started with Pyrogram. For more detailed information,
please refer to the Documentation_ and the Examples_ folder.
Documentation
=============
- The entire Pyrogram documentation resides at https://docs.pyrogram.ml.
Contribution
============
Pyrogram is brand new! **You are welcome to try it and help make it better** by either submitting pull
requests or reporting issues/bugs as well as suggesting best practices, ideas, enhancements on both code
and documentation. Any help is appreciated!
Feedback
========
Means for getting in touch:
- `Community`_
- `GitHub`_
- `Email`_
License
=======
Copyright & License
-------------------
- Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
- Licensed under the terms of the
`GNU Lesser General Public License v3 or later (LGPLv3+)`_
- Licensed under the terms of the `GNU Lesser General Public License v3 or later (LGPLv3+)`_
.. _`Telegram`: https://telegram.org/
.. _`your own`: https://docs.pyrogram.ml/start/ProjectSetup#api-keys
.. _`Examples`: https://github.com/pyrogram/pyrogram/blob/master/examples/README.md
.. _`Telegram API key`: https://docs.pyrogram.ml/start/ProjectSetup#api-keys
.. _`Community`: https://t.me/PyrogramChat
.. _`bot-like`: https://core.telegram.org/bots/api#available-methods
.. _`Examples`: https://github.com/pyrogram/pyrogram/tree/master/examples
.. _`GitHub`: https://github.com/pyrogram/pyrogram/issues
.. _`Email`: admin@pyrogram.ml
.. _`Message`: https://t.me/haskell
.. _TgCrypto: https://github.com/pyrogram/tgcrypto
.. _`GNU Lesser General Public License v3 or later (LGPLv3+)`: COPYING.lesser
.. |header| raw:: html
@ -190,6 +111,9 @@ License
</a>
</p>
.. |twitter| image:: https://media.pyrogram.ml/images/twitter.svg
:target: https://twitter.com/intent/tweet?text=Build%20custom%20Telegram%20applications%20with%20Pyrogram&url=https://github.com/pyrogram/pyrogram&hashtags=Telegram,MTProto,Python
.. |logo| image:: https://pyrogram.ml/images/logo.png
:target: https://pyrogram.ml
:alt: Pyrogram

View File

@ -22,20 +22,93 @@ import shutil
HOME = "compiler/api"
DESTINATION = "pyrogram/api"
notice_path = "NOTICE"
NOTICE_PATH = "NOTICE"
SECTION_RE = re.compile(r"---(\w+)---")
LAYER_RE = re.compile(r"//\sLAYER\s(\d+)")
COMBINATOR_RE = re.compile(r"^([\w.]+)#([0-9a-f]+)\s(?:.*)=\s([\w<>.]+);$", re.MULTILINE)
COMBINATOR_RE = re.compile(r"^([\w.]+)#([0-9a-f]+)\s(?:.*)=\s([\w<>.]+);(?: // Docs: (.+))?$", re.MULTILINE)
ARGS_RE = re.compile("[^{](\w+):([\w?!.<>]+)")
FLAGS_RE = re.compile(r"flags\.(\d+)\?")
FLAGS_RE_2 = re.compile(r"flags\.(\d+)\?([\w<>.]+)")
FLAGS_RE_3 = re.compile(r"flags:#")
INT_RE = re.compile(r"int(\d+)")
core_types = ["int", "long", "int128", "int256", "double", "bytes", "string", "Bool"]
types_to_constructors = {}
types_to_functions = {}
constructors_to_functions = {}
def get_docstring_arg_type(t: str, is_list: bool = False, is_pyrogram_type: bool = False):
if t in core_types:
if t == "long":
return "``int`` ``64-bit``"
elif "int" in t:
size = INT_RE.match(t)
return "``int`` ``{}-bit``".format(size.group(1)) if size else "``int`` ``32-bit``"
elif t == "double":
return "``float`` ``64-bit``"
elif t == "string":
return "``str``"
else:
return "``{}``".format(t.lower())
elif t == "true":
return "``bool``"
elif t == "Object" or t == "X":
return "Any object from :obj:`pyrogram.api.types`"
elif t == "!X":
return "Any method from :obj:`pyrogram.api.functions`"
elif t.startswith("Vector"):
return "List of " + get_docstring_arg_type(t.split("<", 1)[1][:-1], True, is_pyrogram_type)
else:
if is_pyrogram_type:
t = "pyrogram." + t
t = types_to_constructors.get(t, [t])
n = len(t) - 1
t = (("e" if is_list else "E") + "ither " if n else "") + ", ".join(
":obj:`{1} <pyrogram.api.types.{0}{1}>`".format(
"pyrogram." if is_pyrogram_type else "",
i.lstrip("pyrogram.")
)
for i in t
)
if n:
t = t.split(", ")
t = ", ".join(t[:-1]) + " or " + t[-1]
return t
def get_references(t: str):
t = constructors_to_functions.get(t)
if t:
n = len(t) - 1
t = ", ".join(
":obj:`{0} <pyrogram.api.functions.{0}>`".format(i)
for i in t
)
if n:
t = t.split(", ")
t = ", ".join(t[:-1]) + " and " + t[-1]
return t
class Combinator:
def __init__(self, section: str, namespace: str, name: str, id: str, args: list, has_flags: bool, return_type: str):
def __init__(self,
section: str,
namespace: str,
name: str,
id: str,
args: list,
has_flags: bool,
return_type: str,
docs: str):
self.section = section
self.namespace = namespace
self.name = name
@ -43,6 +116,7 @@ class Combinator:
self.args = args
self.has_flags = has_flags
self.return_type = return_type
self.docs = docs
def snek(s: str):
@ -72,13 +146,17 @@ def start():
with open("{}/source/auth_key.tl".format(HOME), encoding="utf-8") as auth, \
open("{}/source/sys_msgs.tl".format(HOME), encoding="utf-8") as system, \
open("{}/source/main_api.tl".format(HOME), encoding="utf-8") as api:
schema = (auth.read() + system.read() + api.read()).splitlines()
open("{}/source/main_api.tl".format(HOME), encoding="utf-8") as api, \
open("{}/source/pyrogram.tl".format(HOME), encoding="utf-8") as pyrogram:
schema = (auth.read() + system.read() + api.read() + pyrogram.read()).splitlines()
with open("{}/template/class.txt".format(HOME), encoding="utf-8") as f:
template = f.read()
with open("{}/template/mtproto.txt".format(HOME), encoding="utf-8") as f:
mtproto_template = f.read()
with open(notice_path, encoding="utf-8") as f:
with open("{}/template/pyrogram.txt".format(HOME), encoding="utf-8") as f:
pyrogram_template = f.read()
with open(NOTICE_PATH, encoding="utf-8") as f:
notice = []
for line in f.readlines():
@ -106,9 +184,9 @@ def start():
combinator = COMBINATOR_RE.match(line)
if combinator:
name, id, return_type = combinator.groups()
name, id, return_type, docs = combinator.groups()
namespace, name = name.split(".") if "." in name else ("", name)
args = ARGS_RE.findall(line)
args = ARGS_RE.findall(line.split(" //")[0])
# Pingu!
has_flags = not not FLAGS_RE_3.findall(line)
@ -129,23 +207,37 @@ def start():
Combinator(
section,
namespace,
name,
capit(name),
"0x{}".format(id.zfill(8)),
args,
has_flags,
return_type
".".join(
return_type.split(".")[:-1]
+ [capit(return_type.split(".")[-1])]
),
docs
)
)
by_types = {}
for c in combinators:
return_type = capit(c.return_type)
return_type = c.return_type
if c.section == "types":
if return_type not in by_types:
by_types[return_type] = []
if return_type.startswith("Vector"):
return_type = return_type.split("<")[1][:-1]
by_types[return_type].append(".".join(filter(None, [c.namespace, capit(c.name)])))
d = types_to_constructors if c.section == "types" else types_to_functions
if return_type not in d:
d[return_type] = []
d[return_type].append(".".join(filter(None, [c.namespace, c.name])))
for k, v in types_to_constructors.items():
for i in v:
try:
constructors_to_functions[i] = types_to_functions[k]
except KeyError:
pass
total = len(combinators)
current = 0
@ -182,52 +274,30 @@ def start():
) if c.args else "pass"
docstring_args = []
# docs = c.docs.split("|")[1:] if c.docs else None
for i, arg in enumerate(sorted_args):
arg_name, arg_type = arg
is_optional = arg_type.startswith("flags.")
is_optional = FLAGS_RE.match(arg_type)
flag_number = is_optional.group(1) if is_optional else -1
arg_type = arg_type.split("?")[-1]
if arg_type in core_types:
if "int" in arg_type or arg_type == "long":
arg_type = ":obj:`int`"
elif arg_type == "double":
arg_type = ":obj:`float`"
else:
arg_type = ":obj:`{}`".format(arg_type.lower())
elif arg_type == "true":
arg_type = ":obj:`bool`"
else:
if arg_type.startswith("Vector"):
sub_type = arg_type.split("<")[1][:-1]
if sub_type in core_types:
if "int" in sub_type or sub_type == "long":
arg_type = "List of :obj:`int`"
elif sub_type == "double":
arg_type = "List of :obj:`float`"
else:
arg_type = "List of :obj:`{}`".format(sub_type.lower())
else:
arg_type = "List of :class:`pyrogram.api.types.{}`".format(
".".join(
sub_type.split(".")[:-1]
+ [capit(sub_type.split(".")[-1])]
)
)
else:
arg_type = ":class:`pyrogram.api.types.{}`".format(
".".join(
arg_type.split(".")[:-1]
+ [capit(arg_type.split(".")[-1])]
)
)
# if c.namespace == "pyrogram":
# docstring_args.append(
# "{} ({}{}):\n {}\n".format(
# arg_name,
# get_docstring_arg_type(arg_type, is_pyrogram_type=True),
# ", optional" if "Optional" in docs[i] else "",
# re.sub("Optional\. ", "", docs[i].split("§")[1].rstrip(".") + ".")
# )
# )
# else:
docstring_args.append(
"{}: {}{}".format(
"{}{}: {}".format(
arg_name,
arg_type,
" (optional)" if is_optional else ""
" (optional)".format(flag_number) if is_optional else "",
get_docstring_arg_type(arg_type, is_pyrogram_type=c.namespace == "pyrogram")
)
)
@ -236,81 +306,16 @@ def start():
else:
docstring_args = "No parameters required."
docstring_args = "Attributes:\n ID (:obj:`int`): ``{}``\n\n ".format(c.id) + docstring_args
docstring_args = "Attributes:\n ID: ``{}``\n\n ".format(c.id) + docstring_args
if c.section == "functions":
docstring_args += "\n\n Returns:\n "
if c.return_type in core_types:
if "int" in c.return_type or c.return_type == "long":
return_type = ":obj:`int`"
elif c.return_type == "double":
return_type = ":obj:`float`"
else:
return_type = ":obj:`{}`".format(c.return_type.lower())
else:
if c.return_type.startswith("Vector"):
sub_type = c.return_type.split("<")[1][:-1]
docstring_args += "\n\n Raises:\n :obj:`Error <pyrogram.Error>`"
docstring_args += "\n\n Returns:\n " + get_docstring_arg_type(c.return_type)
else:
references = get_references(".".join(filter(None, [c.namespace, c.name])))
if sub_type in core_types:
if "int" in sub_type or sub_type == "long":
return_type = "List of :obj:`int`"
elif sub_type == "double":
return_type = "List of :obj:`float`"
else:
return_type = "List of :obj:`{}`".format(c.return_type.lower())
else:
if c.section == "functions":
try:
constructors = by_types[capit(sub_type)]
except KeyError:
return_type = "List of :class:`pyrogram.api.types.{}`".format(
".".join(
sub_type.split(".")[:-1]
+ [capit(sub_type.split(".")[-1])]
)
)
else:
constructors = ["List of :class:`pyrogram.api.types.{}`".format(
".".join(
i.split(".")[:-1]
+ [capit(i.split(".")[-1])]
)
) for i in constructors]
return_type = " | ".join(constructors)
else:
return_type = "List of :class:`pyrogram.api.types.{}`".format(
".".join(
sub_type.split(".")[:-1]
+ [capit(sub_type.split(".")[-1])]
)
)
else:
if c.section == "functions":
try:
constructors = by_types[capit(c.return_type)]
except KeyError:
return_type = ":class:`pyrogram.api.types.{}`".format(
".".join(filter(None, [c.namespace, capit(c.name)]))
)
else:
constructors = [":class:`pyrogram.api.types.{}`".format(
".".join(
i.split(".")[:-1]
+ [capit(i.split(".")[-1])]
)
) for i in constructors]
return_type = " | ".join(constructors)
else:
return_type = ":class:`pyrogram.api.types.{}`".format(
".".join(filter(None, [c.namespace, capit(c.name)]))
)
docstring_args += return_type
if c.section == "functions":
docstring_args += "\n\n Raises:\n :class:`pyrogram.Error`"
if references:
docstring_args += "\n\n See Also:\n This object can be returned by " + references + "."
if c.has_flags:
write_flags = []
@ -397,22 +402,38 @@ def start():
read_types += "\n "
read_types += "{} = Object.read(b)\n ".format(arg_name)
if c.docs:
description = c.docs.split("|")[0].split("§")[1]
docstring_args = description + "\n\n " + docstring_args
with open("{}/{}.py".format(path, snek(c.name)), "w", encoding="utf-8") as f:
f.write(
template.format(
notice=notice,
class_name=capit(c.name),
docstring_args=docstring_args,
object_id=c.id,
arguments=arguments,
fields=fields,
read_flags=read_flags,
read_types=read_types,
write_flags=write_flags,
write_types=write_types,
return_arguments=", ".join([i[0] for i in sorted_args])
if c.docs:
f.write(
pyrogram_template.format(
notice=notice,
class_name=capit(c.name),
docstring_args=docstring_args,
object_id=c.id,
arguments=arguments,
fields=fields
)
)
else:
f.write(
mtproto_template.format(
notice=notice,
class_name=capit(c.name),
docstring_args=docstring_args,
object_id=c.id,
arguments=arguments,
fields=fields,
read_flags=read_flags,
read_types=read_types,
write_flags=write_flags,
write_types=write_types,
return_arguments=", ".join([i[0] for i in sorted_args])
)
)
)
with open("{}/all.py".format(DESTINATION), "w", encoding="utf-8") as f:
f.write(notice + "\n\n")
@ -443,5 +464,5 @@ def start():
if "__main__" == __name__:
HOME = "."
DESTINATION = "../../pyrogram/api"
notice_path = "../../NOTICE"
NOTICE_PATH = "../../NOTICE"
start()

View File

@ -0,0 +1,22 @@
// Pyrogram
---types---
pyrogram.update#b0700000 flags:# update_id:int message:flags.0?Message edited_message:flags.1?Message channel_post:flags.2?Message edited_channel_post:flags.3?Message inline_query:flags.4?InlineQuery chosen_inline_result:flags.5?ChosenInlineResult callback_query:flags.6?CallbackQuery shipping_query:flags.7?ShippingQuery pre_checkout_query:flags.8?PreCheckoutQuery = pyrogram.Update;
pyrogram.user#b0700001 flags:# id:int is_bot:Bool first_name:string last_name:flags.0?string username:flags.1?string language_code:flags.2?string phone_number:flags.3?string photo:flags.4?ChatPhoto = pyrogram.User;
pyrogram.chat#b0700002 flags:# id:int type:string title:flags.0?string username:flags.1?string first_name:flags.2?string last_name:flags.3?string all_members_are_administrators:flags.4?Bool photo:flags.5?ChatPhoto description:flags.6?string invite_link:flags.7?string pinned_message:flags.8?Message sticker_set_name:flags.9?string can_set_sticker_set:flags.10?Bool = pyrogram.Chat;
pyrogram.message#b0700003 flags:# message_id:int from_user:flags.0?User date:int chat:Chat forward_from:flags.1?User forward_from_chat:flags.2?Chat forward_from_message_id:flags.3?int forward_signature:flags.4?string forward_date:flags.5?int reply_to_message:flags.6?Message edit_date:flags.7?int media_group_id:flags.8?string author_signature:flags.9?string text:flags.10?string entities:flags.11?Vector<MessageEntity> caption_entities:flags.12?Vector<MessageEntity> audio:flags.13?Audio document:flags.14?Document game:flags.15?Game photo:flags.16?Vector<PhotoSize> sticker:flags.17?Sticker video:flags.18?Video voice:flags.19?Voice video_note:flags.20?VideoNote caption:flags.21?string contact:flags.22?Contact location:flags.23?Location venue:flags.24?Venue new_chat_members:flags.25?Vector<User> left_chat_member:flags.26?User new_chat_title:flags.27?string new_chat_photo:flags.28?Vector<PhotoSize> delete_chat_photo:flags.29?true group_chat_created:flags.30?true supergroup_chat_created:flags.31?true channel_chat_created:flags.32?true migrate_to_chat_id:flags.33?int migrate_from_chat_id:flags.34?int pinned_message:flags.35?Message invoice:flags.36?Invoice successful_payment:flags.37?SuccessfulPayment connected_website:flags.38?string views:flags.39?int via_bot:flags.40?User = pyrogram.Message;
pyrogram.messageEntity#b0700004 flags:# type:string offset:int length:int url:flags.0?string user:flags.1?User = pyrogram.MessageEntity;
pyrogram.photoSize#b0700005 flags:# file_id:string file_size:flags.0?int date:flags.1?int width:int height:int = pyrogram.PhotoSize;
pyrogram.audio#b0700006 flags:# file_id:string thumb:flags.0?PhotoSize file_name:flags.1?string mime_type:flags.2?string file_size:flags.3?int date:flags.4?int duration:int performer:flags.5?string title:flags.6?string = pyrogram.Audio;
pyrogram.document#b0700007 flags:# file_id:string thumb:flags.0?PhotoSize file_name:flags.1?string mime_type:flags.2?string file_size:flags.3?int date:flags.4?int = pyrogram.Document;
pyrogram.video#b0700008 flags:# file_id:string thumb:flags.0?PhotoSize file_name:flags.1?string mime_type:flags.2?string file_size:flags.3?int date:flags.4?int width:int height:int duration:int = pyrogram.Video;
pyrogram.voice#b0700009 flags:# file_id:string thumb:flags.0?PhotoSize file_name:flags.1?string mime_type:flags.2?string file_size:flags.3?int date:flags.4?int duration:int = pyrogram.Voice;
pyrogram.videoNote#b0700010 flags:# file_id:string thumb:flags.0?PhotoSize file_name:flags.1?string mime_type:flags.2?string file_size:flags.3?int date:flags.4?int length:int duration:int = pyrogram.VideoNote;
pyrogram.contact#b0700011 flags:# phone_number:string first_name:string last_name:flags.0?string user_id:flags.1?int = pyrogram.Contact;
pyrogram.location#b0700012 longitude:double latitude:double = pyrogram.Location;
pyrogram.venue#b0700013 flags:# location:Location title:string address:string foursquare_id:flags.0?string = pyrogram.Venue;
pyrogram.userProfilePhotos#b0700014 total_count:int photos:Vector<Vector<PhotoSize>> = pyrogram.UserProfilePhotos;
pyrogram.chatPhoto#b0700015 small_file_id:string big_file_id:string = pyrogram.ChatPhoto;
pyrogram.chatMember#b0700016 flags:# user:User status:string until_date:flags.0?int can_be_edited:flags.1?Bool can_change_info:flags.2?Bool can_post_messages:flags.3?Bool can_edit_messages:flags.4?Bool can_delete_messages:flags.5?Bool can_invite_users:flags.6?Bool can_restrict_members:flags.7?Bool can_pin_messages:flags.8?Bool can_promote_members:flags.9?Bool can_send_messages:flags.10?Bool can_send_media_messages:flags.11?Bool can_send_other_messages:flags.12?Bool can_add_web_page_previews:flags.13?Bool = pyrogram.ChatMember;
pyrogram.sticker#b0700017 flags:# file_id:string thumb:flags.0?PhotoSize file_name:flags.1?string mime_type:flags.2?string file_size:flags.3?int date:flags.4?int width:int height:int emoji:flags.5?string set_name:flags.6?string mask_position:flags.7?MaskPosition = pyrogram.Sticker;

View File

@ -6,8 +6,7 @@ from pyrogram.api.core import *
class {class_name}(Object):
"""
{docstring_args}
"""{docstring_args}
"""
ID = {object_id}

View File

@ -0,0 +1,11 @@
{notice}
from pyrogram.api.core import Object
class {class_name}(Object):
"""{docstring_args}
"""
ID = {object_id}
def __init__(self{arguments}):
{fields}

View File

@ -20,17 +20,17 @@ import ast
import os
import shutil
home = "compiler/docs"
destination = "docs/source"
HOME = "compiler/docs"
DESTINATION = "docs/source"
functions_path = "pyrogram/api/functions"
types_path = "pyrogram/api/types"
FUNCTIONS_PATH = "pyrogram/api/functions"
TYPES_PATH = "pyrogram/api/types"
functions_base = "functions"
types_base = "types"
FUNCTIONS_BASE = "functions"
TYPES_BASE = "types"
shutil.rmtree(types_base, ignore_errors=True)
shutil.rmtree(functions_base, ignore_errors=True)
shutil.rmtree(TYPES_BASE, ignore_errors=True)
shutil.rmtree(FUNCTIONS_BASE, ignore_errors=True)
def generate(source_path, base):
@ -57,9 +57,9 @@ def generate(source_path, base):
if level:
full_path = base + "/" + full_path
os.makedirs(os.path.dirname(destination + "/" + full_path), exist_ok=True)
os.makedirs(os.path.dirname(DESTINATION + "/" + full_path), exist_ok=True)
with open(destination + "/" + full_path, "w", encoding="utf-8") as f:
with open(DESTINATION + "/" + full_path, "w", encoding="utf-8") as f:
f.write(
page_template.format(
title=name,
@ -94,7 +94,10 @@ def generate(source_path, base):
inner_path = base + "/index" + ".rst"
module = "pyrogram.api.{}".format(base)
with open(destination + "/" + inner_path, "w", encoding="utf-8") as f:
with open(DESTINATION + "/" + inner_path, "w", encoding="utf-8") as f:
if k == base:
f.write(":tocdepth: 1\n\n")
f.write(
toctree.format(
title=k.title(),
@ -111,20 +114,20 @@ def start():
global page_template
global toctree
with open(home + "/template/page.txt", encoding="utf-8") as f:
with open(HOME + "/template/page.txt", encoding="utf-8") as f:
page_template = f.read()
with open(home + "/template/toctree.txt", encoding="utf-8") as f:
with open(HOME + "/template/toctree.txt", encoding="utf-8") as f:
toctree = f.read()
generate(types_path, types_base)
generate(functions_path, functions_base)
generate(TYPES_PATH, TYPES_BASE)
generate(FUNCTIONS_PATH, FUNCTIONS_BASE)
if "__main__" == __name__:
functions_path = "../../pyrogram/api/functions"
types_path = "../../pyrogram/api/types"
home = "."
destination = "../../docs/source"
FUNCTIONS_PATH = "../../pyrogram/api/functions"
TYPES_PATH = "../../pyrogram/api/types"
HOME = "."
DESTINATION = "../../docs/source"
start()

View File

@ -21,9 +21,9 @@ import os
import re
import shutil
home = "compiler/error"
dest = "pyrogram/api/errors/exceptions"
notice_path = "NOTICE"
HOME = "compiler/error"
DEST = "pyrogram/api/errors/exceptions"
NOTICE_PATH = "NOTICE"
def snek(s):
@ -38,12 +38,12 @@ def caml(s):
def start():
shutil.rmtree(dest, ignore_errors=True)
os.makedirs(dest)
shutil.rmtree(DEST, ignore_errors=True)
os.makedirs(DEST)
files = [i for i in os.listdir("{}/source".format(home))]
files = [i for i in os.listdir("{}/source".format(HOME))]
with open(notice_path, encoding="utf-8") as f:
with open(NOTICE_PATH, encoding="utf-8") as f:
notice = []
for line in f.readlines():
@ -51,7 +51,7 @@ def start():
notice = "\n".join(notice)
with open("{}/all.py".format(dest), "w", encoding="utf-8") as f_all:
with open("{}/all.py".format(DEST), "w", encoding="utf-8") as f_all:
f_all.write(notice + "\n\n")
f_all.write("count = {count}\n\n")
f_all.write("exceptions = {\n")
@ -63,7 +63,7 @@ def start():
f_all.write(" {}: {{\n".format(code))
init = "{}/__init__.py".format(dest)
init = "{}/__init__.py".format(DEST)
if not os.path.exists(init):
with open(init, "w", encoding="utf-8") as f_init:
@ -72,8 +72,8 @@ def start():
with open(init, "a", encoding="utf-8") as f_init:
f_init.write("from .{}_{} import *\n".format(name.lower(), code))
with open("{}/source/{}".format(home, i), encoding="utf-8") as f_csv, \
open("{}/{}_{}.py".format(dest, name.lower(), code), "w", encoding="utf-8") as f_class:
with open("{}/source/{}".format(HOME, i), encoding="utf-8") as f_csv, \
open("{}/{}_{}.py".format(DEST, name.lower(), code), "w", encoding="utf-8") as f_class:
reader = csv.reader(f_csv, delimiter="\t")
super_class = caml(name)
@ -98,10 +98,10 @@ def start():
sub_classes.append((sub_class, id, message))
with open("{}/template/class.txt".format(home), "r", encoding="utf-8") as f_class_template:
with open("{}/template/class.txt".format(HOME), "r", encoding="utf-8") as f_class_template:
class_template = f_class_template.read()
with open("{}/template/sub_class.txt".format(home), "r", encoding="utf-8") as f_sub_class_template:
with open("{}/template/sub_class.txt".format(HOME), "r", encoding="utf-8") as f_sub_class_template:
sub_class_template = f_sub_class_template.read()
class_template = class_template.format(
@ -123,18 +123,18 @@ def start():
f_all.write("}\n")
with open("{}/all.py".format(dest), encoding="utf-8") as f:
with open("{}/all.py".format(DEST), encoding="utf-8") as f:
content = f.read()
with open("{}/all.py".format(dest), "w", encoding="utf-8") as f:
with open("{}/all.py".format(DEST), "w", encoding="utf-8") as f:
f.write(re.sub("{count}", str(count), content))
print("Compiling Errors: [100%]")
if "__main__" == __name__:
home = "."
dest = "../../pyrogram/api/errors/exceptions"
notice_path = "../../NOTICE"
HOME = "."
DEST = "../../pyrogram/api/errors/exceptions"
NOTICE_PATH = "../../NOTICE"
start()

View File

@ -48,4 +48,15 @@ ABOUT_TOO_LONG The about text is too long
MULTI_MEDIA_TOO_LONG The album contains more than 10 items
USERNAME_OCCUPIED The username is already in use
BOT_INLINE_DISABLED The inline feature of the bot is disabled
INLINE_RESULT_EXPIRED The inline bot query expired
INLINE_RESULT_EXPIRED The inline bot query expired
INVITE_HASH_INVALID The invite link hash is invalid
USER_ALREADY_PARTICIPANT The user is already a participant of this chat
TTL_MEDIA_INVALID This kind of media does not support self-destruction
MAX_ID_INVALID The max_id parameter is invalid
CHANNEL_INVALID The channel parameter is invalid
DC_ID_INVALID The dc_id parameter is invalid
LIMIT_INVALID The limit parameter is invalid
OFFSET_INVALID The offset parameter is invalid
EMAIL_INVALID The email provided is invalid
USER_IS_BOT A bot cannot send messages to other bots or to itself
WEBPAGE_CURL_FAILED Telegram could not fetch the provided URL
1 id message
48 MULTI_MEDIA_TOO_LONG The album contains more than 10 items
49 USERNAME_OCCUPIED The username is already in use
50 BOT_INLINE_DISABLED The inline feature of the bot is disabled
51 INLINE_RESULT_EXPIRED The inline bot query expired
52 INVITE_HASH_INVALID The invite link hash is invalid
53 USER_ALREADY_PARTICIPANT The user is already a participant of this chat
54 TTL_MEDIA_INVALID This kind of media does not support self-destruction
55 MAX_ID_INVALID The max_id parameter is invalid
56 CHANNEL_INVALID The channel parameter is invalid
57 DC_ID_INVALID The dc_id parameter is invalid
58 LIMIT_INVALID The limit parameter is invalid
59 OFFSET_INVALID The offset parameter is invalid
60 EMAIL_INVALID The email provided is invalid
61 USER_IS_BOT A bot cannot send messages to other bots or to itself
62 WEBPAGE_CURL_FAILED Telegram could not fetch the provided URL

View File

@ -1,2 +1,4 @@
id message
AUTH_RESTART User authorization has restarted
RPC_CALL_FAIL Telegram is having internal problems. Please try again later
RPC_MCGET_FAIL Telegram is having internal problems. Please try again later
1 id message
2 AUTH_RESTART User authorization has restarted
3 RPC_CALL_FAIL Telegram is having internal problems. Please try again later
4 RPC_MCGET_FAIL Telegram is having internal problems. Please try again later

View File

@ -6,7 +6,7 @@ from ..error import Error
class {super_class}(Error):
{docstring}
CODE = {code}
""":obj:`int`: Error Code"""
"""``int``: Error Code"""
NAME = __doc__

View File

@ -1,7 +1,7 @@
class {sub_class}({super_class}):
{docstring}
ID = {id}
""":obj:`str`: Error ID"""
"""``str``: Error ID"""
MESSAGE = __doc__

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -36,11 +36,12 @@ from pyrogram import __version__
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.napoleon'
'sphinx.ext.napoleon',
'sphinx.ext.autosummary'
]
# Don't show source files on docs
html_show_sourcelink = False
html_show_sourcelink = True
# Order by source, not alphabetically
autodoc_member_order = 'bysource'
@ -84,41 +85,48 @@ language = None
exclude_patterns = []
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
pygments_style = 'tango'
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
# -- Options for HTML output ----------------------------------------------
html_title = "Pyrogram Documentation"
# Overridden by template
html_show_copyright = False
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'sphinx_rtd_theme'
html_theme_options = {
'collapse_navigation': False
}
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}
html_theme_options = {
'canonical_url': "https://docs.pyrogram.ml/",
'collapse_navigation': False,
'sticky_navigation': False,
'logo_only': True,
'display_version': True
}
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
html_logo = '_static/pyrogram.png'
html_logo = '_images/logo.png'
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
html_favicon = '_static/pyrogram.ico'
html_favicon = '_images/favicon.ico'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# html_static_path = ['_static']
# Custom sidebar templates, must be a dictionary that maps document names
# to template names.

View File

@ -3,6 +3,6 @@ Unknown Error
.. module:: pyrogram.api.errors.UnknownError
.. autoclass:: pyrogram.api.errors.error.UnknownError
.. autoexception:: pyrogram.api.errors.error.UnknownError
:members:
:show-inheritance:

View File

@ -1,73 +0,0 @@
Basic Usage
===========
.. note::
All the snippets below assume you have successfully created and started a :obj:`pyrogram.Client` instance.
You also must be authorized, that is, a valid *.session file does exist in your working directory.
Simple API Access
-----------------
The easiest way to interact with the API is via the :obj:`pyrogram.Client` class which exposes bot-like_ methods.
The purpose of this Client class is to make it even simpler to work with Telegram's API by abstracting the
raw functions listed in the API scheme.
The result is a much cleaner interface that allows you to:
- Get information about the authorized user:
.. code-block:: python
print(client.get_me())
- Send a message to yourself (Saved Messages):
.. code-block:: python
client.send_message(
chat_id="me",
text="Hi there! I'm using Pyrogram"
)
.. seealso:: For a complete list of the available methods have a look at the :obj:`pyrogram.Client` class.
.. _using-raw-functions:
Using Raw Functions
-------------------
If you want **complete**, low-level access to the Telegram API you have to use the raw
:obj:`functions <pyrogram.api.functions>` and :obj:`types <pyrogram.api.types>` exposed by the ``pyrogram.api``
package and call any Telegram API method you wish using the :obj:`send <pyrogram.Client.send>` method provided by
the Client class.
Here some examples:
- Update first name, last name and bio:
.. code-block:: python
from pyrogram.api import functions
client.send(
functions.account.UpdateProfile(
first_name="Dan", last_name="Tès",
about="Bio written from Pyrogram"
)
)
- Share your Last Seen time only with your contacts:
.. code-block:: python
from pyrogram.api import functions, types
client.send(
functions.account.SetPrivacy(
key=types.InputPrivacyKeyStatusTimestamp(),
rules=[types.InputPrivacyValueAllowContacts()]
)
)
.. _bot-like: https://core.telegram.org/bots/api#available-methods

View File

@ -1,66 +0,0 @@
Project Setup
=============
This section provides all the information you need to setup your project with Pyrogram.
There are a few steps you have to follow before you can actually use the library to make API calls.
API Keys
--------
The very first step requires you to obtain a valid Telegram API key.
If you already have one you can skip this, otherwise:
#. Visit https://my.telegram.org/apps and log in with your Telegram Account.
#. Fill out the form to register a new Telegram application.
#. Done. The Telegram API key consists of two parts: the **App api_id** and the **App api_hash**
.. important:: This key should be kept secret.
Configuration
-------------
Create a new ``config.ini`` file at the root of your working directory,
copy-paste the following and replace the **api_id** and **api_hash** values with `your own <#api-keys>`_:
.. code-block:: ini
[pyrogram]
api_id = 12345
api_hash = 0123456789abcdef0123456789abcdef
Authorization
-------------
Telegram requires that users be authorized in order to use the API.
Pyrogram automatically manages this access, all you need to do is create an instance of
the :class:`pyrogram.Client` class by passing to it a ``<session_name>`` of your choice
and call the :obj:`start <pyrogram.Client.start>` method:
.. code-block:: python
from pyrogram import Client
client = Client(session_name="example")
client.start()
This starts an interactive shell asking you to input your **phone number** (including your `Country Code`_)
and the **phone code** you will receive:
.. code::
Enter phone number: +39**********
Is "+39**********" correct? (y/n): y
Enter phone code: 32768
After successfully authorizing yourself, a new file called ``example.session`` will be created allowing
Pyrogram executing API calls with your identity.
.. important:: Your *.session file(s) must be kept secret.
.. note::
The authorization process is executed only once.
However, the code above is always required; as long as a valid session file exists,
Pyrogram will use that and won't ask you to enter your phone number again when you restart your script.
.. _`Country Code`: https://en.wikipedia.org/wiki/List_of_country_calling_codes

View File

@ -1,37 +0,0 @@
Quick Installation
==================
The most straightforward and recommended way to install or upgrade Pyrogram is by using **pip**:
.. code-block:: bash
$ pip install --upgrade pyrogram
Bleeding Edge
-------------
If you want the latest development version of the library, you can either install it automatically with:
.. code-block:: bash
$ pip install git+https://github.com/pyrogram/pyrogram.git
or manually, using:
.. code-block:: bash
$ git clone https://github.com/pyrogram/pyrogram.git
$ cd pyrogram
$ python setup.py install
Verifying
---------
To verify that Pyrogram is correctly installed, open a Python shell and try to import it.
If no errors show up you are good to go.
.. code-block:: bash
>>> import pyrogram
>>> pyrogram.__version__
'0.3.2'

View File

@ -3,16 +3,10 @@ Welcome to Pyrogram
.. raw:: html
<p align="right">
<a class="github-button" href="https://github.com/pyrogram/pyrogram/subscription" data-icon="octicon-eye" data-size="large" data-show-count="true" aria-label="Watch pyrogram/pyrogram on GitHub">Watch</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>
<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>
</p>
<div align="center">
<a href="https://pyrogram.ml">
<div><img src="https://pyrogram.ml/images/icon.png" alt="Pyrogram Icon"></div>
<div><img src="https://pyrogram.ml/images/label.png" alt="Pyrogram Label"></div>
<a href="https://docs.pyrogram.ml">
<div><img src="https://media.pyrogram.ml/images/icon.png" alt="Pyrogram Icon"></div>
<div><img src="https://media.pyrogram.ml/images/label.png" alt="Pyrogram Label"></div>
</a>
</div>
@ -32,83 +26,72 @@ Welcome to Pyrogram
</a>
<br><br>
<a href="https://github.com/pyrogram/pyrogram/blob/master/compiler/api/source/main_api.tl">
<img src="https://www.pyrogram.ml/images/scheme.svg"
<img src="https://media.pyrogram.ml/images/scheme.svg"
alt="Scheme Layer 75">
</a>
<a href="https://core.telegram.org/mtproto">
<img src="https://www.pyrogram.ml/images/mtproto.svg"
alt="MTProto v2.0">
<a href="https://github.com/pyrogram/tgcrypto">
<img src="https://media.pyrogram.ml/images/tgcrypto.svg"
alt="TgCrypto">
</a>
</p>
.. code-block:: python
from pyrogram import Client, Filters
app = Client("my_account")
@app.on_message(Filters.private)
def hello(client, message):
client.send_message(
message.chat.id, "Hello {}".format(message.from_user.first_name))
app.start()
app.idle()
Welcome to Pyrogram's Documentation! Here you can find resources for learning how to use the library.
Contents are organized by topic and can be accessed from the sidebar, or by following them one by one using the Next
button at the end of each page. But first, here's a brief overview of what is this all about.
About
-----
Pyrogram is a fully functional Telegram Client Library written from the ground up in Python.
It offers **simple** and **complete** access to the Telegram Messenger API and is designed for Python developers
keen on building custom Telegram applications.
Pyrogram is a brand new Telegram_ Client Library written from the ground up in Python and C. It can be used for building
custom Telegram applications that interact with the MTProto API as both User and Bot.
Features
--------
- **Easy to setup**: Pyrogram can be easily installed and upgraded using **pip**, requires
a minimal set of dependencies (which are also automatically managed) and very few lines
of code to get started with.
- **Easy to use**: You can easily install Pyrogram using pip and start building your app right away.
- **High-level**: The low-level details of MTProto are abstracted and automatically handled.
- **Fast**: Crypto parts are boosted up by TgCrypto_, a high-performance library written in pure C.
- **Updated** to the latest Telegram API version, currently Layer 76 running on MTProto 2.0.
- **Documented**: Pyrogram API methods are documented and resemble the Telegram Bot API.
- **Full API**, allowing to execute any advanced action an official client is able to do, and more.
- **Easy to use**: Pyrogram provides idiomatic, developer-friendly, clean and readable
Python code (either generated or hand-written) making the Telegram API simple to use.
- **High level**: Pyrogram automatically handles all the low-level details of
communication with the Telegram servers by implementing the
`MTProto Mobile Protocol v2.0`_ and the mechanisms needed for establishing
a reliable connection.
- **Fast**: Pyrogram's speed is boosted up by `TgCrypto`_, a high-performance, easy-to-install
crypto library written in C.
- **Updated**: Pyrogram makes use of the latest Telegram API version, currently `Layer 75`_.
- **Documented**: Pyrogram API public methods are documented and resemble the well
established Telegram Bot API, thus offering a familiar look to Bot developers.
- **Full API support**: Beside the simple, bot-like methods offered by the Pyrogram API,
the library also provides a complete, low-level access to every single Telegram API method.
Preview
-------
.. code-block:: python
from pyrogram import Client
client = Client("example")
client.start()
client.send_message("me", "Hi there! I'm using Pyrogram")
client.send_photo("me", "/home/dan/pic.jpg", "Nice photo!")
client.stop()
To get started, press Next.
To get started, press the Next button.
.. toctree::
:hidden:
:caption: Getting Started
getting_started/QuickInstallation
getting_started/ProjectSetup
getting_started/BasicUsage
start/QuickInstallation
start/ProjectSetup
start/BasicUsage
.. toctree::
:hidden:
:caption: Resources
resources/TextFormatting
resources/UpdateHandling
resources/ErrorHandling
resources/ProxyServer
resources/SOCKS5Proxy
resources/TgCrypto
resources/AutoAuthorization
resources/FastCrypto
resources/TextFormatting
resources/BotsInteraction
resources/ErrorHandling
.. toctree::
:hidden:
@ -123,8 +106,6 @@ To get started, press Next.
functions/index
types/index
.. _`MTProto Mobile Protocol v2.0`: https://core.telegram.org/mtproto
.. _`Telegram`: https://telegram.org/
.. _TgCrypto: https://docs.pyrogram.ml/resources/FastCrypto/
.. _`Layer 75`: https://github.com/pyrogram/pyrogram/blob/master/compiler/api/source/main_api.tl
.. _TgCrypto: https://docs.pyrogram.ml/resources/TgCrypto/

View File

@ -1,5 +1,52 @@
Client
======
.. currentmodule:::: pyrogram.Client
.. autoclass:: pyrogram.Client
:members:
**Available methods**
.. autosummary::
:nosignatures:
start
stop
idle
on_message
on_raw_update
add_handler
send
resolve_peer
get_me
send_message
forward_messages
send_photo
send_audio
send_document
send_video
send_voice
send_video_note
send_media_group
send_location
send_venue
send_contact
send_chat_action
send_sticker
download_media
get_user_profile_photos
edit_message_text
edit_message_caption
delete_messages
join_chat
leave_chat
export_chat_invite_link
enable_cloud_password
change_cloud_password
remove_cloud_password
add_contacts
delete_contacts
get_inline_bot_results
send_inline_bot_result
get_messages

View File

@ -0,0 +1,6 @@
Emoji
======
.. autoclass:: pyrogram.Emoji
:members:
:undoc-members:

View File

@ -1,3 +1,5 @@
:tocdepth: 1
Error
=====

View File

@ -0,0 +1,6 @@
Filters
=======
.. autoclass:: pyrogram.Filters
:members:
:undoc-members:

View File

@ -1,6 +0,0 @@
InputMedia
==========
.. autoclass:: pyrogram.InputMedia
:members:
:undoc-members:

View File

@ -0,0 +1,6 @@
InputMediaPhoto
===============
.. autoclass:: pyrogram.InputMediaPhoto
:members:
:undoc-members:

View File

@ -0,0 +1,6 @@
InputMediaVideo
===============
.. autoclass:: pyrogram.InputMediaVideo
:members:
:undoc-members:

View File

@ -0,0 +1,6 @@
InputPhoneContact
=================
.. autoclass:: pyrogram.InputPhoneContact
:members:
:undoc-members:

View File

@ -0,0 +1,6 @@
MessageHandler
==============
.. autoclass:: pyrogram.MessageHandler
:members:
:undoc-members:

View File

@ -0,0 +1,6 @@
RawUpdateHandler
================
.. autoclass:: pyrogram.RawUpdateHandler
:members:
:undoc-members:

View File

@ -1,17 +1,45 @@
Pyrogram
========
In this section you can find a detailed description of the Pyrogram API.
In this section you can find a detailed description of the Pyrogram package and its high-level API.
:obj:`pyrogram.Client` is the main class you have to deal with.
You will notice that methods are named after the well established `Telegram Bot API`_ and that most of them accept
the same parameters as well, thus offering a familiar look to Bot developers.
:class:`Client <pyrogram.Client>` is the main class. It exposes easy-to-use methods that are named
after the well established `Telegram Bot API`_ methods, thus offering a familiar look to Bot developers.
.. toctree::
Client
MessageHandler
RawUpdateHandler
Filters
ChatAction
ParseMode
InputMedia
Emoji
Error
Types
-----
.. toctree::
../types/pyrogram/User
../types/pyrogram/Chat
../types/pyrogram/Message
../types/pyrogram/MessageEntity
../types/pyrogram/PhotoSize
../types/pyrogram/Audio
../types/pyrogram/Document
../types/pyrogram/Video
../types/pyrogram/Voice
../types/pyrogram/VideoNote
../types/pyrogram/Contact
../types/pyrogram/Location
../types/pyrogram/Venue
../types/pyrogram/UserProfilePhotos
../types/pyrogram/ChatPhoto
../types/pyrogram/ChatMember
InputMediaPhoto
InputMediaVideo
InputPhoneContact
../types/pyrogram/Sticker
.. _Telegram Bot API: https://core.telegram.org/bots/api#available-methods

View File

@ -0,0 +1,64 @@
Auto Authorization
==================
Manually writing phone number, phone code and password on the terminal every time you want to login can be tedious.
Pyrogram is able to automate both **Log In** and **Sign Up** processes, all you need to do is pass the relevant
parameters when creating a new :class:`Client <pyrogram.Client>`.
.. note:: If you omit any of the optional parameter required for the authorization, Pyrogram will ask you to
manually write it. For instance, if you don't want to set a ``last_name`` when creating a new account you
have to explicitly pass an empty string ""; the default value (None) will trigger the input() call.
Log In
-------
To automate the **Log In** process, pass your ``phone_number`` and ``password`` (if you have one) in the Client parameters.
If you want to retrieve the phone code programmatically, pass a callback function in the ``phone_code`` field — this
function must return the correct phone code as string (e.g., "12345") — otherwise, ignore this parameter, Pyrogram will
ask you to input the phone code manually.
.. code-block:: python
from pyrogram import Client
def phone_code_callback():
code = ... # Get your code programmatically
return code # Must be string, e.g., "12345"
app = Client(
session_name="example",
phone_number="39**********",
phone_code=phone_code_callback,
password="password" # (if you have one)
)
app.start()
print(app.get_me())
Sign Up
-------
To automate the **Sign Up** process (i.e., automatically create a new Telegram account), simply fill **both**
``first_name`` and ``last_name`` fields alongside the other parameters; they will be used to automatically create a new
Telegram account in case the phone number you passed is not registered yet.
.. code-block:: python
from pyrogram import Client
def phone_code_callback():
code = ... # Get your code programmatically
return code # Must be string, e.g., "12345"
app = Client(
session_name="example",
phone_number="39**********",
phone_code=phone_code_callback,
first_name="Pyrogram",
last_name="" # Can be an empty string
)
app.start()
print(app.get_me())

View File

@ -0,0 +1,40 @@
Bots Interaction
================
Users can interact with other bots via plain text messages as well as inline queries.
Inline Bots
-----------
- If a bot accepts inline queries, you can call it by using
:meth:`get_inline_bot_results() <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:`send_inline_bot_result() <pyrogram.Client.send_inline_bot_result>` to send a chosen result to any chat:
.. code-block:: python
# 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)
.. 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

@ -29,8 +29,7 @@ Examples
)
try:
# Something
pass
...
except BadRequest:
pass
except Flood:
@ -44,18 +43,18 @@ Examples
except UnknownError:
pass
Exceptions may also contain some informative values which can be useful.
e.g. :obj:`FloodWait <pyrogram.api.errors.exceptions.flood_420.FloodWait>` holds the amount of seconds you have to wait before you
can try again. The value is always stored in the ``x`` field of the returned exception object:
Exception objects may also contain some informative values.
E.g.: :obj:`FloodWait <pyrogram.api.errors.exceptions.flood_420.FloodWait>` holds the amount of seconds you have to wait
before you can try again. The value is always stored in the ``x`` field of the returned exception object:
.. code-block:: python
import time
from pyrogram.api.errors import FloodWait
try:
# something
pass
...
except FloodWait as e:
print(e.x)
time.sleep(e.x)
**TODO: Better explanation on how to deal with exceptions**

View File

@ -1,37 +0,0 @@
Fast Crypto
===========
Pyrogram's speed can be *dramatically* boosted up by installing TgCrypto_, a high-performance, easy-to-install crypto
library specifically written in C for Pyrogram [#f1]_. TgCrypto is a replacement for the painfully slow PyAES and
implements the crypto algorithms MTProto requires, namely AES-IGE and AES-CTR 256 bit.
Installation
------------
.. code-block:: bash
$ pip install --upgrade tgcrypto
.. note:: Being a C extension for Python, TgCrypto is an optional but *highly recommended* dependency; when TgCrypto
is not detected on your system, Pyrogram will automatically fall back to PyAES 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.
Usually the errors you receive when trying to install TgCrypto are enough to understand what you should do next.
- **Windows**: Install `Visual C++ 2015 Build Tools <http://landinghub.visualstudio.com/visual-cpp-build-tools>`_.
- **macOS**: A pop-up will automatically ask you to install the command line developer tools as soon as you issue the
installation command.
- **Linux**: Depending on your distro, install a proper C compiler (``gcc``, ``clang``) and the Python header files
(``python3-dev``).
- **Termux (Android)**: Install ``clang`` and ``python-dev`` packages.
More help on the `Pyrogram group chat <https://t.me/PyrogramChat>`_.
.. _TgCrypto: https://github.com/pyrogram/tgcrypto
.. [#f1] Although TgCrypto is intended for Pyrogram, it is shipped as a standalone package and can thus be used for
other projects too.

View File

@ -0,0 +1,50 @@
SOCKS5 Proxy
============
Pyrogram supports proxies with and without authentication. This feature allows Pyrogram to exchange data with Telegram
through an intermediate SOCKS5 proxy server.
Usage
-----
- To use Pyrogram with a proxy, simply append the following to your ``config.ini`` file and replace the values
with your own settings:
.. code-block:: ini
[proxy]
enabled = True
hostname = 11.22.33.44
port = 1080
username = <your_username>
password = <your_password>
To enable or disable the proxy without deleting your settings from the config file,
change the ``enabled`` value as follows:
- ``1``, ``yes``, ``True`` or ``on``: Enables the proxy
- ``0``, ``no``, ``False`` or ``off``: Disables the proxy
- Alternatively, you can setup your proxy without the need of the ``config.ini`` file by using the *proxy* parameter
in the Client class:
.. code-block:: python
from pyrogram import Client
app = Client(
session_name="example",
proxy=dict(
hostname="11.22.33.44",
port=1080,
username="<your_username>",
password="<your_password>"
)
)
app.start()
...
.. note:: If your proxy doesn't require authorization you can omit ``username`` and ``password`` by either leaving the
values blank/empty or completely delete the lines.

View File

@ -8,8 +8,8 @@ Beside bold, italic, and pre-formatted code, **Pyrogram does also support inline
Markdown Style
--------------
To use this mode, pass :obj:`pyrogram.ParseMode.MARKDOWN` or "markdown" in the *parse_mode* field when using
:obj:`send_message <pyrogram.Client.send_message>`. Use the following syntax in your message:
To use this mode, pass :obj:`MARKDOWN <pyrogram.ParseMode.MARKDOWN>` or "markdown" in the *parse_mode* field when using
:obj:`send_message() <pyrogram.Client.send_message>`. Use the following syntax in your message:
.. code::
@ -30,8 +30,8 @@ To use this mode, pass :obj:`pyrogram.ParseMode.MARKDOWN` or "markdown" in the *
HTML Style
----------
To use this mode, pass :obj:`pyrogram.ParseMode.HTML` or "html" in the *parse_mode* field when using
:obj:`send_message <pyrogram.Client.send_message>`. The following tags are currently supported:
To use this mode, pass :obj:`HTML <pyrogram.ParseMode.HTML>` or "html" in the *parse_mode* field when using
:obj:`send_message() <pyrogram.Client.send_message>`. The following tags are currently supported:
.. code::
@ -56,14 +56,14 @@ Examples
.. code-block:: python
client.send_message(
app.send_message(
chat_id="me",
text=(
"**bold**\n"
"__italic__\n"
"[mention](tg://user?id=23122162)\n"
"[url](https://pyrogram.ml)\n"
"`code`\n"
"**bold**, "
"__italic__, "
"[mention](tg://user?id=23122162), "
"[url](https://pyrogram.ml), "
"`code`"
)
)
@ -71,7 +71,7 @@ Examples
.. code-block:: python
client.send_message(
app.send_message(
chat_id="me",
text=(
# Code block language is optional
@ -88,15 +88,15 @@ Examples
from pyrogram import ParseMode
client.send_message(
app.send_message(
chat_id="me",
text=(
"<b>bold</b>, <strong>bold</strong>\n"
"<i>italic</i>, <em>italic</em>\n"
"<a href=\"https://pyrogram.ml/\">inline URL</a>\n"
"<a href=\"tg://user?id=23122162\">inline mention of a user</a>\n"
"<code>inline fixed-width code</code>\n"
"<pre>pre-formatted fixed-width code block</pre>\n"
"<b>bold</b>, <strong>bold</strong>, "
"<i>italic</i>, <em>italic</em>, "
"<a href=\"https://pyrogram.ml/\">inline URL</a>, "
"<a href=\"tg://user?id=23122162\">inline mention of a user</a>, "
"<code>inline fixed-width code</code>, "
"<pre>pre-formatted fixed-width code block</pre>"
),
parse_mode=ParseMode.HTML
)

View File

@ -0,0 +1,32 @@
TgCrypto
========
Pyrogram's speed can be *dramatically* boosted up by TgCrypto_, a high-performance, easy-to-install Telegram Crypto
Library specifically written in C for Pyrogram [#f1]_ as a Python extension.
TgCrypto is a replacement for the much slower PyAES and implements the crypto algorithms Telegram requires, namely
**AES-IGE 256 bit** (used in MTProto v2.0) and **AES-CTR 256 bit** (used for CDN encrypted files).
Installation
------------
.. code-block:: bash
$ pip3 install --upgrade tgcrypto
.. note:: Being a C extension for Python, TgCrypto is an optional but *highly recommended* dependency; when TgCrypto is
not detected in your system, Pyrogram will automatically fall back to PyAES 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 errors you receive when trying to install TgCrypto are system dependent, but also descriptive enough to understand
what you should do next:
- **Windows**: Install `Visual C++ 2015 Build Tools <http://landinghub.visualstudio.com/visual-cpp-build-tools>`_.
- **macOS**: A pop-up will automatically ask you to install the command line developer tools.
- **Linux**: Install a proper C compiler (``gcc``, ``clang``) and the Python header files (``python3-dev``).
- **Termux (Android)**: Install ``clang`` and ``python-dev`` packages.
.. _TgCrypto: https://github.com/pyrogram/tgcrypto
.. [#f1] 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

@ -1,46 +1,188 @@
Update Handling
===============
Updates are events that happen in your Telegram account (incoming messages, new channel posts, user name changes, ...)
and can be handled by using a callback function, that is, a function called every time an ``Update`` is received from
Telegram.
Updates are events that happen in your Telegram account (incoming messages, new channel posts, new members join, ...)
and are handled by registering one or more callback functions with an Handler. There are multiple Handlers to choose
from, one for each kind of update:
To set an update handler simply call :obj:`set_update_handler <pyrogram.Client.set_update_handler>`
by passing the name of your defined callback function as argument *before* you start the Client.
- `MessageHandler <../pyrogram/MessageHandler.html>`_
- `RawUpdateHandler <../pyrogram/RawUpdateHandler.html>`_
Here's a complete example on how to set it up:
Registering an Handler
----------------------
.. code-block:: python
We shall examine the :obj:`MessageHandler <pyrogram.MessageHandler>`, which will be in charge for handling
:obj:`Message <pyrogram.api.types.pyrogram.Message>` objects.
from pyrogram import Client
def callback(update):
print(update)
client = Client(session_name="example")
client.set_update_handler(callback)
client.start()
client.idle()
The last line, :obj:`client.idle() <pyrogram.Client.idle>` is not strictly necessary but highly recommended;
it will block your script execution until you press :obj:`CTRL`:obj:`C` and automatically call the
:obj:`stop <pyrogram.Client.stop>` method which stops the Client and gently close the underlying connection.
Examples
--------
- Echo:
- The easiest and nicest way to register a MessageHandler is by decorating your function with the
:meth:`on_message() <pyrogram.Client.on_message>` decorator. Here's a full example that prints out the content
of a message as soon as it arrives.
.. code-block:: python
from pyrogram.api import types
from pyrogram import Client
def callback(update):
if isinstance(update, types.UpdateShortMessage) and not update.out:
client.send_message(update.user_id, update.message)
app = Client("my_account")
This checks if the update type is :obj:`UpdateShortMessage <pyrogram.api.types.UpdateShortMessage>` and that the
update is not generated by yourself (i.e., the message is not outgoing), then sends back the same message.
@app.on_message()
def my_handler(client, message):
print(message)
app.start()
app.idle()
- If you prefer not to use decorators, there is an alternative way for registering Handlers.
This is useful, for example, when you want to keep your callback functions in separate files.
.. code-block:: python
from pyrogram import Client, MessageHandler
def my_handler(client, message):
print(message)
app = Client("my_account")
app.add_handler(MessageHandler(my_handler))
app.start()
app.idle()
Using Filters
-------------
For a finer grained control over what kind of messages will be allowed or not in your callback functions, you can use
:class:`Filters <pyrogram.Filters>`.
- This example will show you how to **only** handle messages containing an
:obj:`Audio <pyrogram.api.types.pyrogram.Audio>` object and filter out any other message:
.. code-block:: python
from pyrogram import Filters
@app.on_message(Filters.audio)
def my_handler(client, message):
print(message)
- or, without decorators:
.. code-block:: python
from pyrogram import Filters, MessageHandler
def my_handler(client, message):
print(message)
app.add_handler(MessageHandler(my_handler, Filters.audio))
Combining Filters
-----------------
Filters can also be used in a more advanced way by combining more filters together using bitwise operators:
- Use ``~`` to invert a filter (behaves like the ``not`` operator).
- Use ``&`` and ``|`` to merge two filters (behave like ``and``, ``or`` operators respectively).
Here are some examples:
- Message is a **text** message **and** is **not edited**.
.. code-block:: python
@app.on_message(Filters.text & ~Filters.edited)
def my_handler(client, message):
print(message)
- Message is a **sticker** **and** is coming from a **channel or** a **private** chat.
.. code-block:: python
@app.on_message(Filters.sticker & (Filters.channel | Filters.private))
def my_handler(client, message):
print(message)
Advanced Filters
----------------
Some filters, like :obj:`command() <pyrogram.Filters.command>` or :obj:`regex() <pyrogram.Filters.regex>`
can also accept arguments:
- Message is either a */start* or */help* **command**.
.. code-block:: python
@app.on_message(Filters.command(["start", "help"]))
def my_handler(client, message):
print(message)
- Message is a **text** message matching the given **regex** pattern.
.. code-block:: python
@app.on_message(Filters.regex("pyrogram"))
def my_handler(client, message):
print(message)
More handlers using different filters can also live together.
.. code-block:: python
@app.on_message(Filters.command("start"))
def start_command(client, message):
print("This is the /start command")
@app.on_message(Filters.command("help"))
def help_command(client, message):
print("This is the /help command")
@app.on_message(Filters.chat("PyrogramChat"))
def from_pyrogramchat(client, message):
print("New message in @PyrogramChat")
Handler Groups
--------------
If you register handlers with overlapping filters, only the first one is executed and any other handler will be ignored.
In order to process the same message more than once, you can register your handler in a different group.
Groups are identified by a number (number 0 being the default) and are sorted. This means that a lower group number has
a higher priority.
For example, in:
.. code-block:: python
@app.on_message(Filters.text | Filters.sticker)
def text_or_sticker(client, message):
print("Text or Sticker")
@app.on_message(Filters.text)
def just_text(client, message):
print("Just Text")
``just_text`` is never executed. To enable it, simply register the function using a different group:
.. code-block:: python
@app.on_message(Filters.text, group=1)
def just_text(client, message):
print("Just Text")
or, if you want ``just_text`` to be fired *before* ``text_or_sticker``:
.. code-block:: python
@app.on_message(Filters.text, group=-1)
def just_text(client, message):
print("Just Text")

68
docs/source/sitemap.py Normal file
View File

@ -0,0 +1,68 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <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
import re
canonical = "https://docs.pyrogram.ml"
dirs = {
"start": ("weekly", 0.9),
"resources": ("weekly", 0.8),
"pyrogram": ("weekly", 0.8),
"functions": ("monthly", 0.7),
"types": ("monthly", 0.7),
"errors": ("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 = [(canonical, now(), "weekly", 1.0)]
def search(path):
try:
for j in os.listdir(path):
search("{}/{}".format(path, j))
except NotADirectoryError:
d = path.split("/")[0]
path = "{}/{}".format(canonical, path.split(".")[0])
path = re.sub("^(.+)/index$", "\g<1>", path)
urls.append((path, now(), dirs[d][0], dirs[d][1]))
for i in dirs.keys():
search(i)
for i in urls:
f.write(" <url>\n")
f.write(" <loc>{}</loc>\n".format(i[0]))
f.write(" <lastmod>{}</lastmod>\n".format(i[1]))
f.write(" <changefreq>{}</changefreq>\n".format(i[2]))
f.write(" <priority>{}</priority>\n".format(i[3]))
f.write(" </url>\n\n")
f.write("</urlset>")

View File

@ -0,0 +1,97 @@
Basic Usage
===========
.. note::
All the snippets below assume you have successfully created and started a :class:`Client <pyrogram.Client>`
instance. You also must be authorized, that is, a valid *.session file does exist in your working directory.
Simple API Access
-----------------
The easiest way to interact with the Telegram API is via the :class:`Client <pyrogram.Client>` class, which
exposes `Bot API-like`_ methods:
- Get information about the authorized user:
.. code-block:: python
print(app.get_me())
- Send a message to yourself (Saved Messages):
.. code-block:: python
app.send_message("me", "Hi there! I'm using Pyrogram")
- Upload a new photo (with caption):
.. code-block:: python
app.send_photo("me", "/home/dan/perla.jpg", "Cute!")
.. seealso:: For a complete list of the available methods and an exhaustive description for each of them, have a look
at the :class:`Client <pyrogram.Client>` class.
.. _using-raw-functions:
Using Raw Functions
-------------------
If you want **complete**, low-level access to the Telegram API you have to use the raw
:mod:`functions <pyrogram.api.functions>` and :mod:`types <pyrogram.api.types>` exposed by the ``pyrogram.api``
package and call any Telegram API method you wish using the :meth:`send() <pyrogram.Client.send>` method provided by
the Client class.
Here some examples:
- Update first name, last name and bio:
.. code-block:: python
from pyrogram.api import functions
...
app.send(
functions.account.UpdateProfile(
first_name="Dan", last_name="Tès",
about="Bio written from Pyrogram"
)
)
- Share your Last Seen time only with your contacts:
.. code-block:: python
from pyrogram.api import functions, types
...
app.send(
functions.account.SetPrivacy(
key=types.InputPrivacyKeyStatusTimestamp(),
rules=[types.InputPrivacyValueAllowContacts()]
)
)
- Invite users to your channel/supergroup:
.. code-block:: python
from pyrogram.api import functions, types
...
app.send(
functions.channels.InviteToChannel(
channel=app.resolve_peer(123456789), # ID or 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
]
)
)
.. _`Bot API-like`: https://core.telegram.org/bots/api#available-methods

View File

@ -0,0 +1,100 @@
Project Setup
=============
This section provides all the information you need to setup your project with Pyrogram.
There are a few steps you have to follow before you can actually use the library to make API calls.
API Keys
--------
The very first step requires you to obtain a valid Telegram API key.
If you already have one you can skip this step, otherwise:
#. Visit https://my.telegram.org/apps and log in with your Telegram Account.
#. Fill out the form to register a new Telegram application.
#. Done. The Telegram API key consists of two parts: the **App api_id** and the **App api_hash**.
.. important:: This key should be kept secret.
Configuration
-------------
There are two ways to configure a Pyrogram application project, and you can choose the one that fits better for you:
- Create a new ``config.ini`` file at the root of your working directory, copy-paste the following and replace the
**api_id** and **api_hash** values with `your own <#api-keys>`_. This is the preferred method because allows you
to keep your credentials out of your code without having to deal with how to load them:
.. code-block:: ini
[pyrogram]
api_id = 12345
api_hash = 0123456789abcdef0123456789abcdef
- 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:
.. code-block:: python
from pyrogram import Client
app = Client(
session_name="my_account",
api_id=12345
api_hash="0123456789abcdef0123456789abcdef"
)
.. note:: The examples below assume you have created a ``config.ini`` file, thus they won't show the *api_id*
and *api_hash* parameters usage.
User Authorization
------------------
In order to use the API, Telegram requires that Users be authorized via their phone numbers.
Pyrogram automatically manages this access, all you need to do is create an instance of
the :class:`Client <pyrogram.Client>` class by passing to it a ``session_name`` of your choice
(e.g.: "my_account") and call the :meth:`start() <pyrogram.Client.start>` method:
.. code-block:: python
from pyrogram import Client
app = Client("my_account")
app.start()
This starts an interactive shell asking you to input your **phone number** (including your `Country Code`_)
and the **phone code** you will receive:
.. code::
Enter phone number: +39**********
Is "+39**********" correct? (y/n): y
Enter phone code: 32768
After successfully authorizing yourself, a new file called ``my_account.session`` will be created allowing
Pyrogram executing API calls with your identity. This file will be loaded again when you restart your app,
and as long as you keep the session alive, Pyrogram won't ask you again to enter your phone number.
.. important:: Your *.session file(s) must be kept secret.
Bot Authorization
-----------------
Being written entirely from the ground up, Pyrogram is also able to authorize Bots.
Bots are a special kind of users which also make use of MTProto. This means that you can use Pyrogram to
execute API calls with a Bot identity.
Instead of phone numbers, Bots are authorized via their tokens which are created by BotFather_:
.. code-block:: python
from pyrogram import Client
app = Client("123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11")
app.start()
That's all, no further action is needed. The session file will be named after the Bot user_id, which is
``123456.session`` for the example above.
.. _`Country Code`: https://en.wikipedia.org/wiki/List_of_country_calling_codes
.. _BotFather: https://t.me/botfather

View File

@ -0,0 +1,37 @@
Quick Installation
==================
- The easiest way to install and upgrade Pyrogram is by using **pip**:
.. code-block:: bash
$ pip3 install --upgrade pyrogram
- or, with TgCrypto_ (recommended):
.. code-block:: bash
$ pip3 install --upgrade pyrogram[tgcrypto]
Bleeding Edge
-------------
If you want the latest development version of the library, you can install it with:
.. code-block:: bash
$ pip3 install --upgrade git+https://github.com/pyrogram/pyrogram.git
Verifying
---------
To verify that Pyrogram is correctly installed, open a Python shell and import it.
If no error shows up you are good to go.
.. code-block:: bash
>>> import pyrogram
>>> pyrogram.__version__
'0.7.0'
.. _TgCrypto: https://docs.pyrogram.ml/resources/TgCrypto

View File

@ -5,16 +5,13 @@ You can start with [hello_world.py](https://github.com/pyrogram/pyrogram/blob/ma
with the more advanced examples.
Every script is working right away (provided you correctly set up your credentials), meaning
you can simply copy-paste and run, the only things you have to change are the target chats (username, id) and file paths for
sending media (photo, video, ...).
you can simply copy-paste and run, the only things you have to change are your session names and the target chats
- [**hello_world.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/hello_world.py)
- [**echo_bot.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/echo_bot.py)
- [**get_history.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/get_history.py)
- [**get_participants.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/get_participants.py)
- [**get_participants2.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/get_participants2.py)
- [**hello_world.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/hello_world.py)
- [**inline_bots.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/inline_bots.py)
- [**updates.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/updates.py)
- [**simple_echo.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/simple_echo.py)
- [**advanced_echo.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/advanced_echo.py)
- [**advanced_echo2.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/advanced_echo2.py)
- [**raw_update_handler.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/raw_update_handler.py)
- [**welcome_bot.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/welcome_bot.py)

View File

@ -1,64 +0,0 @@
from pyrogram import Client
from pyrogram.api import types
"""This is a more advanced example bot that will reply to all private and basic groups text messages
by also mentioning the Users.
Beware! This script will make you reply to ALL new messages in private chats and in every basic group you are in.
Make sure you add an extra check to filter them:
# Filter Groups by ID
if message.to_id.chat_id == MY_GROUP_ID:
...
"""
def update_handler(client, update, users, chats):
if isinstance(update, types.UpdateNewMessage): # Filter by UpdateNewMessage (PM and Chats)
message = update.message
if isinstance(message, types.Message): # Filter by Message to exclude MessageService and MessageEmpty
if isinstance(message.to_id, types.PeerUser): # Private Messages
text = '[{}](tg://user?id={}) said "{}" to me ([{}](tg://user?id={}))'.format(
users[message.from_id].first_name,
users[message.from_id].id,
message.message,
users[message.to_id.user_id].first_name,
users[message.to_id.user_id].id
)
client.send_message(
message.from_id, # Send the message to the private chat (from_id)
text,
reply_to_message_id=message.id
)
else: # Group chats
text = '[{}](tg://user?id={}) said "{}" in **{}** group'.format(
users[message.from_id].first_name,
users[message.from_id].id,
message.message,
chats[message.to_id.chat_id].title
)
client.send_message(
message.to_id, # Send the message to the group chat (to_id)
text,
reply_to_message_id=message.id
)
def main():
# Pyrogram setup
client = Client("example")
# Set the update_handler callback function
client.set_update_handler(update_handler)
client.start()
# Blocks the program execution until you press CTRL+C then
# automatically stops the Client by closing the underlying connection
client.idle()
if __name__ == "__main__":
main()

View File

@ -1,55 +0,0 @@
from pyrogram import Client
from pyrogram.api import types
"""This example is similar to advanced_echo.py, except for the fact that it will reply to Supergroup text messages only.
Beware! This script will make you reply to ALL new messages in every single supergroup you are in.
Make sure you add an extra check to filter them:
# Filter Supergroups by ID
if message.to_id.channel_id == MY_SUPERGROUP_ID:
...
# Filter Supergroups by Username
if chats[message.to_id.channel_id].username == MY_SUPERGROUP_USERNAME:
...
"""
def update_handler(client, update, users, chats):
# Channels and Supergroups share the same type (Channel). The .megagroup field is used to tell them apart, and is
# True for Supegroups, False for Channels.
if isinstance(update, types.UpdateNewChannelMessage): # Filter by UpdateNewChannelMessage (Channels/Supergroups)
message = update.message
if isinstance(message, types.Message): # Filter by Message to exclude MessageService and MessageEmpty
if chats[message.to_id.channel_id].megagroup: # Only handle messages from Supergroups not Channels
text = '[{}](tg://user?id={}) said "{}" in **{}** supergroup'.format(
users[message.from_id].first_name,
users[message.from_id].id,
message.message,
chats[message.to_id.channel_id].title
)
client.send_message(
message.to_id,
text,
reply_to_message_id=message.id
)
def main():
# Pyrogram setup
client = Client("example")
# Set the update_handler callback function
client.set_update_handler(update_handler)
client.start()
# Blocks the program execution until you press CTRL+C then
# automatically stops the Client by closing the underlying connection
client.idle()
if __name__ == "__main__":
main()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

17
examples/echo_bot.py Normal file
View File

@ -0,0 +1,17 @@
from pyrogram import Client, Filters
"""This simple echo bot replies to every private text message"""
app = Client("my_account")
@app.on_message(Filters.text & Filters.private)
def echo(client, message):
client.send_message(
message.chat.id, message.text,
reply_to_message_id=message.message_id
)
app.start()
app.idle()

View File

@ -4,8 +4,8 @@ from pyrogram import Client
from pyrogram.api import functions
from pyrogram.api.errors import FloodWait
client = Client("example")
client.start()
app = Client("my_account")
app.start()
target = "me" # "me" refers to your own chat (Saved Messages)
history = [] # List that will contain all the messages of the target chat
@ -14,9 +14,9 @@ offset = 0 # Offset starts at 0
while True:
try:
messages = client.send(
messages = app.send(
functions.messages.GetHistory(
client.resolve_peer(target),
app.resolve_peer(target),
0, 0, offset, limit, 0, 0, 0
)
)
@ -31,7 +31,7 @@ while True:
history.extend(messages.messages)
offset += limit
client.stop()
app.stop()
# Now the "history" list contains all the messages sorted by date in
# descending order (from the most recent to the oldest one)

View File

@ -4,8 +4,8 @@ from pyrogram import Client
from pyrogram.api import functions, types
from pyrogram.api.errors import FloodWait
client = Client("example")
client.start()
app = Client("my_account")
app.start()
target = "username" # Target channel/supergroup
users = [] # List that will contain all the users of the target chat
@ -14,9 +14,9 @@ offset = 0 # Offset starts at 0
while True:
try:
participants = client.send(
participants = app.send(
functions.channels.GetParticipants(
channel=client.resolve_peer(target),
channel=app.resolve_peer(target),
filter=types.ChannelParticipantsSearch(""), # Filter by empty string (search for all)
offset=offset,
limit=limit,
@ -35,6 +35,6 @@ while True:
users.extend(participants.users)
offset += limit
client.stop()
app.stop()
# Now the "users" list contains all the members of the target chat

View File

@ -15,8 +15,8 @@ This can be further improved by also searching for non-ascii characters (e.g.: J
as some user names may not contain ascii letters at all.
"""
client = Client("example")
client.start()
app = Client("my_account")
app.start()
target = "username" # Target channel/supergroup username or id
users = {} # To ensure uniqueness, users will be stored in a dictionary with user_id as key
@ -31,9 +31,9 @@ for q in queries:
while True:
try:
participants = client.send(
participants = app.send(
functions.channels.GetParticipants(
channel=client.resolve_peer(target),
channel=app.resolve_peer(target),
filter=types.ChannelParticipantsSearch(q),
offset=offset,
limit=limit,
@ -60,4 +60,4 @@ for q in queries:
print("Total users: {}".format(len(users)))
client.stop()
app.stop()

View File

@ -1,19 +1,18 @@
from pyrogram import Client
"""This example demonstrates a simple API methods usage"""
# Create a new Client
client = Client("example")
app = Client("my_account")
# Start the Client
client.start()
app.start()
# Send a message to yourself, Markdown is enabled by default
client.send_message("me", "Hi there! I'm using **Pyrogram**")
# Send a photo with a formatted caption to yourself
client.send_photo("me", "data/pyrogram.png", "__This is a formatted__ **caption**")
app.send_message("me", "Hi there! I'm using **Pyrogram**")
# Send a location to yourself
client.send_location("me", 51.500729, -0.124583)
app.send_location("me", 51.500729, -0.124583)
# Stop the client
client.stop()
app.stop()

View File

@ -1,15 +1,15 @@
from pyrogram import Client
# Create a new Client
client = Client("example")
app = Client("my_account")
# Start the Client
client.start()
app.start()
# Get bot results for "Fuzz Universe" from the inline bot @vid
bot_results = client.get_inline_bot_results("vid", "Fuzz Universe")
bot_results = app.get_inline_bot_results("vid", "Fuzz Universe")
# Send the first result (bot_results.results[0]) to your own chat (Saved Messages)
client.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)
# Stop the client
client.stop()
app.stop()

View File

@ -0,0 +1,14 @@
from pyrogram import Client
"""This example shows how to handle raw updates"""
app = Client("my_account")
@app.on_raw_update()
def raw(client, update, users, chats):
print(update)
app.start()
app.idle()

View File

@ -1,34 +0,0 @@
from pyrogram import Client
from pyrogram.api import types
"""This simple example bot will reply to all private text messages"""
def update_handler(client, update, users, chats):
if isinstance(update, types.UpdateNewMessage): # Filter by UpdateNewMessage (Private Messages)
message = update.message # type: types.Message
if isinstance(message, types.Message): # Filter by Message to exclude MessageService and MessageEmpty
if isinstance(message.to_id, types.PeerUser): # Private Messages (Message from user)
client.send_message(
chat_id=message.from_id,
text=message.message,
reply_to_message_id=message.id
)
def main():
# Pyrogram setup
client = Client("example")
# Set the update_handler callback function
client.set_update_handler(update_handler)
client.start()
# Blocks the program execution until you press CTRL+C then
# automatically stops the Client by closing the underlying connection
client.idle()
if __name__ == "__main__":
main()

View File

@ -1,25 +0,0 @@
from pyrogram import Client
# This function will be called every time a new Update is received from Telegram
def update_handler(client, update, users, chats):
# Send EVERY update that arrives to your own chat (Saved Messages)
# Use triple backticks to make the text look nicer.
client.send_message("me", "```\n" + str(update) + "```")
def main():
# Pyrogram setup
client = Client("example")
# Set the update_handler callback function
client.set_update_handler(update_handler)
client.start()
# Blocks the program execution until you press CTRL+C then
# automatically stops the Client by closing the underlying connection
client.idle()
if __name__ == "__main__":
main()

View File

@ -1,52 +1,32 @@
from pyrogram import Client, Emoji
from pyrogram.api import types
from pyrogram import Client, Emoji, Filters
"""
This is the Welcome Bot in @PyrogramChat
The code is commented to help you understand each part
It also uses the Emoji module to easily add emojis in your text messages
It uses the Emoji module to easily add emojis in your text messages and Filters
to make it only work for specific messages in a specific chat
"""
# Your Supergroup ID
SUPERGROUP_ID = 1387666944
app = Client("my_account")
def update_handler(client, update, users, chats):
# Supergroup messages are contained in the "UpdateNewChannelMessage" update type
if isinstance(update, types.UpdateNewChannelMessage):
message = update.message
# When a user joins, a "MessageService" is received
if isinstance(message, types.MessageService):
# Check if the message is sent to your SUPERGROUP_ID
if message.to_id.channel_id == SUPERGROUP_ID:
# A "MessageService" contains the "action" field.
# The action for user joins is "MessageActionChatAddUser" if the user
# joined using the username, otherwise is "MessageActionChatJoinedByLink" if
# the user joined a private group by link
if isinstance(message.action, (types.MessageActionChatAddUser, types.MessageActionChatJoinedByLink)):
# Now send the welcome message. Extra info about a user (such as the first_name, username, ...)
# are contained in the users dictionary and can be accessed by the user ID
client.send_message(
SUPERGROUP_ID,
"{} Welcome to [Pyrogram](https://docs.pyrogram.ml/)'s "
"group chat, [{}](tg://user?id={})!".format(
Emoji.SPARKLES, # Add an emoji
users[message.from_id].first_name,
users[message.from_id].id
),
reply_to_message_id=message.id,
disable_web_page_preview=True
)
@app.on_message(Filters.chat("PyrogramChat") & Filters.new_chat_members)
def welcome(client, message):
new_members = ", ".join([
"[{}](tg://user?id={})".format(i.first_name, i.id)
for i in message.new_chat_members]
)
text = "{} Welcome to [Pyrogram](https://docs.pyrogram.ml/)'s group chat {}!".format(
Emoji.SPARKLES,
new_members
)
client.send_message(
message.chat.id, text,
reply_to_message_id=message.message_id,
disable_web_page_preview=True
)
def main():
client = Client("example")
client.set_update_handler(update_handler)
client.start()
client.idle()
if __name__ == "__main__":
main()
app.start()
app.idle()

View File

@ -23,12 +23,16 @@ __copyright__ = "Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance
"e" if sys.getfilesystemencoding() != "utf-8" else "\xe8"
)
__license__ = "GNU Lesser General Public License v3 or later (LGPLv3+)"
__version__ = "0.6.5"
__version__ = "0.7.0"
from .api.errors import Error
from .api.types.pyrogram import *
from .client import ChatAction
from .client import Client
from .client import ParseMode
from .client.input_media import InputMedia
from .client.input_media_photo import InputMediaPhoto
from .client.input_media_video import InputMediaVideo
from .client.input_phone_contact import InputPhoneContact
from .client import Emoji
from .client.handlers import MessageHandler, RawUpdateHandler
from .client.filters import Filters

View File

@ -37,6 +37,9 @@ class Object:
def __str__(self) -> str:
return dumps(self, cls=Encoder, indent=4)
def __bool__(self) -> bool:
return True
def __eq__(self, other) -> bool:
return self.__dict__ == other.__dict__
@ -50,6 +53,15 @@ class Object:
return getattr(self, item)
def remove_none(obj):
if isinstance(obj, (list, tuple, set)):
return type(obj)(remove_none(x) for x in obj if x is not None)
elif isinstance(obj, dict):
return type(obj)((remove_none(k), remove_none(v)) for k, v in obj.items() if k is not None and v is not None)
else:
return obj
class Encoder(JSONEncoder):
def default(self, o: Object):
try:
@ -60,7 +72,10 @@ class Encoder(JSONEncoder):
else:
return repr(o)
return OrderedDict(
[("_", objects.get(getattr(o, "ID", None), None))]
+ [i for i in content.items()]
)
if "pyrogram" in objects.get(getattr(o, "ID", "")):
return remove_none(OrderedDict([i for i in content.items()]))
else:
return OrderedDict(
[("_", objects.get(getattr(o, "ID", None), None))]
+ [i for i in content.items()]
)

View File

@ -18,5 +18,5 @@
from .chat_action import ChatAction
from .client import Client
from .parse_mode import ParseMode
from .emoji import Emoji
from .parse_mode import ParseMode

View File

@ -21,7 +21,7 @@ from pyrogram.api import types
class ChatAction:
"""This class provides a convenient access to all Chat Actions available.
It is intended to be used with :obj:`pyrogram.Client.send_chat_action`.
Chat Actions are intended to be used with :meth:`send_chat_action() <pyrogram.Client.send_chat_action>`.
"""
CANCEL = types.SendMessageCancelAction

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,19 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <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/>.
from .dispatcher import Dispatcher

View File

@ -0,0 +1,159 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <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 logging
import threading
from collections import OrderedDict
from queue import Queue
from threading import Thread
import pyrogram
from pyrogram.api import types
from .. import message_parser
from ..handlers import RawUpdateHandler, MessageHandler
log = logging.getLogger(__name__)
class Dispatcher:
MESSAGE_UPDATES = (
types.UpdateNewMessage,
types.UpdateNewChannelMessage
)
EDIT_UPDATES = (
types.UpdateEditMessage,
types.UpdateEditChannelMessage
)
ALLOWED_UPDATES = MESSAGE_UPDATES + EDIT_UPDATES
def __init__(self, client, workers):
self.client = client
self.workers = workers
self.workers_list = []
self.updates = Queue()
self.groups = OrderedDict()
def start(self):
for i in range(self.workers):
self.workers_list.append(
Thread(
target=self.update_worker,
name="UpdateWorker#{}".format(i + 1)
)
)
self.workers_list[-1].start()
def stop(self):
for _ in range(self.workers):
self.updates.put(None)
for i in self.workers_list:
i.join()
def add_handler(self, handler, group: int):
if group not in self.groups:
self.groups[group] = []
self.groups = OrderedDict(sorted(self.groups.items()))
self.groups[group].append(handler)
def dispatch(self, update, users: dict = None, chats: dict = None, is_raw: bool = False):
for group in self.groups.values():
for handler in group:
if is_raw:
if not isinstance(handler, RawUpdateHandler):
continue
args = (self.client, update, users, chats)
else:
if not isinstance(handler, MessageHandler):
continue
message = (update.message
or update.channel_post
or update.edited_message
or update.edited_channel_post)
if not handler.check(message):
continue
args = (self.client, message)
handler.callback(*args)
break
def update_worker(self):
name = threading.current_thread().name
log.debug("{} started".format(name))
while True:
update = self.updates.get()
if update is None:
break
try:
users = {i.id: i for i in update[1]}
chats = {i.id: i for i in update[2]}
update = update[0]
self.dispatch(update, users=users, chats=chats, is_raw=True)
if isinstance(update, Dispatcher.ALLOWED_UPDATES):
if isinstance(update.message, types.Message):
parser = message_parser.parse_message
elif isinstance(update.message, types.MessageService):
parser = message_parser.parse_message_service
else:
continue
message = parser(
self.client,
update.message,
users,
chats
)
else:
continue
is_edited_message = isinstance(update, Dispatcher.EDIT_UPDATES)
self.dispatch(
pyrogram.Update(
update_id=0,
message=((message if message.chat.type != "channel"
else None) if not is_edited_message
else None),
edited_message=((message if message.chat.type != "channel"
else None) if is_edited_message
else None),
channel_post=((message if message.chat.type == "channel"
else None) if not is_edited_message
else None),
edited_channel_post=((message if message.chat.type == "channel"
else None) if is_edited_message
else None)
)
)
except Exception as e:
log.error(e, exc_info=True)
log.debug("{} stopped".format(name))

View File

@ -0,0 +1,19 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <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/>.
from .filters import Filters

View File

@ -0,0 +1,57 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <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/>.
class Filter:
def __call__(self, message):
raise NotImplementedError
def __invert__(self):
return InvertFilter(self)
def __and__(self, other):
return AndFilter(self, other)
def __or__(self, other):
return OrFilter(self, other)
class InvertFilter(Filter):
def __init__(self, base):
self.base = base
def __call__(self, message):
return not self.base(message)
class AndFilter(Filter):
def __init__(self, base, other):
self.base = base
self.other = other
def __call__(self, message):
return self.base(message) and self.other(message)
class OrFilter(Filter):
def __init__(self, base, other):
self.base = base
self.other = other
def __call__(self, message):
return self.base(message) or self.other(message)

View File

@ -0,0 +1,235 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <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 re
from .filter import Filter
def build(name: str, func: callable, **kwargs) -> type:
d = {"__call__": func}
d.update(kwargs)
return type(name, (Filter,), d)()
class Filters:
"""This class provides access to all Filters available in Pyrogram.
Filters are intended to be used with the :obj:`MessageHandler <pyrogram.MessageHandler>`."""
text = build("Text", lambda _, m: bool(m.text and not m.text.startswith("/")))
"""Filter text messages."""
reply = build("Reply", lambda _, m: bool(m.reply_to_message))
"""Filter messages that are replies to other messages."""
forwarded = build("Forwarded", lambda _, m: bool(m.forward_date))
"""Filter messages that are forwarded."""
caption = build("Caption", lambda _, m: bool(m.caption))
"""Filter media messages that contain captions."""
edited = build("Edited", lambda _, m: bool(m.edit_date))
"""Filter edited messages."""
audio = build("Audio", lambda _, m: bool(m.audio))
"""Filter messages that contain :obj:`Audio <pyrogram.api.types.pyrogram.Audio>` objects."""
document = build("Document", lambda _, m: bool(m.document))
"""Filter messages that contain :obj:`Document <pyrogram.api.types.pyrogram.Document>` objects."""
photo = build("Photo", lambda _, m: bool(m.photo))
"""Filter messages that contain :obj:`Photo <pyrogram.api.types.pyrogram.PhotoSize>` objects."""
sticker = build("Sticker", lambda _, m: bool(m.sticker))
"""Filter messages that contain :obj:`Sticker <pyrogram.api.types.pyrogram.Sticker>` objects."""
video = build("Video", lambda _, m: bool(m.video))
"""Filter messages that contain :obj:`Video <pyrogram.api.types.pyrogram.Video>` objects."""
voice = build("Voice", lambda _, m: bool(m.voice))
"""Filter messages that contain :obj:`Voice <pyrogram.api.types.pyrogram.Voice>` note objects."""
video_note = build("Voice", lambda _, m: bool(m.video_note))
"""Filter messages that contain :obj:`VideoNote <pyrogram.api.types.pyrogram.VideoNote>` objects."""
contact = build("Contact", lambda _, m: bool(m.contact))
"""Filter messages that contain :obj:`Contact <pyrogram.api.types.pyrogram.Contact>` objects."""
location = build("Location", lambda _, m: bool(m.location))
"""Filter messages that contain :obj:`Location <pyrogram.api.types.pyrogram.Location>` objects."""
venue = build("Venue", lambda _, m: bool(m.venue))
"""Filter messages that contain :obj:`Venue <pyrogram.api.types.pyrogram.Venue>` objects."""
private = build("Private", lambda _, m: bool(m.chat.type == "private"))
"""Filter messages sent in private chats."""
group = build("Group", lambda _, m: bool(m.chat.type in {"group", "supergroup"}))
"""Filter messages sent in group or supergroup chats."""
channel = build("Channel", lambda _, m: bool(m.chat.type == "channel"))
"""Filter messages sent in channels."""
new_chat_members = build("NewChatMembers", lambda _, m: bool(m.new_chat_members))
"""Filter service messages for new chat members."""
left_chat_member = build("LeftChatMember", lambda _, m: bool(m.left_chat_member))
"""Filter service messages for members that left the chat."""
new_chat_title = build("NewChatTitle", lambda _, m: bool(m.new_chat_title))
"""Filter service messages for new chat titles."""
new_chat_photo = build("NewChatPhoto", lambda _, m: bool(m.new_chat_photo))
"""Filter service messages for new chat photos."""
delete_chat_photo = build("DeleteChatPhoto", lambda _, m: bool(m.delete_chat_photo))
"""Filter service messages for deleted photos."""
group_chat_created = build("GroupChatCreated", lambda _, m: bool(m.group_chat_created))
"""Filter service messages for group chat creations."""
supergroup_chat_created = build("SupergroupChatCreated", lambda _, m: bool(m.supergroup_chat_created))
"""Filter service messages for supergroup chat creations."""
channel_chat_created = build("ChannelChatCreated", lambda _, m: bool(m.channel_chat_created))
"""Filter service messages for channel chat creations."""
migrate_to_chat_id = build("MigrateToChatId", lambda _, m: bool(m.migrate_to_chat_id))
"""Filter service messages that contain migrate_to_chat_id."""
migrate_from_chat_id = build("MigrateFromChatId", lambda _, m: bool(m.migrate_from_chat_id))
"""Filter service messages that contain migrate_from_chat_id."""
pinned_message = build("PinnedMessage", lambda _, m: bool(m.pinned_message))
"""Filter service messages for pinned messages."""
@staticmethod
def command(command: str or list):
"""Filter commands, i.e.: text messages starting with '/'.
Args:
command (``str`` | ``list``):
The command or list of commands as strings the filter should look for.
"""
return build(
"Command",
lambda _, m: bool(
m.text
and m.text.startswith("/")
and (m.text[1:].split()[0] in _.c)
),
c=(
{command}
if not isinstance(command, list)
else {c for c in command}
)
)
@staticmethod
def regex(pattern, flags: int = 0):
"""Filter messages that match a given RegEx pattern.
Args:
pattern (``str``):
The RegEx pattern.
flags (``int``, optional):
RegEx flags.
"""
return build(
"Regex", lambda _, m: bool(_.p.search(m.text or "")),
p=re.compile(pattern, flags)
)
@staticmethod
def user(user: int or str or list):
"""Filter messages coming from specific users.
Args:
user (``int`` | ``str`` | ``list``):
The user or list of user IDs (int) or usernames (str) the filter should look for.
"""
return build(
"User",
lambda _, m: bool(m.from_user
and (m.from_user.id in _.u
or (m.from_user.username
and m.from_user.username.lower() in _.u))),
u=(
{user.lower().strip("@") if type(user) is str else user}
if not isinstance(user, list)
else {i.lower().strip("@") if type(i) is str else i for i in user}
)
)
@staticmethod
def chat(chat: int or str or list):
"""Filter messages coming from specific chats.
Args:
chat (``int`` | ``str`` | ``list``):
The chat or list of chat IDs (int) or usernames (str) the filter should look for.
"""
return build(
"Chat",
lambda _, m: bool(m.chat
and (m.chat.id in _.c
or (m.chat.username
and m.chat.username.lower() in _.c))),
c=(
{chat.lower().strip("@") if type(chat) is str else chat}
if not isinstance(chat, list)
else {i.lower().strip("@") if type(i) is str else i for i in chat}
)
)
service = build(
"Service",
lambda _, m: bool(
Filters.new_chat_members(m)
or Filters.left_chat_member(m)
or Filters.new_chat_title(m)
or Filters.new_chat_photo(m)
or Filters.delete_chat_photo(m)
or Filters.group_chat_created(m)
or Filters.supergroup_chat_created(m)
or Filters.channel_chat_created(m)
or Filters.migrate_to_chat_id(m)
or Filters.migrate_from_chat_id(m)
or Filters.pinned_message(m)
)
)
"""Filter all service messages."""
media = build(
"Media",
lambda _, m: bool(
Filters.audio(m)
or Filters.document(m)
or Filters.photo(m)
or Filters.sticker(m)
or Filters.video(m)
or Filters.voice(m)
or Filters.video_note(m)
or Filters.contact(m)
or Filters.location(m)
or Filters.venue(m)
)
)
"""Filter all media messages."""

View File

@ -0,0 +1,19 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <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/>.
from .handlers import MessageHandler, RawUpdateHandler

View File

@ -0,0 +1,23 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <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/>.
class Handler:
def __init__(self, callback: callable, filters=None):
self.callback = callback
self.filters = filters

View File

@ -0,0 +1,95 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <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/>.
from .handler import Handler
class MessageHandler(Handler):
"""The Message handler class. Used to handle text, media and service messages coming from
any chat (private, group, channel). It is intended to be used with
:meth:`add_handler() <pyrogram.Client.add_handler>`
Args:
callback (``callable``):
Pass a function that will be called when a new Message arrives. It takes *(client, message)*
as positional arguments (look at the section below for a detailed description).
filters (:obj:`Filters <pyrogram.Filters>`):
Pass one or more filters to allow only a subset of messages to be passed
in your callback function.
Other parameters:
client (:obj:`Client <pyrogram.Client>`):
The Client itself, useful when you want to call other API methods inside the message handler.
message (:obj:`Message <pyrogram.api.types.pyrogram.Message>`):
The received message.
"""
def __init__(self, callback: callable, filters=None):
super().__init__(callback, filters)
def check(self, message):
return (
self.filters(message)
if self.filters
else True
)
class RawUpdateHandler(Handler):
"""The Raw Update handler class. Used to handle raw updates. It is intended to be used with
:meth:`add_handler() <pyrogram.Client.add_handler>`
Args:
callback (``callable``):
A function that will be called when a new update is received from the server. It takes
*(client, update, users, chats)* as positional arguments (look at the section below for
a detailed description).
Other Parameters:
client (:class:`Client <pyrogram.Client>`):
The Client itself, useful when you want to call other API methods inside the update handler.
update (``Update``):
The received update, which can be one of the many single Updates listed in the *updates*
field you see in the :obj:`Update <pyrogram.api.types.Update>` type.
users (``dict``):
Dictionary of all :obj:`User <pyrogram.api.types.User>` mentioned in the update.
You can access extra info about the user (such as *first_name*, *last_name*, etc...) by using
the IDs you find in the *update* argument (e.g.: *users[1768841572]*).
chats (``dict``):
Dictionary of all :obj:`Chat <pyrogram.api.types.Chat>` and
:obj:`Channel <pyrogram.api.types.Channel>` mentioned in the update.
You can access extra info about the chat (such as *title*, *participants_count*, etc...)
by using the IDs you find in the *update* argument (e.g.: *chats[1701277281]*).
Note:
The following Empty or Forbidden types may exist inside the *users* and *chats* dictionaries.
They mean you have been blocked by the user or banned from the group/channel.
- :obj:`UserEmpty <pyrogram.api.types.UserEmpty>`
- :obj:`ChatEmpty <pyrogram.api.types.ChatEmpty>`
- :obj:`ChatForbidden <pyrogram.api.types.ChatForbidden>`
- :obj:`ChannelForbidden <pyrogram.api.types.ChannelForbidden>`
"""
def __init__(self, callback: callable):
super().__init__(callback)

View File

@ -0,0 +1,46 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <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/>.
class InputMediaPhoto:
"""This object represents a photo to be sent inside an album.
It is intended to be used with :obj:`send_media_group() <pyrogram.Client.send_media_group>`.
Args:
media (:obj:`str`):
Photo to send.
Pass a file_id as string to send a photo that exists on the Telegram servers or
pass a file path as string to upload a new photo that exists on your local machine.
Sending photo by a URL is currently unsupported.
caption (:obj:`str`, optional):
Caption of the photo to be sent, 0-200 characters
parse_mode (:obj:`str`, optional):
Use :obj:`MARKDOWN <pyrogram.ParseMode.MARKDOWN>` or :obj:`HTML <pyrogram.ParseMode.HTML>`
if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your caption.
Defaults to Markdown.
"""
def __init__(self,
media: str,
caption: str = "",
parse_mode: str = ""):
self.media = media
self.caption = caption
self.parse_mode = parse_mode

View File

@ -0,0 +1,66 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <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/>.
class InputMediaVideo:
"""This object represents a video to be sent inside an album.
It is intended to be used with :obj:`send_media_group() <pyrogram.Client.send_media_group>`.
Args:
media (:obj:`str`):
Video to send.
Pass a file_id as string to send a video that exists on the Telegram servers or
pass a file path as string to upload a new video that exists on your local machine.
Sending video by a URL is currently unsupported.
caption (:obj:`str`, optional):
Caption of the video to be sent, 0-200 characters
parse_mode (:obj:`str`, optional):
Use :obj:`MARKDOWN <pyrogram.ParseMode.MARKDOWN>` or :obj:`HTML <pyrogram.ParseMode.HTML>`
if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your caption.
Defaults to Markdown.
width (:obj:`int`, optional):
Video width.
height (:obj:`int`, optional):
Video height.
duration (:obj:`int`, optional):
Video duration.
supports_streaming (:obj:`bool`, optional):
Pass True, if the uploaded video is suitable for streaming.
"""
def __init__(self,
media: str,
caption: str = "",
parse_mode: str = "",
width: int = 0,
height: int = 0,
duration: int = 0,
supports_streaming: bool = True):
self.media = media
self.caption = caption
self.parse_mode = parse_mode
self.width = width
self.height = height
self.duration = duration
self.supports_streaming = supports_streaming

View File

@ -22,19 +22,22 @@ from pyrogram.session.internals import MsgId
class InputPhoneContact:
"""This object represents a Phone Contact to be added in your Telegram address book.
It is intended to be used with :obj:`pyrogram.Client.add_contacts`
It is intended to be used with :meth:`add_contacts() <pyrogram.Client.add_contacts>`
Args:
phone (:obj:`str`):
phone (``str``):
Contact's phone number
first_name (:obj:`str`):
first_name (``str``):
Contact's first name
last_name (:obj:`str`, optional):
last_name (``str``, optional):
Contact's last name
"""
def __init__(self, phone: str, first_name: str, last_name: str = ""):
pass
def __new__(cls, phone: str, first_name: str, last_name: str = ""):
return RawInputPhoneContact(
client_id=MsgId(),

View File

@ -0,0 +1,581 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <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/>.
from struct import pack
import pyrogram
from pyrogram.api import types, functions
from .utils import encode
# TODO: Organize the code better?
ENTITIES = {
types.MessageEntityMention.ID: "mention",
types.MessageEntityHashtag.ID: "hashtag",
types.MessageEntityBotCommand.ID: "bot_command",
types.MessageEntityUrl.ID: "url",
types.MessageEntityEmail.ID: "email",
types.MessageEntityBold.ID: "bold",
types.MessageEntityItalic.ID: "italic",
types.MessageEntityCode.ID: "code",
types.MessageEntityPre.ID: "pre",
types.MessageEntityTextUrl.ID: "text_link",
types.MessageEntityMentionName.ID: "text_mention"
}
def parse_entities(entities: list, users: dict) -> list:
output_entities = []
for entity in entities:
entity_type = ENTITIES.get(entity.ID, None)
if entity_type:
output_entities.append(pyrogram.MessageEntity(
type=entity_type,
offset=entity.offset,
length=entity.length,
url=getattr(entity, "url", None),
user=parse_user(
users.get(
getattr(entity, "user_id", None),
None
)
)
))
return output_entities
def parse_chat_photo(photo):
if not isinstance(photo, (types.UserProfilePhoto, types.ChatPhoto)):
return None
if not isinstance(photo.photo_small, types.FileLocation):
return None
if not isinstance(photo.photo_big, types.FileLocation):
return None
loc_small = photo.photo_small
loc_big = photo.photo_big
return pyrogram.ChatPhoto(
small_file_id=encode(
pack(
"<iiqqqqi", 0, loc_small.dc_id, 0, 0, loc_small.volume_id,
loc_small.secret, loc_small.local_id
)
),
big_file_id=encode(
pack(
"<iiqqqqi", 0, loc_big.dc_id, 0, 0, loc_big.volume_id,
loc_big.secret, loc_big.local_id
)
)
)
def parse_user(user: types.User) -> pyrogram.User or None:
return pyrogram.User(
id=user.id,
is_bot=user.bot,
first_name=user.first_name,
last_name=user.last_name,
username=user.username,
language_code=user.lang_code,
phone_number=user.phone,
photo=parse_chat_photo(user.photo)
) if user else None
def parse_chat(message: types.Message, users: dict, chats: dict) -> pyrogram.Chat:
if isinstance(message.to_id, types.PeerUser):
return parse_user_chat(users[message.to_id.user_id if message.out else message.from_id])
elif isinstance(message.to_id, types.PeerChat):
return parse_chat_chat(chats[message.to_id.chat_id])
else:
return parse_channel_chat(chats[message.to_id.channel_id])
def parse_user_chat(user: types.User) -> pyrogram.Chat:
return pyrogram.Chat(
id=user.id,
type="private",
username=user.username,
first_name=user.first_name,
last_name=user.last_name,
photo=parse_chat_photo(user.photo)
)
def parse_chat_chat(chat: types.Chat) -> pyrogram.Chat:
return pyrogram.Chat(
id=-chat.id,
type="group",
title=chat.title,
all_members_are_administrators=not chat.admins_enabled,
photo=parse_chat_photo(chat.photo)
)
def parse_channel_chat(channel: types.Channel) -> pyrogram.Chat:
return pyrogram.Chat(
id=int("-100" + str(channel.id)),
type="supergroup" if channel.megagroup else "channel",
title=channel.title,
username=channel.username,
photo=parse_chat_photo(channel.photo)
)
def parse_thumb(thumb: types.PhotoSize or types.PhotoCachedSize) -> pyrogram.PhotoSize or None:
if isinstance(thumb, (types.PhotoSize, types.PhotoCachedSize)):
loc = thumb.location
if isinstance(thumb, types.PhotoSize):
file_size = thumb.size
else:
file_size = len(thumb.bytes)
if isinstance(loc, types.FileLocation):
return pyrogram.PhotoSize(
file_id=encode(
pack(
"<iiqqqqi",
0,
loc.dc_id,
0,
0,
loc.volume_id,
loc.secret,
loc.local_id
)
),
width=thumb.w,
height=thumb.h,
file_size=file_size
)
# TODO: Reorganize code, maybe split parts as well
def parse_message(
client,
message: types.Message,
users: dict,
chats: dict,
replies: int = 1
) -> pyrogram.Message:
entities = parse_entities(message.entities, users)
forward_from = None
forward_from_chat = None
forward_from_message_id = None
forward_signature = None
forward_date = None
forward_header = message.fwd_from # type: types.MessageFwdHeader
if forward_header:
forward_date = forward_header.date
if forward_header.from_id:
forward_from = parse_user(users[forward_header.from_id])
else:
forward_from_chat = parse_channel_chat(chats[forward_header.channel_id])
forward_from_message_id = forward_header.channel_post
forward_signature = forward_header.post_author
photo = None
location = None
contact = None
venue = None
audio = None
voice = None
video = None
video_note = None
sticker = None
document = None
media = message.media
if media:
if isinstance(media, types.MessageMediaPhoto):
photo = media.photo
if isinstance(photo, types.Photo):
sizes = photo.sizes
photo_sizes = []
for size in sizes:
if isinstance(size, (types.PhotoSize, types.PhotoCachedSize)):
loc = size.location
if isinstance(size, types.PhotoSize):
file_size = size.size
else:
file_size = len(size.bytes)
if isinstance(loc, types.FileLocation):
photo_size = pyrogram.PhotoSize(
file_id=encode(
pack(
"<iiqqqqi",
2,
loc.dc_id,
photo.id,
photo.access_hash,
loc.volume_id,
loc.secret,
loc.local_id
)
),
width=size.w,
height=size.h,
file_size=file_size,
date=photo.date
)
photo_sizes.append(photo_size)
photo = photo_sizes
elif isinstance(media, types.MessageMediaGeo):
geo_point = media.geo
if isinstance(geo_point, types.GeoPoint):
location = pyrogram.Location(
longitude=geo_point.long,
latitude=geo_point.lat
)
elif isinstance(media, types.MessageMediaContact):
contact = pyrogram.Contact(
phone_number=media.phone_number,
first_name=media.first_name,
last_name=media.last_name or None,
user_id=media.user_id or None
)
elif isinstance(media, types.MessageMediaVenue):
venue = pyrogram.Venue(
location=pyrogram.Location(
longitude=media.geo.long,
latitude=media.geo.lat
),
title=media.title,
address=media.address,
foursquare_id=media.venue_id or None
)
elif isinstance(media, types.MessageMediaDocument):
doc = media.document
if isinstance(doc, types.Document):
attributes = {type(i): i for i in doc.attributes}
file_name = getattr(
attributes.get(
types.DocumentAttributeFilename, None
), "file_name", None
)
if types.DocumentAttributeAudio in attributes:
audio_attributes = attributes[types.DocumentAttributeAudio]
if audio_attributes.voice:
voice = pyrogram.Voice(
file_id=encode(
pack(
"<iiqq",
3,
doc.dc_id,
doc.id,
doc.access_hash
)
),
duration=audio_attributes.duration,
mime_type=doc.mime_type,
file_size=doc.size,
thumb=parse_thumb(doc.thumb),
file_name=file_name,
date=doc.date
)
else:
audio = pyrogram.Audio(
file_id=encode(
pack(
"<iiqq",
9,
doc.dc_id,
doc.id,
doc.access_hash
)
),
duration=audio_attributes.duration,
performer=audio_attributes.performer,
title=audio_attributes.title,
mime_type=doc.mime_type,
file_size=doc.size,
thumb=parse_thumb(doc.thumb),
file_name=file_name,
date=doc.date
)
elif types.DocumentAttributeAnimated in attributes:
document = pyrogram.Document(
file_id=encode(
pack(
"<iiqq",
10,
doc.dc_id,
doc.id,
doc.access_hash
)
),
thumb=parse_thumb(doc.thumb),
file_name=file_name,
mime_type=doc.mime_type,
file_size=doc.size,
date=doc.date
)
elif types.DocumentAttributeVideo in attributes:
video_attributes = attributes[types.DocumentAttributeVideo]
if video_attributes.round_message:
video_note = pyrogram.VideoNote(
file_id=encode(
pack(
"<iiqq",
13,
doc.dc_id,
doc.id,
doc.access_hash
)
),
length=video_attributes.w,
duration=video_attributes.duration,
thumb=parse_thumb(doc.thumb),
file_size=doc.size,
file_name=file_name,
mime_type=doc.mime_type,
date=doc.date
)
else:
video = pyrogram.Video(
file_id=encode(
pack(
"<iiqq",
4,
doc.dc_id,
doc.id,
doc.access_hash
)
),
width=video_attributes.w,
height=video_attributes.h,
duration=video_attributes.duration,
thumb=parse_thumb(doc.thumb),
mime_type=doc.mime_type,
file_size=doc.size,
file_name=file_name,
date=doc.date
)
elif types.DocumentAttributeSticker in attributes:
image_size_attributes = attributes[types.DocumentAttributeImageSize]
sticker_attribute = attributes[types.DocumentAttributeSticker]
if isinstance(sticker_attribute.stickerset, types.InputStickerSetID):
set_name = client.send(
functions.messages.GetStickerSet(sticker_attribute.stickerset)
).set.short_name
else:
set_name = None
sticker = pyrogram.Sticker(
file_id=encode(
pack(
"<iiqq",
8,
doc.dc_id,
doc.id,
doc.access_hash
)
),
width=image_size_attributes.w,
height=image_size_attributes.h,
thumb=parse_thumb(doc.thumb),
# TODO: mask_position
set_name=set_name,
emoji=sticker_attribute.alt,
file_size=doc.size,
mime_type=doc.mime_type,
file_name=file_name,
date=doc.date
)
else:
document = pyrogram.Document(
file_id=encode(
pack(
"<iiqq",
5,
doc.dc_id,
doc.id,
doc.access_hash
)
),
thumb=parse_thumb(doc.thumb),
file_name=file_name,
mime_type=doc.mime_type,
file_size=doc.size,
date=doc.date
)
else:
media = None
m = pyrogram.Message(
message_id=message.id,
date=message.date,
chat=parse_chat(message, users, chats),
from_user=parse_user(users.get(message.from_id, None)),
text=message.message or None if media is None else None,
caption=message.message or None if media is not None else None,
entities=entities or None if media is None else None,
caption_entities=entities or None if media is not None else None,
author_signature=message.post_author,
forward_from=forward_from,
forward_from_chat=forward_from_chat,
forward_from_message_id=forward_from_message_id,
forward_signature=forward_signature,
forward_date=forward_date,
edit_date=message.edit_date,
media_group_id=message.grouped_id,
photo=photo,
location=location,
contact=contact,
venue=venue,
audio=audio,
voice=voice,
video=video,
video_note=video_note,
sticker=sticker,
document=document,
views=message.views,
via_bot=parse_user(users.get(message.via_bot_id, None))
)
if message.reply_to_msg_id and replies:
m.reply_to_message = client.get_messages(m.chat.id, [message.reply_to_msg_id])
m.reply_to_message = m.reply_to_message[0] if m.reply_to_message else None
return m
def parse_message_service(
client,
message: types.MessageService,
users: dict,
chats: dict
) -> pyrogram.Message:
action = message.action
new_chat_members = None
left_chat_member = None
new_chat_title = None
delete_chat_photo = None
migrate_to_chat_id = None
migrate_from_chat_id = None
group_chat_created = None
channel_chat_created = None
new_chat_photo = None
if isinstance(action, types.MessageActionChatAddUser):
new_chat_members = [parse_user(users[i]) for i in action.users]
elif isinstance(action, types.MessageActionChatJoinedByLink):
new_chat_members = [parse_user(users[message.from_id])]
elif isinstance(action, types.MessageActionChatDeleteUser):
left_chat_member = parse_user(users[action.user_id])
elif isinstance(action, types.MessageActionChatEditTitle):
new_chat_title = action.title
elif isinstance(action, types.MessageActionChatDeletePhoto):
delete_chat_photo = True
elif isinstance(action, types.MessageActionChatMigrateTo):
migrate_to_chat_id = action.channel_id
elif isinstance(action, types.MessageActionChannelMigrateFrom):
migrate_from_chat_id = action.chat_id
elif isinstance(action, types.MessageActionChatCreate):
group_chat_created = True
elif isinstance(action, types.MessageActionChannelCreate):
channel_chat_created = True
elif isinstance(action, types.MessageActionChatEditPhoto):
photo = action.photo
if isinstance(photo, types.Photo):
sizes = photo.sizes
photo_sizes = []
for size in sizes:
if isinstance(size, (types.PhotoSize, types.PhotoCachedSize)):
loc = size.location
if isinstance(size, types.PhotoSize):
file_size = size.size
else:
file_size = len(size.bytes)
if isinstance(loc, types.FileLocation):
photo_size = pyrogram.PhotoSize(
file_id=encode(
pack(
"<iiqqqqi",
2,
loc.dc_id,
photo.id,
photo.access_hash,
loc.volume_id,
loc.secret,
loc.local_id
)
),
width=size.w,
height=size.h,
file_size=file_size,
date=photo.date
)
photo_sizes.append(photo_size)
new_chat_photo = photo_sizes
m = pyrogram.Message(
message_id=message.id,
date=message.date,
chat=parse_chat(message, users, chats),
from_user=parse_user(users.get(message.from_id, None)),
new_chat_members=new_chat_members,
left_chat_member=left_chat_member,
new_chat_title=new_chat_title,
new_chat_photo=new_chat_photo,
delete_chat_photo=delete_chat_photo,
migrate_to_chat_id=int("-100" + str(migrate_to_chat_id)) if migrate_to_chat_id else None,
migrate_from_chat_id=-migrate_from_chat_id if migrate_from_chat_id else None,
group_chat_created=group_chat_created,
channel_chat_created=channel_chat_created
# TODO: supergroup_chat_created
)
if isinstance(action, types.MessageActionPinMessage):
m.pinned_message = client.get_messages(m.chat.id, [message.reply_to_msg_id])
m.pinned_message = m.pinned_message[0] if m.pinned_message else None
return m

View File

@ -18,8 +18,8 @@
class ParseMode:
"""This class provides a convenient access to parse modes.
It is intended to be used with any method that accepts the optional argument **parse_mode**
"""This class provides a convenient access to Parse Modes.
Parse Modes are intended to be used with any method that accepts the optional argument **parse_mode**.
"""
HTML = "html"

View File

@ -1,3 +1,21 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <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 base64
import json
import logging

View File

@ -1,3 +1,23 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <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/>.
from base64 import b64decode, b64encode
from pyrogram.api import types
@ -26,3 +46,39 @@ def get_offset_date(dialogs):
return m.date
else:
return 0
def decode(s: str) -> bytes:
s = b64decode(s + "=" * (-len(s) % 4), "-_")
r = b""
assert s[-1] == 2
i = 0
while i < len(s) - 1:
if s[i] != 0:
r += bytes([s[i]])
else:
r += b"\x00" * s[i + 1]
i += 1
i += 1
return r
def encode(s: bytes) -> str:
r = b""
n = 0
for i in s + bytes([2]):
if i == 0:
n += 1
else:
if n:
r += b"\x00" + bytes([n])
n = 0
r += bytes([i])
return b64encode(r, b"-_").decode().rstrip("=")

View File

@ -262,7 +262,6 @@ class Auth:
else:
raise e
log.warning("Auth key creation failed. Let's try again: {}".format(repr(e)))
time.sleep(1)
continue
else:

View File

@ -32,7 +32,7 @@ from pyrogram import __copyright__, __license__, __version__
from pyrogram.api import functions, types, core
from pyrogram.api.all import layer
from pyrogram.api.core import Message, Object, MsgContainer, Long, FutureSalt, Int
from pyrogram.api.errors import Error
from pyrogram.api.errors import Error, InternalServerError
from pyrogram.connection import Connection
from pyrogram.crypto import AES, KDF
from .internals import MsgId, MsgFactory, DataCenter
@ -399,17 +399,19 @@ class Session:
else:
return result
def send(self, data: Object):
for i in range(self.MAX_RETRIES):
self.is_connected.wait(self.WAIT_TIMEOUT)
def send(self, data: Object, retries: int = MAX_RETRIES):
self.is_connected.wait(self.WAIT_TIMEOUT)
try:
return self._send(data)
except (OSError, TimeoutError):
(log.warning if i > 2 else log.info)(
"{}: {} Retrying {}".format(i, datetime.now(), type(data))
)
time.sleep(1)
continue
else:
return None
try:
return self._send(data)
except (OSError, TimeoutError, InternalServerError) as e:
if retries == 0:
raise e from None
(log.warning if retries < 3 else log.info)(
"{}: {} Retrying {}".format(
Session.MAX_RETRIES - retries,
datetime.now(), type(data)))
time.sleep(0.5)
self.send(data, retries - 1)