mirror of
https://github.com/pyrogram/pyrogram
synced 2025-08-28 12:57:52 +00:00
commit
f47ee057e4
162
README.rst
162
README.rst
@ -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
|
||||
|
@ -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()
|
||||
|
22
compiler/api/source/pyrogram.tl
Normal file
22
compiler/api/source/pyrogram.tl
Normal 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;
|
@ -6,8 +6,7 @@ from pyrogram.api.core import *
|
||||
|
||||
|
||||
class {class_name}(Object):
|
||||
"""
|
||||
{docstring_args}
|
||||
"""{docstring_args}
|
||||
"""
|
||||
ID = {object_id}
|
||||
|
11
compiler/api/template/pyrogram.txt
Normal file
11
compiler/api/template/pyrogram.txt
Normal 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}
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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,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
|
|
@ -6,7 +6,7 @@ from ..error import Error
|
||||
class {super_class}(Error):
|
||||
{docstring}
|
||||
CODE = {code}
|
||||
""":obj:`int`: Error Code"""
|
||||
"""``int``: Error Code"""
|
||||
NAME = __doc__
|
||||
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
class {sub_class}({super_class}):
|
||||
{docstring}
|
||||
ID = {id}
|
||||
""":obj:`str`: Error ID"""
|
||||
"""``str``: Error ID"""
|
||||
MESSAGE = __doc__
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
@ -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.
|
||||
|
@ -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:
|
||||
|
@ -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
|
@ -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
|
@ -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'
|
@ -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/
|
||||
|
@ -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
|
6
docs/source/pyrogram/Emoji.rst
Normal file
6
docs/source/pyrogram/Emoji.rst
Normal file
@ -0,0 +1,6 @@
|
||||
Emoji
|
||||
======
|
||||
|
||||
.. autoclass:: pyrogram.Emoji
|
||||
:members:
|
||||
:undoc-members:
|
@ -1,3 +1,5 @@
|
||||
:tocdepth: 1
|
||||
|
||||
Error
|
||||
=====
|
||||
|
||||
|
6
docs/source/pyrogram/Filters.rst
Normal file
6
docs/source/pyrogram/Filters.rst
Normal file
@ -0,0 +1,6 @@
|
||||
Filters
|
||||
=======
|
||||
|
||||
.. autoclass:: pyrogram.Filters
|
||||
:members:
|
||||
:undoc-members:
|
@ -1,6 +0,0 @@
|
||||
InputMedia
|
||||
==========
|
||||
|
||||
.. autoclass:: pyrogram.InputMedia
|
||||
:members:
|
||||
:undoc-members:
|
6
docs/source/pyrogram/InputMediaPhoto.rst
Normal file
6
docs/source/pyrogram/InputMediaPhoto.rst
Normal file
@ -0,0 +1,6 @@
|
||||
InputMediaPhoto
|
||||
===============
|
||||
|
||||
.. autoclass:: pyrogram.InputMediaPhoto
|
||||
:members:
|
||||
:undoc-members:
|
6
docs/source/pyrogram/InputMediaVideo.rst
Normal file
6
docs/source/pyrogram/InputMediaVideo.rst
Normal file
@ -0,0 +1,6 @@
|
||||
InputMediaVideo
|
||||
===============
|
||||
|
||||
.. autoclass:: pyrogram.InputMediaVideo
|
||||
:members:
|
||||
:undoc-members:
|
6
docs/source/pyrogram/InputPhoneContact.rst
Normal file
6
docs/source/pyrogram/InputPhoneContact.rst
Normal file
@ -0,0 +1,6 @@
|
||||
InputPhoneContact
|
||||
=================
|
||||
|
||||
.. autoclass:: pyrogram.InputPhoneContact
|
||||
:members:
|
||||
:undoc-members:
|
6
docs/source/pyrogram/MessageHandler.rst
Normal file
6
docs/source/pyrogram/MessageHandler.rst
Normal file
@ -0,0 +1,6 @@
|
||||
MessageHandler
|
||||
==============
|
||||
|
||||
.. autoclass:: pyrogram.MessageHandler
|
||||
:members:
|
||||
:undoc-members:
|
6
docs/source/pyrogram/RawUpdateHandler.rst
Normal file
6
docs/source/pyrogram/RawUpdateHandler.rst
Normal file
@ -0,0 +1,6 @@
|
||||
RawUpdateHandler
|
||||
================
|
||||
|
||||
.. autoclass:: pyrogram.RawUpdateHandler
|
||||
:members:
|
||||
:undoc-members:
|
@ -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
|
||||
|
64
docs/source/resources/AutoAuthorization.rst
Normal file
64
docs/source/resources/AutoAuthorization.rst
Normal 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())
|
40
docs/source/resources/BotsInteraction.rst
Normal file
40
docs/source/resources/BotsInteraction.rst
Normal 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.
|
@ -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**
|
@ -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.
|
50
docs/source/resources/SOCKS5Proxy.rst
Normal file
50
docs/source/resources/SOCKS5Proxy.rst
Normal 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.
|
@ -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
|
||||
)
|
||||
|
32
docs/source/resources/TgCrypto.rst
Normal file
32
docs/source/resources/TgCrypto.rst
Normal 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.
|
@ -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
68
docs/source/sitemap.py
Normal 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>")
|
97
docs/source/start/BasicUsage.rst
Normal file
97
docs/source/start/BasicUsage.rst
Normal 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
|
100
docs/source/start/ProjectSetup.rst
Normal file
100
docs/source/start/ProjectSetup.rst
Normal 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
|
37
docs/source/start/QuickInstallation.rst
Normal file
37
docs/source/start/QuickInstallation.rst
Normal 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
|
@ -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)
|
||||
|
@ -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()
|
@ -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
17
examples/echo_bot.py
Normal 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()
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
14
examples/raw_update_handler.py
Normal file
14
examples/raw_update_handler.py
Normal 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()
|
@ -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()
|
@ -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()
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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()]
|
||||
)
|
||||
|
@ -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
|
||||
|
@ -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
19
pyrogram/client/dispatcher/__init__.py
Normal file
19
pyrogram/client/dispatcher/__init__.py
Normal 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
|
159
pyrogram/client/dispatcher/dispatcher.py
Normal file
159
pyrogram/client/dispatcher/dispatcher.py
Normal 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))
|
19
pyrogram/client/filters/__init__.py
Normal file
19
pyrogram/client/filters/__init__.py
Normal 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
|
57
pyrogram/client/filters/filter.py
Normal file
57
pyrogram/client/filters/filter.py
Normal 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)
|
235
pyrogram/client/filters/filters.py
Normal file
235
pyrogram/client/filters/filters.py
Normal 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."""
|
19
pyrogram/client/handlers/__init__.py
Normal file
19
pyrogram/client/handlers/__init__.py
Normal 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
|
23
pyrogram/client/handlers/handler.py
Normal file
23
pyrogram/client/handlers/handler.py
Normal 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
|
95
pyrogram/client/handlers/handlers.py
Normal file
95
pyrogram/client/handlers/handlers.py
Normal 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)
|
46
pyrogram/client/input_media_photo.py
Normal file
46
pyrogram/client/input_media_photo.py
Normal 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
|
66
pyrogram/client/input_media_video.py
Normal file
66
pyrogram/client/input_media_video.py
Normal 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
|
@ -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(),
|
||||
|
581
pyrogram/client/message_parser.py
Normal file
581
pyrogram/client/message_parser.py
Normal 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
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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("=")
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user