` or "html" in the *p
italic, italic
- inline URL
+ inline URL
inline mention of a user
@@ -66,7 +66,7 @@ Examples
"**bold**, "
"__italic__, "
"[mention](tg://user?id=23122162), "
- "[URL](https://docs.pyrogram.ml), "
+ "[URL](https://docs.pyrogram.org), "
"`code`, "
"```"
"for i in range(10):\n"
@@ -84,7 +84,7 @@ Examples
"bold, "
"italic, "
"mention, "
- "URL, "
+ "URL, "
"code
, "
""
"for i in range(10):\n"
diff --git a/docs/source/resources/TgCrypto.rst b/docs/source/topics/tgcrypto.rst
similarity index 87%
rename from docs/source/resources/TgCrypto.rst
rename to docs/source/topics/tgcrypto.rst
index 2af09a06..454bf05c 100644
--- a/docs/source/resources/TgCrypto.rst
+++ b/docs/source/topics/tgcrypto.rst
@@ -2,7 +2,7 @@ Fast Crypto
===========
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.
+Library specifically written in C for Pyrogram [1]_ 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).
@@ -28,5 +28,5 @@ what you should do next:
.. _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
+.. [1] Although TgCrypto is intended for Pyrogram, it is shipped as a standalone package and can thus be used for
other Python projects too.
diff --git a/docs/source/topics/use-filters.rst b/docs/source/topics/use-filters.rst
new file mode 100644
index 00000000..d481b393
--- /dev/null
+++ b/docs/source/topics/use-filters.rst
@@ -0,0 +1,108 @@
+Using Filters
+=============
+
+So far we've seen how to register a callback function that executes every time a specific update comes from the server,
+but there's much more than that to come.
+
+Here we'll discuss about :class:`~pyrogram.Filters`. Filters enable a fine-grain control over what kind of
+updates are allowed or not to be passed in your callback functions, based on their inner details.
+
+Single Filters
+--------------
+
+Let's start right away with a simple example:
+
+- This example will show you how to **only** handle messages containing an :class:`~pyrogram.Audio` object and
+ ignore any other message. Filters are passed as the first argument of the decorator:
+
+ .. code-block:: python
+ :emphasize-lines: 4
+
+ from pyrogram import Filters
+
+
+ @app.on_message(Filters.audio)
+ def my_handler(client, message):
+ print(message)
+
+- or, without decorators. Here filters are passed as the second argument of the handler constructor; the first is the
+ callback function itself:
+
+ .. code-block:: python
+ :emphasize-lines: 8
+
+ 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 inverting and combining more filters together using bitwise
+operators ``~``, ``&`` and ``|``:
+
+- Use ``~`` to invert a filter (behaves like the ``not`` operator).
+- Use ``&`` and ``|`` to merge two filters (behave like ``and``, ``or`` operators respectively).
+
+Here are some examples:
+
+- Message is a **text** message **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 :meth:`~pyrogram.Filters.command` or :meth:`~pyrogram.Filters.regex`
+can also accept arguments:
+
+- Message is either a */start* or */help* **command**.
+
+ .. code-block:: python
+
+ @app.on_message(Filters.command(["start", "help"]))
+ def my_handler(client, message):
+ print(message)
+
+- Message is a **text** message or a media **caption** matching the given **regex** pattern.
+
+ .. code-block:: python
+
+ @app.on_message(Filters.regex("pyrogram"))
+ 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")
diff --git a/docs/source/resources/VoiceCalls.rst b/docs/source/topics/voice-calls.rst
similarity index 100%
rename from docs/source/resources/VoiceCalls.rst
rename to docs/source/topics/voice-calls.rst
diff --git a/examples/README.md b/examples/README.md
index 643fe56d..b8898a71 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -12,12 +12,12 @@ Example | Description
---: | :---
[**hello_world**](hello_world.py) | Demonstration of basic API usage
[**echobot**](echobot.py) | Echo every private text message
-[**welcome**](welcome.py) | The Welcome Bot in [@PyrogramChat](https://t.me/pyrogramchat)
-[**history**](history.py) | Get the full message history of a chat
-[**chat_members**](chat_members.py) | Get all the members of a chat
-[**dialogs**](dialogs.py) | Get all of your dialog chats
-[**using_inline_bots**](using_inline_bots.py) | Query an inline bot (as user) and send a result to a chat
-[**keyboards**](keyboards.py) | Send normal and inline keyboards using regular bots
-[**callback_queries**](callback_queries.py) | Handle queries coming from inline button presses
-[**inline_queries**](inline_queries.py) | Handle inline queries
+[**welcomebot**](welcomebot.py) | The Welcome Bot in [@PyrogramChat](https://t.me/pyrogramchat)
+[**get_history**](get_history.py) | Get the full message history of a chat
+[**get_chat_members**](get_chat_members.py) | Get all the members of a chat
+[**get_dialogs**](get_dialogs.py) | Get all of your dialog chats
+[**callback_queries**](callback_queries.py) | Handle callback queries (as bot) coming from inline button presses
+[**inline_queries**](inline_queries.py) | Handle inline queries (as bot) and answer with results
+[**use_inline_bots**](use_inline_bots.py) | Query an inline bot (as user) and send a result to a chat
+[**bot_keyboards**](bot_keyboards.py) | Send normal and inline keyboards using regular bots
[**raw_updates**](raw_updates.py) | Handle raw updates (old, should be avoided)
diff --git a/examples/keyboards.py b/examples/bot_keyboards.py
similarity index 96%
rename from examples/keyboards.py
rename to examples/bot_keyboards.py
index 1a1140b6..e1ff1e7e 100644
--- a/examples/keyboards.py
+++ b/examples/bot_keyboards.py
@@ -1,4 +1,4 @@
-"""This example will show you how to send normal and inline keyboards.
+"""This example will show you how to send normal and inline keyboards (as bot).
You must log-in as a regular bot in order to send keyboards (use the token from @BotFather).
Any attempt in sending keyboards with a user account will be simply ignored by the server.
@@ -39,7 +39,7 @@ with app:
),
InlineKeyboardButton( # Opens a web URL
"URL",
- url="https://docs.pyrogram.ml"
+ url="https://docs.pyrogram.org"
),
],
[ # Second row
diff --git a/examples/chat_members.py b/examples/get_chat_members.py
similarity index 100%
rename from examples/chat_members.py
rename to examples/get_chat_members.py
diff --git a/examples/dialogs.py b/examples/get_dialogs.py
similarity index 72%
rename from examples/dialogs.py
rename to examples/get_dialogs.py
index 08c769e2..92da8834 100644
--- a/examples/dialogs.py
+++ b/examples/get_dialogs.py
@@ -1,4 +1,4 @@
-"""This example shows how to get the full dialogs list of a user."""
+"""This example shows how to get the full dialogs list (as user)."""
from pyrogram import Client
diff --git a/examples/history.py b/examples/get_history.py
similarity index 100%
rename from examples/history.py
rename to examples/get_history.py
diff --git a/examples/inline_queries.py b/examples/inline_queries.py
index c1727fe6..d86d90d5 100644
--- a/examples/inline_queries.py
+++ b/examples/inline_queries.py
@@ -22,12 +22,12 @@ def answer(client, inline_query):
input_message_content=InputTextMessageContent(
"Here's how to install **Pyrogram**"
),
- url="https://docs.pyrogram.ml/start/Installation",
+ url="https://docs.pyrogram.org/intro/install",
description="How to install Pyrogram",
thumb_url="https://i.imgur.com/JyxrStE.png",
reply_markup=InlineKeyboardMarkup(
[
- [InlineKeyboardButton("Open website", url="https://docs.pyrogram.ml/start/Installation")]
+ [InlineKeyboardButton("Open website", url="https://docs.pyrogram.org/intro/install")]
]
)
),
@@ -37,12 +37,12 @@ def answer(client, inline_query):
input_message_content=InputTextMessageContent(
"Here's how to use **Pyrogram**"
),
- url="https://docs.pyrogram.ml/start/Usage",
+ url="https://docs.pyrogram.org/start/invoking",
description="How to use Pyrogram",
thumb_url="https://i.imgur.com/JyxrStE.png",
reply_markup=InlineKeyboardMarkup(
[
- [InlineKeyboardButton("Open website", url="https://docs.pyrogram.ml/start/Usage")]
+ [InlineKeyboardButton("Open website", url="https://docs.pyrogram.org/start/invoking")]
]
)
)
diff --git a/examples/use_inline_bots.py b/examples/use_inline_bots.py
new file mode 100644
index 00000000..5681df87
--- /dev/null
+++ b/examples/use_inline_bots.py
@@ -0,0 +1,13 @@
+"""This example shows how to query an inline bot (as user)"""
+
+from pyrogram import Client
+
+# Create a new Client
+app = Client("my_account")
+
+with app:
+ # Get bot results for "Fuzz Universe" from the inline bot @vid
+ bot_results = app.get_inline_bot_results("vid", "Fuzz Universe")
+
+ # 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)
diff --git a/examples/using_inline_bots.py b/examples/using_inline_bots.py
deleted file mode 100644
index c3b48874..00000000
--- a/examples/using_inline_bots.py
+++ /dev/null
@@ -1,17 +0,0 @@
-"""This example shows how to query an inline bot"""
-
-from pyrogram import Client
-
-# Create a new Client
-app = Client("my_account")
-
-# Start the Client
-app.start()
-
-# Get bot results for "Fuzz Universe" from the inline bot @vid
-bot_results = app.get_inline_bot_results("vid", "Fuzz Universe")
-# 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)
-
-# Stop the client
-app.stop()
diff --git a/examples/welcome.py b/examples/welcomebot.py
similarity index 91%
rename from examples/welcome.py
rename to examples/welcomebot.py
index ab252672..35f72aff 100644
--- a/examples/welcome.py
+++ b/examples/welcomebot.py
@@ -8,7 +8,7 @@ from pyrogram import Client, Emoji, Filters
TARGET = "PyrogramChat" # Target chat. Can also be a list of multiple chat ids/usernames
MENTION = "[{}](tg://user?id={})" # User mention markup
-MESSAGE = "{} Welcome to [Pyrogram](https://docs.pyrogram.ml/)'s group chat {}!" # Welcome message
+MESSAGE = "{} Welcome to [Pyrogram](https://docs.pyrogram.org/)'s group chat {}!" # Welcome message
app = Client("my_account")
diff --git a/pyrogram/__init__.py b/pyrogram/__init__.py
index 44dbe231..ac184844 100644
--- a/pyrogram/__init__.py
+++ b/pyrogram/__init__.py
@@ -24,11 +24,9 @@ if sys.version_info[:3] in [(3, 5, 0), (3, 5, 1), (3, 5, 2)]:
# Monkey patch the standard "typing" module because Python versions from 3.5.0 to 3.5.2 have a broken one.
sys.modules["typing"] = typing
-__version__ = "0.12.0"
+__version__ = "0.15.0-develop"
__license__ = "GNU Lesser General Public License v3 or later (LGPLv3+)"
-__copyright__ = "Copyright (C) 2017-2019 Dan Tès ".replace(
- "\xe8", "e" if sys.getfilesystemencoding() != "utf-8" else "\xe8"
-)
+__copyright__ = "Copyright (C) 2017-2019 Dan "
from .errors import RPCError
from .client import *
diff --git a/pyrogram/api/__init__.py b/pyrogram/api/__init__.py
index e57f0661..8d7831ff 100644
--- a/pyrogram/api/__init__.py
+++ b/pyrogram/api/__init__.py
@@ -19,8 +19,8 @@
from importlib import import_module
from .all import objects
-from .core.object import Object
+from .core.tl_object import TLObject
for k, v in objects.items():
path, name = v.rsplit(".", 1)
- Object.all[k] = getattr(import_module(path), name)
+ TLObject.all[k] = getattr(import_module(path), name)
diff --git a/pyrogram/api/core/__init__.py b/pyrogram/api/core/__init__.py
index daba6b7c..aaf5a324 100644
--- a/pyrogram/api/core/__init__.py
+++ b/pyrogram/api/core/__init__.py
@@ -19,10 +19,11 @@
from .future_salt import FutureSalt
from .future_salts import FutureSalts
from .gzip_packed import GzipPacked
+from .list import List
from .message import Message
from .msg_container import MsgContainer
-from .object import Object
from .primitives import (
Bool, BoolTrue, BoolFalse, Bytes, Double,
Int, Long, Int128, Int256, Null, String, Vector
)
+from .tl_object import TLObject
diff --git a/pyrogram/api/core/future_salt.py b/pyrogram/api/core/future_salt.py
index 4ee8197b..ab387f6c 100644
--- a/pyrogram/api/core/future_salt.py
+++ b/pyrogram/api/core/future_salt.py
@@ -16,29 +16,28 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see .
-from datetime import datetime
from io import BytesIO
-from .object import Object
from .primitives import Int, Long
+from .tl_object import TLObject
-class FutureSalt(Object):
+class FutureSalt(TLObject):
ID = 0x0949d9dc
__slots__ = ["valid_since", "valid_until", "salt"]
QUALNAME = "FutureSalt"
- def __init__(self, valid_since: int or datetime, valid_until: int or datetime, salt: int):
+ def __init__(self, valid_since: int, valid_until: int, salt: int):
self.valid_since = valid_since
self.valid_until = valid_until
self.salt = salt
@staticmethod
def read(b: BytesIO, *args) -> "FutureSalt":
- valid_since = datetime.fromtimestamp(Int.read(b))
- valid_until = datetime.fromtimestamp(Int.read(b))
+ valid_since = Int.read(b)
+ valid_until = Int.read(b)
salt = Long.read(b)
return FutureSalt(valid_since, valid_until, salt)
diff --git a/pyrogram/api/core/future_salts.py b/pyrogram/api/core/future_salts.py
index cf6a9902..a97b9d2a 100644
--- a/pyrogram/api/core/future_salts.py
+++ b/pyrogram/api/core/future_salts.py
@@ -16,22 +16,21 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see .
-from datetime import datetime
from io import BytesIO
from . import FutureSalt
-from .object import Object
from .primitives import Int, Long
+from .tl_object import TLObject
-class FutureSalts(Object):
+class FutureSalts(TLObject):
ID = 0xae500895
__slots__ = ["req_msg_id", "now", "salts"]
QUALNAME = "FutureSalts"
- def __init__(self, req_msg_id: int, now: int or datetime, salts: list):
+ def __init__(self, req_msg_id: int, now: int, salts: list):
self.req_msg_id = req_msg_id
self.now = now
self.salts = salts
@@ -39,7 +38,7 @@ class FutureSalts(Object):
@staticmethod
def read(b: BytesIO, *args) -> "FutureSalts":
req_msg_id = Long.read(b)
- now = datetime.fromtimestamp(Int.read(b))
+ now = Int.read(b)
count = Int.read(b)
salts = [FutureSalt.read(b) for _ in range(count)]
diff --git a/pyrogram/api/core/gzip_packed.py b/pyrogram/api/core/gzip_packed.py
index 135c36bf..5a8e76da 100644
--- a/pyrogram/api/core/gzip_packed.py
+++ b/pyrogram/api/core/gzip_packed.py
@@ -19,24 +19,24 @@
from gzip import compress, decompress
from io import BytesIO
-from .object import Object
from .primitives import Int, Bytes
+from .tl_object import TLObject
-class GzipPacked(Object):
+class GzipPacked(TLObject):
ID = 0x3072cfa1
__slots__ = ["packed_data"]
QUALNAME = "GzipPacked"
- def __init__(self, packed_data: Object):
+ def __init__(self, packed_data: TLObject):
self.packed_data = packed_data
@staticmethod
def read(b: BytesIO, *args) -> "GzipPacked":
# Return the Object itself instead of a GzipPacked wrapping it
- return Object.read(
+ return TLObject.read(
BytesIO(
decompress(
Bytes.read(b)
diff --git a/pyrogram/client/ext/parse_mode.py b/pyrogram/api/core/list.py
similarity index 71%
rename from pyrogram/client/ext/parse_mode.py
rename to pyrogram/api/core/list.py
index 46ed97e3..4b309a6d 100644
--- a/pyrogram/client/ext/parse_mode.py
+++ b/pyrogram/api/core/list.py
@@ -16,14 +16,13 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see .
+from .tl_object import TLObject
-class ParseMode:
- """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"
- """Set the parse mode to HTML style"""
+class List(list, TLObject):
+ __slots__ = []
- MARKDOWN = "markdown"
- """Set the parse mode to Markdown style"""
+ def __repr__(self):
+ return "pyrogram.api.core.List([{}])".format(
+ ",".join(TLObject.__repr__(i) for i in self)
+ )
diff --git a/pyrogram/api/core/message.py b/pyrogram/api/core/message.py
index 5b2e5b64..1b9b55f1 100644
--- a/pyrogram/api/core/message.py
+++ b/pyrogram/api/core/message.py
@@ -18,18 +18,18 @@
from io import BytesIO
-from .object import Object
from .primitives import Int, Long
+from .tl_object import TLObject
-class Message(Object):
+class Message(TLObject):
ID = 0x5bb8e511 # hex(crc32(b"message msg_id:long seqno:int bytes:int body:Object = Message"))
__slots__ = ["msg_id", "seq_no", "length", "body"]
QUALNAME = "Message"
- def __init__(self, body: Object, msg_id: int, seq_no: int, length: int):
+ def __init__(self, body: TLObject, msg_id: int, seq_no: int, length: int):
self.msg_id = msg_id
self.seq_no = seq_no
self.length = length
@@ -42,7 +42,7 @@ class Message(Object):
length = Int.read(b)
body = b.read(length)
- return Message(Object.read(BytesIO(body)), msg_id, seq_no, length)
+ return Message(TLObject.read(BytesIO(body)), msg_id, seq_no, length)
def write(self) -> bytes:
b = BytesIO()
diff --git a/pyrogram/api/core/msg_container.py b/pyrogram/api/core/msg_container.py
index bfc41333..58732403 100644
--- a/pyrogram/api/core/msg_container.py
+++ b/pyrogram/api/core/msg_container.py
@@ -19,11 +19,11 @@
from io import BytesIO
from .message import Message
-from .object import Object
from .primitives import Int
+from .tl_object import TLObject
-class MsgContainer(Object):
+class MsgContainer(TLObject):
ID = 0x73f1f8dc
__slots__ = ["messages"]
diff --git a/pyrogram/api/core/object.py b/pyrogram/api/core/object.py
deleted file mode 100644
index a479fb6e..00000000
--- a/pyrogram/api/core/object.py
+++ /dev/null
@@ -1,72 +0,0 @@
-# Pyrogram - Telegram MTProto API Client Library for Python
-# Copyright (C) 2017-2019 Dan Tès
-#
-# 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 .
-
-from collections import OrderedDict
-from datetime import datetime
-from io import BytesIO
-from json import dumps
-
-
-class Object:
- all = {}
-
- __slots__ = []
-
- QUALNAME = "Base"
-
- @staticmethod
- def read(b: BytesIO, *args):
- return Object.all[int.from_bytes(b.read(4), "little")].read(b, *args)
-
- def write(self, *args) -> bytes:
- pass
-
- def __str__(self) -> str:
- return dumps(self, indent=4, default=default, ensure_ascii=False)
-
- def __len__(self) -> int:
- return len(self.write())
-
- def __getitem__(self, item):
- 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
-
-
-def default(o: "Object"):
- try:
- content = {i: getattr(o, i) for i in o.__slots__}
-
- return remove_none(
- OrderedDict(
- [("_", o.QUALNAME)]
- + [i for i in content.items()]
- )
- )
- except AttributeError:
- if isinstance(o, datetime):
- return o.strftime("%d-%b-%Y %H:%M:%S")
- else:
- return repr(o)
diff --git a/pyrogram/api/core/primitives/bool.py b/pyrogram/api/core/primitives/bool.py
index 117ee7a4..0d3732e0 100644
--- a/pyrogram/api/core/primitives/bool.py
+++ b/pyrogram/api/core/primitives/bool.py
@@ -18,10 +18,10 @@
from io import BytesIO
-from ..object import Object
+from ..tl_object import TLObject
-class BoolFalse(Object):
+class BoolFalse(TLObject):
ID = 0xbc799737
value = False
@@ -38,7 +38,7 @@ class BoolTrue(BoolFalse):
value = True
-class Bool(Object):
+class Bool(TLObject):
@classmethod
def read(cls, b: BytesIO) -> bool:
return int.from_bytes(b.read(4), "little") == BoolTrue.ID
diff --git a/pyrogram/api/core/primitives/bytes.py b/pyrogram/api/core/primitives/bytes.py
index 8030b598..f511fef3 100644
--- a/pyrogram/api/core/primitives/bytes.py
+++ b/pyrogram/api/core/primitives/bytes.py
@@ -18,10 +18,10 @@
from io import BytesIO
-from ..object import Object
+from ..tl_object import TLObject
-class Bytes(Object):
+class Bytes(TLObject):
@staticmethod
def read(b: BytesIO, *args) -> bytes:
length = int.from_bytes(b.read(1), "little")
diff --git a/pyrogram/api/core/primitives/double.py b/pyrogram/api/core/primitives/double.py
index 3dcaa461..067d08bd 100644
--- a/pyrogram/api/core/primitives/double.py
+++ b/pyrogram/api/core/primitives/double.py
@@ -19,10 +19,10 @@
from io import BytesIO
from struct import unpack, pack
-from ..object import Object
+from ..tl_object import TLObject
-class Double(Object):
+class Double(TLObject):
@staticmethod
def read(b: BytesIO, *args) -> float:
return unpack("d", b.read(8))[0]
diff --git a/pyrogram/api/core/primitives/int.py b/pyrogram/api/core/primitives/int.py
index 7833a610..ea43983c 100644
--- a/pyrogram/api/core/primitives/int.py
+++ b/pyrogram/api/core/primitives/int.py
@@ -18,10 +18,10 @@
from io import BytesIO
-from ..object import Object
+from ..tl_object import TLObject
-class Int(Object):
+class Int(TLObject):
SIZE = 4
@classmethod
diff --git a/pyrogram/api/core/primitives/null.py b/pyrogram/api/core/primitives/null.py
index d2d3b1c0..ffddea94 100644
--- a/pyrogram/api/core/primitives/null.py
+++ b/pyrogram/api/core/primitives/null.py
@@ -18,10 +18,10 @@
from io import BytesIO
-from ..object import Object
+from ..tl_object import TLObject
-class Null(Object):
+class Null(TLObject):
ID = 0x56730bcc
@staticmethod
diff --git a/pyrogram/api/core/primitives/vector.py b/pyrogram/api/core/primitives/vector.py
index cd24ec35..641b33ef 100644
--- a/pyrogram/api/core/primitives/vector.py
+++ b/pyrogram/api/core/primitives/vector.py
@@ -19,31 +19,32 @@
from io import BytesIO
from . import Int
-from ..object import Object
+from ..list import List
+from ..tl_object import TLObject
-class Vector(Object):
+class Vector(TLObject):
ID = 0x1cb5c415
# Method added to handle the special case when a query returns a bare Vector (of Ints);
# i.e., RpcResult body starts with 0x1cb5c415 (Vector Id) - e.g., messages.GetMessagesViews.
@staticmethod
- def _read(b: BytesIO) -> Object or int:
+ def _read(b: BytesIO) -> TLObject or int:
try:
- return Object.read(b)
+ return TLObject.read(b)
except KeyError:
b.seek(-4, 1)
return Int.read(b)
@staticmethod
- def read(b: BytesIO, t: Object = None) -> list:
- return [
+ def read(b: BytesIO, t: TLObject = None) -> list:
+ return List(
t.read(b) if t
else Vector._read(b)
for _ in range(Int.read(b))
- ]
+ )
- def __new__(cls, value: list, t: Object = None) -> bytes:
+ def __new__(cls, value: list, t: TLObject = None) -> bytes:
return b"".join(
[Int(cls.ID, False), Int(len(value))]
+ [
diff --git a/pyrogram/api/core/tl_object.py b/pyrogram/api/core/tl_object.py
new file mode 100644
index 00000000..4b951404
--- /dev/null
+++ b/pyrogram/api/core/tl_object.py
@@ -0,0 +1,82 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-2019 Dan Tès
+#
+# 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 .
+
+from collections import OrderedDict
+from io import BytesIO
+from json import dumps
+
+
+class TLObject:
+ all = {}
+
+ __slots__ = []
+
+ QUALNAME = "Base"
+
+ @staticmethod
+ def read(b: BytesIO, *args): # TODO: Rename b -> data
+ return TLObject.all[int.from_bytes(b.read(4), "little")].read(b, *args)
+
+ def write(self, *args) -> bytes:
+ pass
+
+ @staticmethod
+ def default(obj: "TLObject"):
+ if isinstance(obj, bytes):
+ return repr(obj)
+
+ return OrderedDict(
+ [("_", obj.QUALNAME)]
+ + [
+ (attr, getattr(obj, attr))
+ for attr in obj.__slots__
+ if getattr(obj, attr) is not None
+ ]
+ )
+
+ def __str__(self) -> str:
+ return dumps(self, indent=4, default=TLObject.default, ensure_ascii=False)
+
+ def __repr__(self) -> str:
+ return "pyrogram.api.{}({})".format(
+ self.QUALNAME,
+ ", ".join(
+ "{}={}".format(attr, repr(getattr(self, attr)))
+ for attr in self.__slots__
+ if getattr(self, attr) is not None
+ )
+ )
+
+ def __eq__(self, other: "TLObject") -> bool:
+ for attr in self.__slots__:
+ try:
+ if getattr(self, attr) != getattr(other, attr):
+ return False
+ except AttributeError:
+ return False
+
+ return True
+
+ def __len__(self) -> int:
+ return len(self.write())
+
+ def __getitem__(self, item):
+ return getattr(self, item)
+
+ def __setitem__(self, key, value):
+ setattr(self, key, value)
diff --git a/pyrogram/client/__init__.py b/pyrogram/client/__init__.py
index d43511d2..f4a954c6 100644
--- a/pyrogram/client/__init__.py
+++ b/pyrogram/client/__init__.py
@@ -17,9 +17,9 @@
# along with Pyrogram. If not, see .
from .client import Client
-from .ext import BaseClient, ChatAction, Emoji, ParseMode
+from .ext import BaseClient, Emoji
from .filters import Filters
__all__ = [
- "Client", "BaseClient", "ChatAction", "Emoji", "ParseMode", "Filters",
+ "Client", "BaseClient", "Emoji", "Filters",
]
diff --git a/pyrogram/client/client.py b/pyrogram/client/client.py
index 7ea06b3e..1aa436b5 100644
--- a/pyrogram/client/client.py
+++ b/pyrogram/client/client.py
@@ -23,13 +23,11 @@ import mimetypes
import os
import re
import shutil
-import struct
import tempfile
import threading
import time
import warnings
from configparser import ConfigParser
-from datetime import datetime
from hashlib import sha256, md5
from importlib import import_module
from pathlib import Path
@@ -38,7 +36,7 @@ from threading import Thread
from typing import Union, List, Type
from pyrogram.api import functions, types
-from pyrogram.api.core import Object
+from pyrogram.api.core import TLObject
from pyrogram.client.handlers import DisconnectHandler
from pyrogram.client.handlers.handler import Handler
from pyrogram.client.methods.password.utils import compute_check
@@ -48,7 +46,7 @@ from pyrogram.errors import (
PhoneNumberUnoccupied, PhoneCodeInvalid, PhoneCodeHashEmpty,
PhoneCodeExpired, PhoneCodeEmpty, SessionPasswordNeeded,
PasswordHashInvalid, FloodWait, PeerIdInvalid, FirstnameInvalid, PhoneNumberBanned,
- VolumeLocNotFound, UserMigrate, FileIdInvalid, ChannelPrivate, PhoneNumberOccupied,
+ VolumeLocNotFound, UserMigrate, ChannelPrivate, PhoneNumberOccupied,
PasswordRecoveryNa, PasswordEmpty
)
from pyrogram.session import Auth, Session
@@ -63,27 +61,23 @@ log = logging.getLogger(__name__)
class Client(Methods, BaseClient):
- """This class represents a Client, the main mean for interacting with Telegram.
- It exposes bot-like methods for an easy access to the API as well as a simple way to
- invoke every single Telegram API method available.
+ """Pyrogram Client, the main means for interacting with Telegram.
- Args:
+ Parameters:
session_name (``str``):
Name to uniquely identify a session of either a User or a Bot, e.g.: "my_account". This name will be used
to save a file to disk that stores details needed for reconnecting without asking again for credentials.
- Note for bots: You can pass a bot token here, but this usage will be deprecated in next releases.
- Use *bot_token* instead.
api_id (``int``, *optional*):
The *api_id* part of your Telegram API Key, as integer. E.g.: 12345
This is an alternative way to pass it if you don't want to use the *config.ini* file.
api_hash (``str``, *optional*):
- The *api_hash* part of your Telegram API Key, as string. E.g.: "0123456789abcdef0123456789abcdef"
+ The *api_hash* part of your Telegram API Key, as string. E.g.: "0123456789abcdef0123456789abcdef".
This is an alternative way to pass it if you don't want to use the *config.ini* file.
app_version (``str``, *optional*):
- Application version. Defaults to "Pyrogram \U0001f525 vX.Y.Z"
+ Application version. Defaults to "Pyrogram X.Y.Z"
This is an alternative way to set it if you don't want to use the *config.ini* file.
device_model (``str``, *optional*):
@@ -109,10 +103,14 @@ class Client(Methods, BaseClient):
This is an alternative way to setup a proxy if you don't want to use the *config.ini* file.
test_mode (``bool``, *optional*):
- Enable or disable log-in to testing servers. Defaults to False.
+ Enable or disable login to the test servers. Defaults to False.
Only applicable for new sessions and will be ignored in case previously
created sessions are loaded.
+ bot_token (``str``, *optional*):
+ Pass your Bot API token to create a bot session, e.g.: "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11"
+ Only applicable for new sessions.
+
phone_number (``str`` | ``callable``, *optional*):
Pass your phone number as string (with your Country Code prefix included) to avoid entering it manually.
Or pass a callback function which accepts no arguments and must return the correct phone number as string
@@ -146,10 +144,6 @@ class Client(Methods, BaseClient):
a new Telegram account in case the phone number you passed is not registered yet.
Only applicable for new sessions.
- bot_token (``str``, *optional*):
- Pass your Bot API token to create a bot session, e.g.: "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11"
- Only applicable for new sessions.
-
last_name (``str``, *optional*):
Same purpose as *first_name*; pass a Last Name to avoid entering it manually. It can
be an empty string: "". Only applicable for new sessions.
@@ -175,7 +169,7 @@ class Client(Methods, BaseClient):
Defaults to False (updates enabled and always received).
takeout (``bool``, *optional*):
- Pass True to let the client use a takeout session instead of a normal one, implies no_updates.
+ Pass True to let the client use a takeout session instead of a normal one, implies *no_updates=True*.
Useful for exporting your Telegram data. Methods invoked inside a takeout session (such as get_history,
download_media, ...) are less prone to throw FloodWait exceptions.
Only available for users, bots will ignore this parameter.
@@ -196,12 +190,12 @@ class Client(Methods, BaseClient):
ipv6: bool = False,
proxy: dict = None,
test_mode: bool = False,
+ bot_token: str = None,
phone_number: str = None,
phone_code: Union[str, callable] = None,
password: str = None,
recovery_code: callable = None,
force_sms: bool = False,
- bot_token: str = None,
first_name: str = None,
last_name: str = None,
workers: int = BaseClient.WORKERS,
@@ -239,12 +233,12 @@ class Client(Methods, BaseClient):
# TODO: Make code consistent, use underscore for private/protected fields
self._proxy = proxy
self.session_storage.test_mode = test_mode
+ self.bot_token = bot_token
self.phone_number = phone_number
self.phone_code = phone_code
self.password = password
self.recovery_code = recovery_code
self.force_sms = force_sms
- self.bot_token = bot_token
self.first_name = first_name
self.last_name = last_name
self.workers = workers
@@ -279,12 +273,11 @@ class Client(Methods, BaseClient):
self._proxy.update(value)
def start(self):
- """Use this method to start the Client after creating it.
- Requires no parameters.
+ """Start the Client.
Raises:
- :class:`RPCError ` in case of a Telegram RPC error.
- ``ConnectionError`` in case you try to start an already started Client.
+ RPCError: In case of a Telegram RPC error.
+ ConnectionError: In case you try to start an already started Client.
"""
if self.is_started:
raise ConnectionError("Client has already been started")
@@ -297,7 +290,7 @@ class Client(Methods, BaseClient):
warnings.warn('\nWARNING: You are using a bot token as session name!\n'
'This usage will be deprecated soon. Please use a session file name to load '
'an existing session and the bot_token argument to create new sessions.\n'
- 'More info: https://docs.pyrogram.ml/start/Setup#bot-authorization\n')
+ 'More info: https://docs.pyrogram.org/intro/auth#bot-authorization\n')
self.load_config()
self.load_session()
@@ -336,7 +329,7 @@ class Client(Methods, BaseClient):
self.get_initial_dialogs()
self.get_contacts()
else:
- self.send(functions.messages.GetPinnedDialogs())
+ self.send(functions.messages.GetPinnedDialogs(folder_id=0))
self.get_initial_dialogs_chunk()
else:
self.send(functions.updates.GetState())
@@ -373,11 +366,10 @@ class Client(Methods, BaseClient):
return self
def stop(self):
- """Use this method to manually stop the Client.
- Requires no parameters.
+ """Stop the Client.
Raises:
- ``ConnectionError`` in case you try to stop an already stopped Client.
+ ConnectionError: In case you try to stop an already stopped Client.
"""
if not self.is_started:
raise ConnectionError("Client is already stopped")
@@ -416,25 +408,34 @@ class Client(Methods, BaseClient):
return self
def restart(self):
- """Use this method to restart the Client.
- Requires no parameters.
+ """Restart the Client.
Raises:
- ``ConnectionError`` in case you try to restart a stopped Client.
+ ConnectionError: In case you try to restart a stopped Client.
"""
self.stop()
self.start()
def idle(self, stop_signals: tuple = (SIGINT, SIGTERM, SIGABRT)):
- """Blocks the program execution until one of the signals are received,
- then gently stop the Client by closing the underlying connection.
+ """Block the main script execution until a signal (e.g.: from CTRL+C) is received.
+ Once the signal is received, the client will automatically stop and the main script will continue its execution.
- Args:
+ This is used after starting one or more clients and is useful for event-driven applications only, that are,
+ applications which react upon incoming Telegram updates through handlers, rather than executing a set of methods
+ sequentially.
+
+ The way Pyrogram works, will keep your handlers in a pool of workers, which are executed concurrently outside
+ the main script; calling idle() will ensure the client(s) will be kept alive by not letting the main script to
+ end, until you decide to quit.
+
+ Parameters:
stop_signals (``tuple``, *optional*):
Iterable containing signals the signal handler will listen to.
Defaults to (SIGINT, SIGTERM, SIGABRT).
"""
+ # TODO: Maybe make this method static and don't automatically stop
+
def signal_handler(*args):
self.is_idle = False
@@ -449,23 +450,26 @@ class Client(Methods, BaseClient):
self.stop()
def run(self):
- """Use this method to automatically start and idle a Client.
- Requires no parameters.
+ """Start the Client and automatically idle the main script.
+
+ This is a convenience method that literally just calls :meth:`~Client.start` and :meth:`~Client.idle`. It makes
+ running a client less verbose, but is not suitable in case you want to run more than one client in a single main
+ script, since :meth:`~Client.idle` will block.
Raises:
- :class:`RPCError ` in case of a Telegram RPC error.
+ RPCError: In case of a Telegram RPC error.
"""
self.start()
self.idle()
def add_handler(self, handler: Handler, group: int = 0):
- """Use this method to register an update handler.
+ """Register an update handler.
You can register multiple handlers, but at most one handler within a group
will be used for a single update. To handle the same update more than once, register
your handler using a different group id (lower group id == higher priority).
- Args:
+ Parameters:
handler (``Handler``):
The handler to be registered.
@@ -473,7 +477,7 @@ class Client(Methods, BaseClient):
The group identifier, defaults to 0.
Returns:
- A tuple of (handler, group)
+ ``tuple``: A tuple consisting of (handler, group).
"""
if isinstance(handler, DisconnectHandler):
self.disconnect_handler = handler.callback
@@ -483,13 +487,13 @@ class Client(Methods, BaseClient):
return handler, group
def remove_handler(self, handler: Handler, group: int = 0):
- """Removes a previously-added update handler.
+ """Remove a previously-registered update handler.
Make sure to provide the right group that the handler was added in. You can use
- the return value of the :meth:`add_handler` method, a tuple of (handler, group), and
+ the return value of the :meth:`~Client.add_handler` method, a tuple of (handler, group), and
pass it directly.
- Args:
+ Parameters:
handler (``Handler``):
The handler to be removed.
@@ -502,7 +506,7 @@ class Client(Methods, BaseClient):
self.dispatcher.remove_handler(handler, group)
def stop_transmission(self):
- """Use this method to stop downloading or uploading a file.
+ """Stop downloading or uploading a file.
Must be called inside a progress callback function.
"""
raise Client.StopTransmission
@@ -771,96 +775,52 @@ class Client(Methods, BaseClient):
print("Logged in successfully as {}".format(r.user.first_name))
- def fetch_peers(self, entities: List[Union[types.User,
- types.Chat, types.ChatForbidden,
- types.Channel, types.ChannelForbidden]]):
+ def fetch_peers(
+ self,
+ entities: List[
+ Union[
+ types.User,
+ types.Chat, types.ChatForbidden,
+ types.Channel, types.ChannelForbidden
+ ]
+ ]
+ ) -> bool:
+ is_min = False
+
for entity in entities:
if isinstance(entity, (types.User, types.Channel, types.ChannelForbidden)) and not entity.access_hash:
continue
self.session_storage.cache_peer(entity)
+ return is_min
+
def download_worker(self):
name = threading.current_thread().name
log.debug("{} started".format(name))
while True:
- media = self.download_queue.get()
+ packet = self.download_queue.get()
- if media is None:
+ if packet is None:
break
temp_file_path = ""
final_file_path = ""
try:
- media, file_name, done, progress, progress_args, path = media
-
- file_id = media.file_id
- size = media.file_size
-
- directory, file_name = os.path.split(file_name)
- directory = directory or "downloads"
-
- try:
- decoded = utils.decode(file_id)
- fmt = " 24 else " 24:
- volume_id = unpacked[4]
- secret = unpacked[5]
- local_id = unpacked[6]
-
- media_type_str = Client.MEDIA_TYPE_ID.get(media_type, None)
-
- if media_type_str is None:
- raise FileIdInvalid("Unknown media type: {}".format(unpacked[0]))
-
- file_name = file_name or getattr(media, "file_name", None)
-
- if not file_name:
- if media_type == 3:
- extension = ".ogg"
- elif media_type in (4, 10, 13):
- extension = mimetypes.guess_extension(media.mime_type) or ".mp4"
- elif media_type == 5:
- extension = mimetypes.guess_extension(media.mime_type) or ".unknown"
- elif media_type == 8:
- extension = ".webp"
- elif media_type == 9:
- extension = mimetypes.guess_extension(media.mime_type) or ".mp3"
- elif media_type in (0, 1, 2):
- extension = ".jpg"
- else:
- continue
-
- file_name = "{}_{}_{}{}".format(
- media_type_str,
- datetime.fromtimestamp(
- getattr(media, "date", None) or time.time()
- ).strftime("%Y-%m-%d_%H-%M-%S"),
- self.rnd_id(),
- extension
- )
+ data, directory, file_name, done, progress, progress_args, path = packet
temp_file_path = self.get_file(
- dc_id=dc_id,
- id=id,
- access_hash=access_hash,
- volume_id=volume_id,
- local_id=local_id,
- secret=secret,
- size=size,
+ media_type=data.media_type,
+ dc_id=data.dc_id,
+ document_id=data.document_id,
+ access_hash=data.access_hash,
+ thumb_size=data.thumb_size,
+ peer_id=data.peer_id,
+ volume_id=data.volume_id,
+ local_id=data.local_id,
+ file_size=data.file_size,
+ is_big=data.is_big,
progress=progress,
progress_args=progress_args
)
@@ -898,8 +858,10 @@ class Client(Methods, BaseClient):
try:
if isinstance(updates, (types.Update, types.UpdatesCombined)):
- self.fetch_peers(updates.users)
- self.fetch_peers(updates.chats)
+ is_min = self.fetch_peers(updates.users) or self.fetch_peers(updates.chats)
+
+ users = {u.id: u for u in updates.users}
+ chats = {c.id: c for c in updates.chats}
for update in updates.updates:
channel_id = getattr(
@@ -916,7 +878,7 @@ class Client(Methods, BaseClient):
if isinstance(update, types.UpdateChannelTooLong):
log.warning(update)
- if isinstance(update, types.UpdateNewChannelMessage):
+ if isinstance(update, types.UpdateNewChannelMessage) and is_min:
message = update.message
if not isinstance(message, types.MessageEmpty):
@@ -938,22 +900,10 @@ class Client(Methods, BaseClient):
pass
else:
if not isinstance(diff, types.updates.ChannelDifferenceEmpty):
- updates.users += diff.users
- updates.chats += diff.chats
+ users.update({u.id: u for u in diff.users})
+ chats.update({c.id: c for c in diff.chats})
- if channel_id and pts:
- if channel_id not in self.channels_pts:
- self.channels_pts[channel_id] = []
-
- if pts in self.channels_pts[channel_id]:
- continue
-
- self.channels_pts[channel_id].append(pts)
-
- if len(self.channels_pts[channel_id]) > 50:
- self.channels_pts[channel_id] = self.channels_pts[channel_id][25:]
-
- self.dispatcher.updates_queue.put((update, updates.users, updates.chats))
+ self.dispatcher.updates_queue.put((update, users, chats))
elif isinstance(updates, (types.UpdateShortMessage, types.UpdateShortChatMessage)):
diff = self.send(
functions.updates.GetDifference(
@@ -970,13 +920,13 @@ class Client(Methods, BaseClient):
pts=updates.pts,
pts_count=updates.pts_count
),
- diff.users,
- diff.chats
+ {u.id: u for u in diff.users},
+ {c.id: c for c in diff.chats}
))
else:
- self.dispatcher.updates_queue.put((diff.other_updates[0], [], []))
+ self.dispatcher.updates_queue.put((diff.other_updates[0], {}, {}))
elif isinstance(updates, types.UpdateShort):
- self.dispatcher.updates_queue.put((updates.update, [], []))
+ self.dispatcher.updates_queue.put((updates.update, {}, {}))
elif isinstance(updates, types.UpdatesTooLong):
log.warning(updates)
except Exception as e:
@@ -984,18 +934,21 @@ class Client(Methods, BaseClient):
log.debug("{} stopped".format(name))
- def send(self,
- data: Object,
- retries: int = Session.MAX_RETRIES,
- timeout: float = Session.WAIT_TIMEOUT):
- """Use this method to send Raw Function queries.
+ def send(self, data: TLObject, retries: int = Session.MAX_RETRIES, timeout: float = Session.WAIT_TIMEOUT):
+ """Send raw Telegram queries.
- This method makes possible to manually call every single Telegram API method in a low-level manner.
+ This method makes it possible to manually call every single Telegram API method in a low-level manner.
Available functions are listed in the :obj:`functions ` package and may accept compound
data types from :obj:`types ` as well as bare types such as ``int``, ``str``, etc...
- Args:
- data (``Object``):
+ .. note::
+
+ This is a utility method intended to be used **only** when working with raw
+ :obj:`functions ` (i.e: a Telegram API method you wish to use which is not
+ available yet in the Client class as an easy-to-use method).
+
+ Parameters:
+ data (``RawFunction``):
The API Schema function filled with proper arguments.
retries (``int``):
@@ -1004,8 +957,11 @@ class Client(Methods, BaseClient):
timeout (``float``):
Timeout in seconds.
+ Returns:
+ ``RawType``: The raw type response generated by the query.
+
Raises:
- :class:`RPCError ` in case of a Telegram RPC error.
+ RPCError: In case of a Telegram RPC error.
"""
if not self.is_started:
raise ConnectionError("Client has not been started")
@@ -1036,7 +992,7 @@ class Client(Methods, BaseClient):
else:
raise AttributeError(
"No API Key found. "
- "More info: https://docs.pyrogram.ml/start/ProjectSetup#configuration"
+ "More info: https://docs.pyrogram.org/intro/setup#configuration"
)
for option in ["app_version", "device_model", "system_version", "lang_code"]:
@@ -1065,29 +1021,34 @@ class Client(Methods, BaseClient):
self._proxy["password"] = parser.get("proxy", "password", fallback=None) or None
if self.plugins:
- self.plugins["enabled"] = bool(self.plugins.get("enabled", True))
- self.plugins["include"] = "\n".join(self.plugins.get("include", [])) or None
- self.plugins["exclude"] = "\n".join(self.plugins.get("exclude", [])) or None
+ self.plugins = {
+ "enabled": bool(self.plugins.get("enabled", True)),
+ "root": self.plugins.get("root", None),
+ "include": self.plugins.get("include", []),
+ "exclude": self.plugins.get("exclude", [])
+ }
else:
try:
section = parser["plugins"]
self.plugins = {
"enabled": section.getboolean("enabled", True),
- "root": section.get("root"),
- "include": section.get("include") or None,
- "exclude": section.get("exclude") or None
+ "root": section.get("root", None),
+ "include": section.get("include", []),
+ "exclude": section.get("exclude", [])
}
- except KeyError:
- self.plugins = {}
- if self.plugins:
- for option in ["include", "exclude"]:
- if self.plugins[option] is not None:
- self.plugins[option] = [
- (i.split()[0], i.split()[1:] or None)
- for i in self.plugins[option].strip().split("\n")
- ]
+ include = self.plugins["include"]
+ exclude = self.plugins["exclude"]
+
+ if include:
+ self.plugins["include"] = include.strip().split("\n")
+
+ if exclude:
+ self.plugins["exclude"] = exclude.strip().split("\n")
+
+ except KeyError:
+ self.plugins = None
def load_session(self):
try:
@@ -1098,14 +1059,26 @@ class Client(Methods, BaseClient):
self.ipv6, self._proxy).create()
def load_plugins(self):
- if self.plugins.get("enabled", False):
- root = self.plugins["root"]
- include = self.plugins["include"]
- exclude = self.plugins["exclude"]
+ if self.plugins:
+ plugins = self.plugins.copy()
+
+ for option in ["include", "exclude"]:
+ if plugins[option]:
+ plugins[option] = [
+ (i.split()[0], i.split()[1:] or None)
+ for i in self.plugins[option]
+ ]
+ else:
+ return
+
+ if plugins.get("enabled", False):
+ root = plugins["root"]
+ include = plugins["include"]
+ exclude = plugins["exclude"]
count = 0
- if include is None:
+ if not include:
for path in sorted(Path(root).rglob("*.py")):
module_path = '.'.join(path.parent.parts + (path.stem,))
module = import_module(module_path)
@@ -1118,8 +1091,8 @@ class Client(Methods, BaseClient):
if isinstance(handler, Handler) and isinstance(group, int):
self.add_handler(handler, group)
- log.info('[LOAD] {}("{}") in group {} from "{}"'.format(
- type(handler).__name__, name, group, module_path))
+ log.info('[{}] [LOAD] {}("{}") in group {} from "{}"'.format(
+ self.session_name, type(handler).__name__, name, group, module_path))
count += 1
except Exception:
@@ -1132,11 +1105,13 @@ class Client(Methods, BaseClient):
try:
module = import_module(module_path)
except ImportError:
- log.warning('[LOAD] Ignoring non-existent module "{}"'.format(module_path))
+ log.warning('[{}] [LOAD] Ignoring non-existent module "{}"'.format(
+ self.session_name, module_path))
continue
if "__path__" in dir(module):
- log.warning('[LOAD] Ignoring namespace "{}"'.format(module_path))
+ log.warning('[{}] [LOAD] Ignoring namespace "{}"'.format(
+ self.session_name, module_path))
continue
if handlers is None:
@@ -1151,16 +1126,16 @@ class Client(Methods, BaseClient):
if isinstance(handler, Handler) and isinstance(group, int):
self.add_handler(handler, group)
- log.info('[LOAD] {}("{}") in group {} from "{}"'.format(
- type(handler).__name__, name, group, module_path))
+ log.info('[{}] [LOAD] {}("{}") in group {} from "{}"'.format(
+ self.session_name, type(handler).__name__, name, group, module_path))
count += 1
except Exception:
if warn_non_existent_functions:
- log.warning('[LOAD] Ignoring non-existent function "{}" from "{}"'.format(
- name, module_path))
+ log.warning('[{}] [LOAD] Ignoring non-existent function "{}" from "{}"'.format(
+ self.session_name, name, module_path))
- if exclude is not None:
+ if exclude:
for path, handlers in exclude:
module_path = root + "." + path
warn_non_existent_functions = True
@@ -1168,11 +1143,13 @@ class Client(Methods, BaseClient):
try:
module = import_module(module_path)
except ImportError:
- log.warning('[UNLOAD] Ignoring non-existent module "{}"'.format(module_path))
+ log.warning('[{}] [UNLOAD] Ignoring non-existent module "{}"'.format(
+ self.session_name, module_path))
continue
if "__path__" in dir(module):
- log.warning('[UNLOAD] Ignoring namespace "{}"'.format(module_path))
+ log.warning('[{}] [UNLOAD] Ignoring namespace "{}"'.format(
+ self.session_name, module_path))
continue
if handlers is None:
@@ -1187,25 +1164,26 @@ class Client(Methods, BaseClient):
if isinstance(handler, Handler) and isinstance(group, int):
self.remove_handler(handler, group)
- log.info('[UNLOAD] {}("{}") from group {} in "{}"'.format(
- type(handler).__name__, name, group, module_path))
+ log.info('[{}] [UNLOAD] {}("{}") from group {} in "{}"'.format(
+ self.session_name, type(handler).__name__, name, group, module_path))
count -= 1
except Exception:
if warn_non_existent_functions:
- log.warning('[UNLOAD] Ignoring non-existent function "{}" from "{}"'.format(
- name, module_path))
+ log.warning('[{}] [UNLOAD] Ignoring non-existent function "{}" from "{}"'.format(
+ self.session_name, name, module_path))
if count > 0:
- log.warning('Successfully loaded {} plugin{} from "{}"'.format(count, "s" if count > 1 else "", root))
+ log.warning('[{}] Successfully loaded {} plugin{} from "{}"'.format(
+ self.session_name, count, "s" if count > 1 else "", root))
else:
- log.warning('No plugin loaded from "{}"'.format(root))
+ log.warning('[{}] No plugin loaded from "{}"'.format(
+ self.session_name, root))
def save_session(self):
self.session_storage.save()
- def get_initial_dialogs_chunk(self,
- offset_date: int = 0):
+ def get_initial_dialogs_chunk(self, offset_date: int = 0):
while True:
try:
r = self.send(
@@ -1226,7 +1204,7 @@ class Client(Methods, BaseClient):
return r
def get_initial_dialogs(self):
- self.send(functions.messages.GetPinnedDialogs())
+ self.send(functions.messages.GetPinnedDialogs(folder_id=0))
dialogs = self.get_initial_dialogs_chunk()
offset_date = utils.get_offset_date(dialogs)
@@ -1237,25 +1215,27 @@ class Client(Methods, BaseClient):
self.get_initial_dialogs_chunk()
- def resolve_peer(self,
- peer_id: Union[int, str]):
- """Use this method to get the InputPeer of a known peer_id.
+ def resolve_peer(self, peer_id: Union[int, str]):
+ """Get the InputPeer of a known peer id.
+ Useful whenever an InputPeer type is required.
- This is a utility method intended to be used **only** when working with Raw Functions (i.e: a Telegram API
- method you wish to use which is not available yet in the Client class as an easy-to-use method), whenever an
- InputPeer type is required.
+ .. note::
- Args:
+ This is a utility method intended to be used **only** when working with raw
+ :obj:`functions ` (i.e: a Telegram API method you wish to use which is not
+ available yet in the Client class as an easy-to-use method).
+
+ Parameters:
peer_id (``int`` | ``str``):
The peer id you want to extract the InputPeer from.
Can be a direct id (int), a username (str) or a phone number (str).
Returns:
- On success, the resolved peer id is returned in form of an InputPeer object.
+ ``InputPeer``: On success, the resolved peer id is returned in form of an InputPeer object.
Raises:
- :class:`RPCError ` in case of a Telegram RPC error.
- ``KeyError`` in case the peer doesn't exist in the internal database.
+ RPCError: In case of a Telegram RPC error.
+ KeyError: In case the peer doesn't exist in the internal database.
"""
try:
return self.session_storage.get_peer_by_id(peer_id)
@@ -1312,19 +1292,24 @@ class Client(Methods, BaseClient):
except KeyError:
raise PeerIdInvalid
- def save_file(self,
- path: str,
- file_id: int = None,
- file_part: int = 0,
- progress: callable = None,
- progress_args: tuple = ()):
- """Use this method to upload a file onto Telegram servers, without actually sending the message to anyone.
+ def save_file(
+ self,
+ path: str,
+ file_id: int = None,
+ file_part: int = 0,
+ progress: callable = None,
+ progress_args: tuple = ()
+ ):
+ """Upload a file onto Telegram servers, without actually sending the message to anyone.
+ Useful whenever an InputFile type is required.
- This is a utility method intended to be used **only** when working with Raw Functions (i.e: a Telegram API
- method you wish to use which is not available yet in the Client class as an easy-to-use method), whenever an
- InputFile type is required.
+ .. note::
- Args:
+ This is a utility method intended to be used **only** when working with raw
+ :obj:`functions ` (i.e: a Telegram API method you wish to use which is not
+ available yet in the Client class as an easy-to-use method).
+
+ Parameters:
path (``str``):
The path of the file you want to upload that exists on your local machine.
@@ -1344,7 +1329,7 @@ class Client(Methods, BaseClient):
a chat_id and a message_id in order to edit a message with the updated progress.
Other Parameters:
- client (:obj:`Client `):
+ client (:obj:`Client`):
The Client itself, useful when you want to call other API methods inside the callback function.
current (``int``):
@@ -1358,10 +1343,10 @@ class Client(Methods, BaseClient):
You can either keep *\*args* or add every single extra argument in your function signature.
Returns:
- On success, the uploaded file is returned in form of an InputFile object.
+ ``InputFile``: On success, the uploaded file is returned in form of an InputFile object.
Raises:
- :class:`RPCError ` in case of a Telegram RPC error.
+ RPCError: In case of a Telegram RPC error.
"""
part_size = 512 * 1024
file_size = os.path.getsize(path)
@@ -1445,16 +1430,21 @@ class Client(Methods, BaseClient):
finally:
session.stop()
- def get_file(self,
- dc_id: int,
- id: int = None,
- access_hash: int = None,
- volume_id: int = None,
- local_id: int = None,
- secret: int = None,
- size: int = None,
- progress: callable = None,
- progress_args: tuple = ()) -> str:
+ def get_file(
+ self,
+ media_type: int,
+ dc_id: int,
+ document_id: int,
+ access_hash: int,
+ thumb_size: str,
+ peer_id: int,
+ volume_id: int,
+ local_id: int,
+ file_size: int,
+ is_big: bool,
+ progress: callable,
+ progress_args: tuple = ()
+ ) -> str:
with self.media_sessions_lock:
session = self.media_sessions.get(dc_id, None)
@@ -1495,18 +1485,33 @@ class Client(Methods, BaseClient):
self.media_sessions[dc_id] = session
- if volume_id: # Photos are accessed by volume_id, local_id, secret
- location = types.InputFileLocation(
+ if media_type == 1:
+ location = types.InputPeerPhotoFileLocation(
+ peer=self.resolve_peer(peer_id),
volume_id=volume_id,
local_id=local_id,
- secret=secret,
- file_reference=b""
+ big=is_big or None
)
- else: # Any other file can be more easily accessed by id and access_hash
- location = types.InputDocumentFileLocation(
- id=id,
+ elif media_type in (0, 2):
+ location = types.InputPhotoFileLocation(
+ id=document_id,
access_hash=access_hash,
- file_reference=b""
+ file_reference=b"",
+ thumb_size=thumb_size
+ )
+ elif media_type == 14:
+ location = types.InputDocumentFileLocation(
+ id=document_id,
+ access_hash=access_hash,
+ file_reference=b"",
+ thumb_size=thumb_size
+ )
+ else:
+ location = types.InputDocumentFileLocation(
+ id=document_id,
+ access_hash=access_hash,
+ file_reference=b"",
+ thumb_size=""
)
limit = 1024 * 1024
@@ -1537,7 +1542,14 @@ class Client(Methods, BaseClient):
offset += limit
if progress:
- progress(self, min(offset, size) if size != 0 else offset, size, *progress_args)
+ progress(
+ self,
+ min(offset, file_size)
+ if file_size != 0
+ else offset,
+ file_size,
+ *progress_args
+ )
r = session.send(
functions.upload.GetFile(
@@ -1619,7 +1631,14 @@ class Client(Methods, BaseClient):
offset += limit
if progress:
- progress(self, min(offset, size) if size != 0 else offset, size, *progress_args)
+ progress(
+ self,
+ min(offset, file_size)
+ if file_size != 0
+ else offset,
+ file_size,
+ *progress_args
+ )
if len(chunk) < limit:
break
@@ -1637,3 +1656,13 @@ class Client(Methods, BaseClient):
return ""
else:
return file_name
+
+ def guess_mime_type(self, filename: str):
+ extension = os.path.splitext(filename)[1]
+ return self.extensions_to_mime_types.get(extension)
+
+ def guess_extension(self, mime_type: str):
+ extensions = self.mime_types_to_extensions.get(mime_type)
+
+ if extensions:
+ return extensions.split(" ")[0]
diff --git a/pyrogram/client/ext/__init__.py b/pyrogram/client/ext/__init__.py
index 18c28ac3..dde1952e 100644
--- a/pyrogram/client/ext/__init__.py
+++ b/pyrogram/client/ext/__init__.py
@@ -17,8 +17,7 @@
# along with Pyrogram. If not, see .
from .base_client import BaseClient
-from .chat_action import ChatAction
from .dispatcher import Dispatcher
from .emoji import Emoji
-from .parse_mode import ParseMode
+from .file_data import FileData
from .syncer import Syncer
diff --git a/pyrogram/client/ext/base_client.py b/pyrogram/client/ext/base_client.py
index a8922846..aaf87823 100644
--- a/pyrogram/client/ext/base_client.py
+++ b/pyrogram/client/ext/base_client.py
@@ -16,8 +16,11 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see .
+import os
import platform
import re
+import sys
+from pathlib import Path
from queue import Queue
from threading import Lock
@@ -31,7 +34,7 @@ class BaseClient:
class StopTransmission(StopIteration):
pass
- APP_VERSION = "Pyrogram \U0001f525 {}".format(__version__)
+ APP_VERSION = "Pyrogram {}".format(__version__)
DEVICE_MODEL = "{} {}".format(
platform.python_implementation(),
@@ -45,18 +48,20 @@ class BaseClient:
LANG_CODE = "en"
+ PARENT_DIR = Path(sys.argv[0]).parent
+
INVITE_LINK_RE = re.compile(r"^(?:https?://)?(?:www\.)?(?:t(?:elegram)?\.(?:org|me|dog)/joinchat/)([\w-]+)$")
BOT_TOKEN_RE = re.compile(r"^\d+:[\w-]+$")
DIALOGS_AT_ONCE = 100
UPDATES_WORKERS = 1
DOWNLOAD_WORKERS = 1
- OFFLINE_SLEEP = 300
+ OFFLINE_SLEEP = 900
WORKERS = 4
- WORKDIR = "."
- CONFIG_FILE = "./config.ini"
+ WORKDIR = PARENT_DIR
+ CONFIG_FILE = PARENT_DIR / "config.ini"
MEDIA_TYPE_ID = {
- 0: "thumbnail",
+ 0: "photo_thumbnail",
1: "chat_photo",
2: "photo",
3: "voice",
@@ -65,14 +70,28 @@ class BaseClient:
8: "sticker",
9: "audio",
10: "animation",
- 13: "video_note"
+ 13: "video_note",
+ 14: "document_thumbnail"
}
+ mime_types_to_extensions = {}
+ extensions_to_mime_types = {}
+
+ with open("{}/mime.types".format(os.path.dirname(__file__)), "r", encoding="UTF-8") as f:
+ for match in re.finditer(r"^([^#\s]+)\s+(.+)$", f.read(), flags=re.M):
+ mime_type, extensions = match.groups()
+
+ extensions = [".{}".format(ext) for ext in extensions.split(" ")]
+
+ for ext in extensions:
+ extensions_to_mime_types[ext] = mime_type
+
+ mime_types_to_extensions[mime_type] = " ".join(extensions)
+
def __init__(self, session_storage: SessionStorage):
self.session_storage = session_storage
self.rnd_id = MsgId
- self.channels_pts = {}
self.markdown = Markdown(self.session_storage, self)
self.html = HTML(self.session_storage, self)
@@ -125,3 +144,30 @@ class BaseClient:
def answer_inline_query(self, *args, **kwargs):
pass
+
+ def guess_mime_type(self, *args, **kwargs):
+ pass
+
+ def guess_extension(self, *args, **kwargs):
+ pass
+
+ def get_profile_photos(self, *args, **kwargs):
+ pass
+
+ def edit_message_text(self, *args, **kwargs):
+ pass
+
+ def edit_inline_text(self, *args, **kwargs):
+ pass
+
+ def edit_message_media(self, *args, **kwargs):
+ pass
+
+ def edit_inline_media(self, *args, **kwargs):
+ pass
+
+ def edit_message_reply_markup(self, *args, **kwargs):
+ pass
+
+ def edit_inline_reply_markup(self, *args, **kwargs):
+ pass
diff --git a/pyrogram/client/ext/chat_action.py b/pyrogram/client/ext/chat_action.py
deleted file mode 100644
index c0ee0585..00000000
--- a/pyrogram/client/ext/chat_action.py
+++ /dev/null
@@ -1,77 +0,0 @@
-# Pyrogram - Telegram MTProto API Client Library for Python
-# Copyright (C) 2017-2019 Dan Tès
-#
-# 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 .
-
-from enum import Enum
-
-from pyrogram.api import types
-
-
-class ChatAction(Enum):
- """This enumeration provides a convenient access to all Chat Actions available.
- Chat Actions are intended to be used with
- :meth:`send_chat_action() `.
- """
-
- CANCEL = types.SendMessageCancelAction
- """Cancels any chat action currently displayed."""
-
- TYPING = types.SendMessageTypingAction
- """User is typing a text message."""
-
- PLAYING = types.SendMessageGamePlayAction
- """User is playing a game."""
-
- CHOOSE_CONTACT = types.SendMessageChooseContactAction
- """User is choosing a contact to share."""
-
- UPLOAD_PHOTO = types.SendMessageUploadPhotoAction
- """User is uploading a photo."""
-
- RECORD_VIDEO = types.SendMessageRecordVideoAction
- """User is recording a video."""
-
- UPLOAD_VIDEO = types.SendMessageUploadVideoAction
- """User is uploading a video."""
-
- RECORD_AUDIO = types.SendMessageRecordAudioAction
- """User is recording an audio message."""
-
- UPLOAD_AUDIO = types.SendMessageUploadAudioAction
- """User is uploading an audio message."""
-
- UPLOAD_DOCUMENT = types.SendMessageUploadDocumentAction
- """User is uploading a generic document."""
-
- FIND_LOCATION = types.SendMessageGeoLocationAction
- """User is searching for a location on the map."""
-
- RECORD_VIDEO_NOTE = types.SendMessageRecordRoundAction
- """User is recording a round video note."""
-
- UPLOAD_VIDEO_NOTE = types.SendMessageUploadRoundAction
- """User is uploading a round video note."""
-
- @staticmethod
- def from_string(action: str) -> "ChatAction":
- for a in ChatAction:
- if a.name.lower() == action.lower():
- return a
-
- raise ValueError("Invalid ChatAction: '{}'. Possible types are {}".format(
- action, [x.name.lower() for x in ChatAction]
- ))
diff --git a/pyrogram/client/ext/dispatcher.py b/pyrogram/client/ext/dispatcher.py
index 7552b034..56cdead6 100644
--- a/pyrogram/client/ext/dispatcher.py
+++ b/pyrogram/client/ext/dispatcher.py
@@ -24,9 +24,10 @@ from threading import Thread
import pyrogram
from pyrogram.api import types
+from . import utils
from ..handlers import (
CallbackQueryHandler, MessageHandler, DeletedMessagesHandler,
- UserStatusHandler, RawUpdateHandler, InlineQueryHandler
+ UserStatusHandler, RawUpdateHandler, InlineQueryHandler, PollHandler
)
log = logging.getLogger(__name__)
@@ -68,7 +69,7 @@ class Dispatcher:
lambda upd, usr, cht: (pyrogram.Message._parse(self.client, upd.message, usr, cht), MessageHandler),
Dispatcher.DELETE_MESSAGES_UPDATES:
- lambda upd, usr, cht: (pyrogram.Messages._parse_deleted(self.client, upd), DeletedMessagesHandler),
+ lambda upd, usr, cht: (utils.parse_deleted_messages(self.client, upd), DeletedMessagesHandler),
Dispatcher.CALLBACK_QUERY_UPDATES:
lambda upd, usr, cht: (pyrogram.CallbackQuery._parse(self.client, upd, usr), CallbackQueryHandler),
@@ -79,7 +80,10 @@ class Dispatcher:
),
(types.UpdateBotInlineQuery,):
- lambda upd, usr, cht: (pyrogram.InlineQuery._parse(self.client, upd, usr), InlineQueryHandler)
+ lambda upd, usr, cht: (pyrogram.InlineQuery._parse(self.client, upd, usr), InlineQueryHandler),
+
+ (types.UpdateMessagePoll,):
+ lambda upd, usr, cht: (pyrogram.Poll._parse_update(self.client, upd), PollHandler)
}
self.update_parsers = {key: value for key_tuple, value in self.update_parsers.items() for key in key_tuple}
@@ -103,6 +107,7 @@ class Dispatcher:
worker.join()
self.workers_list.clear()
+ self.groups.clear()
def add_handler(self, handler, group: int):
if group not in self.groups:
@@ -122,16 +127,13 @@ class Dispatcher:
log.debug("{} started".format(name))
while True:
- update = self.updates_queue.get()
+ packet = self.updates_queue.get()
- if update is None:
+ if packet 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]
-
+ update, users, chats = packet
parser = self.update_parsers.get(type(update), None)
parsed_update, handler_type = (
diff --git a/pyrogram/client/ext/file_data.py b/pyrogram/client/ext/file_data.py
new file mode 100644
index 00000000..9a19cd5d
--- /dev/null
+++ b/pyrogram/client/ext/file_data.py
@@ -0,0 +1,38 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-2019 Dan Tès
+#
+# 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 .
+
+
+class FileData:
+ def __init__(
+ self, *, media_type: int = None, dc_id: int = None, document_id: int = None, access_hash: int = None,
+ thumb_size: str = None, peer_id: int = None, volume_id: int = None, local_id: int = None, is_big: bool = None,
+ file_size: int = None, mime_type: str = None, file_name: str = None, date: int = None
+ ):
+ self.media_type = media_type
+ self.dc_id = dc_id
+ self.document_id = document_id
+ self.access_hash = access_hash
+ self.thumb_size = thumb_size
+ self.peer_id = peer_id
+ self.volume_id = volume_id
+ self.local_id = local_id
+ self.is_big = is_big
+ self.file_size = file_size
+ self.mime_type = mime_type
+ self.file_name = file_name
+ self.date = date
diff --git a/pyrogram/client/ext/mime.types b/pyrogram/client/ext/mime.types
new file mode 100644
index 00000000..50ec065d
--- /dev/null
+++ b/pyrogram/client/ext/mime.types
@@ -0,0 +1,1858 @@
+# This file maps Internet media types to unique file extension(s).
+# Although created for httpd, this file is used by many software systems
+# and has been placed in the public domain for unlimited redisribution.
+#
+# The table below contains both registered and (common) unregistered types.
+# A type that has no unique extension can be ignored -- they are listed
+# here to guide configurations toward known types and to make it easier to
+# identify "new" types. File extensions are also commonly used to indicate
+# content languages and encodings, so choose them carefully.
+#
+# Internet media types should be registered as described in RFC 4288.
+# The registry is at .
+#
+# MIME type (lowercased) Extensions
+# ============================================ ==========
+# application/1d-interleaved-parityfec
+# application/3gpdash-qoe-report+xml
+# application/3gpp-ims+xml
+# application/a2l
+# application/activemessage
+# application/alto-costmap+json
+# application/alto-costmapfilter+json
+# application/alto-directory+json
+# application/alto-endpointcost+json
+# application/alto-endpointcostparams+json
+# application/alto-endpointprop+json
+# application/alto-endpointpropparams+json
+# application/alto-error+json
+# application/alto-networkmap+json
+# application/alto-networkmapfilter+json
+# application/aml
+application/andrew-inset ez
+# application/applefile
+application/applixware aw
+# application/atf
+# application/atfx
+application/atom+xml atom
+application/atomcat+xml atomcat
+# application/atomdeleted+xml
+# application/atomicmail
+application/atomsvc+xml atomsvc
+# application/atxml
+# application/auth-policy+xml
+# application/bacnet-xdd+zip
+# application/batch-smtp
+# application/beep+xml
+# application/calendar+json
+# application/calendar+xml
+# application/call-completion
+# application/cals-1840
+# application/cbor
+# application/ccmp+xml
+application/ccxml+xml ccxml
+# application/cdfx+xml
+application/cdmi-capability cdmia
+application/cdmi-container cdmic
+application/cdmi-domain cdmid
+application/cdmi-object cdmio
+application/cdmi-queue cdmiq
+# application/cdni
+# application/cea
+# application/cea-2018+xml
+# application/cellml+xml
+# application/cfw
+# application/cms
+# application/cnrp+xml
+# application/coap-group+json
+# application/commonground
+# application/conference-info+xml
+# application/cpl+xml
+# application/csrattrs
+# application/csta+xml
+# application/cstadata+xml
+# application/csvm+json
+application/cu-seeme cu
+# application/cybercash
+# application/dash+xml
+# application/dashdelta
+application/davmount+xml davmount
+# application/dca-rft
+# application/dcd
+# application/dec-dx
+# application/dialog-info+xml
+# application/dicom
+# application/dii
+# application/dit
+# application/dns
+application/docbook+xml dbk
+# application/dskpp+xml
+application/dssc+der dssc
+application/dssc+xml xdssc
+# application/dvcs
+application/ecmascript ecma
+# application/edi-consent
+# application/edi-x12
+# application/edifact
+# application/efi
+# application/emergencycalldata.comment+xml
+# application/emergencycalldata.deviceinfo+xml
+# application/emergencycalldata.providerinfo+xml
+# application/emergencycalldata.serviceinfo+xml
+# application/emergencycalldata.subscriberinfo+xml
+application/emma+xml emma
+# application/emotionml+xml
+# application/encaprtp
+# application/epp+xml
+application/epub+zip epub
+# application/eshop
+# application/example
+application/exi exi
+# application/fastinfoset
+# application/fastsoap
+# application/fdt+xml
+# application/fits
+application/font-tdpfr pfr
+# application/framework-attributes+xml
+# application/geo+json
+application/gml+xml gml
+application/gpx+xml gpx
+application/gxf gxf
+# application/gzip
+# application/h224
+# application/held+xml
+# application/http
+application/hyperstudio stk
+# application/ibe-key-request+xml
+# application/ibe-pkg-reply+xml
+# application/ibe-pp-data
+# application/iges
+# application/im-iscomposing+xml
+# application/index
+# application/index.cmd
+# application/index.obj
+# application/index.response
+# application/index.vnd
+application/inkml+xml ink inkml
+# application/iotp
+application/ipfix ipfix
+# application/ipp
+# application/isup
+# application/its+xml
+application/java-archive jar
+application/java-serialized-object ser
+application/java-vm class
+application/javascript js
+# application/jose
+# application/jose+json
+# application/jrd+json
+application/json json
+# application/json-patch+json
+# application/json-seq
+application/jsonml+json jsonml
+# application/jwk+json
+# application/jwk-set+json
+# application/jwt
+# application/kpml-request+xml
+# application/kpml-response+xml
+# application/ld+json
+# application/lgr+xml
+# application/link-format
+# application/load-control+xml
+application/lost+xml lostxml
+# application/lostsync+xml
+# application/lxf
+application/mac-binhex40 hqx
+application/mac-compactpro cpt
+# application/macwriteii
+application/mads+xml mads
+application/marc mrc
+application/marcxml+xml mrcx
+application/mathematica ma nb mb
+application/mathml+xml mathml
+# application/mathml-content+xml
+# application/mathml-presentation+xml
+# application/mbms-associated-procedure-description+xml
+# application/mbms-deregister+xml
+# application/mbms-envelope+xml
+# application/mbms-msk+xml
+# application/mbms-msk-response+xml
+# application/mbms-protection-description+xml
+# application/mbms-reception-report+xml
+# application/mbms-register+xml
+# application/mbms-register-response+xml
+# application/mbms-schedule+xml
+# application/mbms-user-service-description+xml
+application/mbox mbox
+# application/media-policy-dataset+xml
+# application/media_control+xml
+application/mediaservercontrol+xml mscml
+# application/merge-patch+json
+application/metalink+xml metalink
+application/metalink4+xml meta4
+application/mets+xml mets
+# application/mf4
+# application/mikey
+application/mods+xml mods
+# application/moss-keys
+# application/moss-signature
+# application/mosskey-data
+# application/mosskey-request
+application/mp21 m21 mp21
+application/mp4 mp4s
+# application/mpeg4-generic
+# application/mpeg4-iod
+# application/mpeg4-iod-xmt
+# application/mrb-consumer+xml
+# application/mrb-publish+xml
+# application/msc-ivr+xml
+# application/msc-mixer+xml
+application/msword doc dot
+application/mxf mxf
+# application/nasdata
+# application/news-checkgroups
+# application/news-groupinfo
+# application/news-transmission
+# application/nlsml+xml
+# application/nss
+# application/ocsp-request
+# application/ocsp-response
+application/octet-stream bin dms lrf mar so dist distz pkg bpk dump elc deploy
+application/oda oda
+# application/odx
+application/oebps-package+xml opf
+application/ogg ogx
+application/omdoc+xml omdoc
+application/onenote onetoc onetoc2 onetmp onepkg
+application/oxps oxps
+# application/p2p-overlay+xml
+# application/parityfec
+application/patch-ops-error+xml xer
+application/pdf pdf
+# application/pdx
+application/pgp-encrypted pgp
+# application/pgp-keys
+application/pgp-signature asc sig
+application/pics-rules prf
+# application/pidf+xml
+# application/pidf-diff+xml
+application/pkcs10 p10
+# application/pkcs12
+application/pkcs7-mime p7m p7c
+application/pkcs7-signature p7s
+application/pkcs8 p8
+application/pkix-attr-cert ac
+application/pkix-cert cer
+application/pkix-crl crl
+application/pkix-pkipath pkipath
+application/pkixcmp pki
+application/pls+xml pls
+# application/poc-settings+xml
+application/postscript ai eps ps
+# application/ppsp-tracker+json
+# application/problem+json
+# application/problem+xml
+# application/provenance+xml
+# application/prs.alvestrand.titrax-sheet
+application/prs.cww cww
+# application/prs.hpub+zip
+# application/prs.nprend
+# application/prs.plucker
+# application/prs.rdf-xml-crypt
+# application/prs.xsf+xml
+application/pskc+xml pskcxml
+# application/qsig
+# application/raptorfec
+# application/rdap+json
+application/rdf+xml rdf
+application/reginfo+xml rif
+application/relax-ng-compact-syntax rnc
+# application/remote-printing
+# application/reputon+json
+application/resource-lists+xml rl
+application/resource-lists-diff+xml rld
+# application/rfc+xml
+# application/riscos
+# application/rlmi+xml
+application/rls-services+xml rs
+application/rpki-ghostbusters gbr
+application/rpki-manifest mft
+application/rpki-roa roa
+# application/rpki-updown
+application/rsd+xml rsd
+application/rss+xml rss
+application/rtf rtf
+# application/rtploopback
+# application/rtx
+# application/samlassertion+xml
+# application/samlmetadata+xml
+application/sbml+xml sbml
+# application/scaip+xml
+# application/scim+json
+application/scvp-cv-request scq
+application/scvp-cv-response scs
+application/scvp-vp-request spq
+application/scvp-vp-response spp
+application/sdp sdp
+# application/sep+xml
+# application/sep-exi
+# application/session-info
+# application/set-payment
+application/set-payment-initiation setpay
+# application/set-registration
+application/set-registration-initiation setreg
+# application/sgml
+# application/sgml-open-catalog
+application/shf+xml shf
+# application/sieve
+# application/simple-filter+xml
+# application/simple-message-summary
+# application/simplesymbolcontainer
+# application/slate
+# application/smil
+application/smil+xml smi smil
+# application/smpte336m
+# application/soap+fastinfoset
+# application/soap+xml
+application/sparql-query rq
+application/sparql-results+xml srx
+# application/spirits-event+xml
+# application/sql
+application/srgs gram
+application/srgs+xml grxml
+application/sru+xml sru
+application/ssdl+xml ssdl
+application/ssml+xml ssml
+# application/tamp-apex-update
+# application/tamp-apex-update-confirm
+# application/tamp-community-update
+# application/tamp-community-update-confirm
+# application/tamp-error
+# application/tamp-sequence-adjust
+# application/tamp-sequence-adjust-confirm
+# application/tamp-status-query
+# application/tamp-status-response
+# application/tamp-update
+# application/tamp-update-confirm
+application/tei+xml tei teicorpus
+application/thraud+xml tfi
+# application/timestamp-query
+# application/timestamp-reply
+application/timestamped-data tsd
+# application/ttml+xml
+# application/tve-trigger
+# application/ulpfec
+# application/urc-grpsheet+xml
+# application/urc-ressheet+xml
+# application/urc-targetdesc+xml
+# application/urc-uisocketdesc+xml
+# application/vcard+json
+# application/vcard+xml
+# application/vemmi
+# application/vividence.scriptfile
+# application/vnd.3gpp-prose+xml
+# application/vnd.3gpp-prose-pc3ch+xml
+# application/vnd.3gpp.access-transfer-events+xml
+# application/vnd.3gpp.bsf+xml
+# application/vnd.3gpp.mid-call+xml
+application/vnd.3gpp.pic-bw-large plb
+application/vnd.3gpp.pic-bw-small psb
+application/vnd.3gpp.pic-bw-var pvb
+# application/vnd.3gpp.sms
+# application/vnd.3gpp.sms+xml
+# application/vnd.3gpp.srvcc-ext+xml
+# application/vnd.3gpp.srvcc-info+xml
+# application/vnd.3gpp.state-and-event-info+xml
+# application/vnd.3gpp.ussd+xml
+# application/vnd.3gpp2.bcmcsinfo+xml
+# application/vnd.3gpp2.sms
+application/vnd.3gpp2.tcap tcap
+# application/vnd.3lightssoftware.imagescal
+application/vnd.3m.post-it-notes pwn
+application/vnd.accpac.simply.aso aso
+application/vnd.accpac.simply.imp imp
+application/vnd.acucobol acu
+application/vnd.acucorp atc acutc
+application/vnd.adobe.air-application-installer-package+zip air
+# application/vnd.adobe.flash.movie
+application/vnd.adobe.formscentral.fcdt fcdt
+application/vnd.adobe.fxp fxp fxpl
+# application/vnd.adobe.partial-upload
+application/vnd.adobe.xdp+xml xdp
+application/vnd.adobe.xfdf xfdf
+# application/vnd.aether.imp
+# application/vnd.ah-barcode
+application/vnd.ahead.space ahead
+application/vnd.airzip.filesecure.azf azf
+application/vnd.airzip.filesecure.azs azs
+application/vnd.amazon.ebook azw
+# application/vnd.amazon.mobi8-ebook
+application/vnd.americandynamics.acc acc
+application/vnd.amiga.ami ami
+# application/vnd.amundsen.maze+xml
+application/vnd.android.package-archive apk
+# application/vnd.anki
+application/vnd.anser-web-certificate-issue-initiation cii
+application/vnd.anser-web-funds-transfer-initiation fti
+application/vnd.antix.game-component atx
+# application/vnd.apache.thrift.binary
+# application/vnd.apache.thrift.compact
+# application/vnd.apache.thrift.json
+# application/vnd.api+json
+application/vnd.apple.installer+xml mpkg
+application/vnd.apple.mpegurl m3u8
+# application/vnd.arastra.swi
+application/vnd.aristanetworks.swi swi
+# application/vnd.artsquare
+application/vnd.astraea-software.iota iota
+application/vnd.audiograph aep
+# application/vnd.autopackage
+# application/vnd.avistar+xml
+# application/vnd.balsamiq.bmml+xml
+# application/vnd.balsamiq.bmpr
+# application/vnd.bekitzur-stech+json
+# application/vnd.biopax.rdf+xml
+application/vnd.blueice.multipass mpm
+# application/vnd.bluetooth.ep.oob
+# application/vnd.bluetooth.le.oob
+application/vnd.bmi bmi
+application/vnd.businessobjects rep
+# application/vnd.cab-jscript
+# application/vnd.canon-cpdl
+# application/vnd.canon-lips
+# application/vnd.cendio.thinlinc.clientconf
+# application/vnd.century-systems.tcp_stream
+application/vnd.chemdraw+xml cdxml
+# application/vnd.chess-pgn
+application/vnd.chipnuts.karaoke-mmd mmd
+application/vnd.cinderella cdy
+# application/vnd.cirpack.isdn-ext
+# application/vnd.citationstyles.style+xml
+application/vnd.claymore cla
+application/vnd.cloanto.rp9 rp9
+application/vnd.clonk.c4group c4g c4d c4f c4p c4u
+application/vnd.cluetrust.cartomobile-config c11amc
+application/vnd.cluetrust.cartomobile-config-pkg c11amz
+# application/vnd.coffeescript
+# application/vnd.collection+json
+# application/vnd.collection.doc+json
+# application/vnd.collection.next+json
+# application/vnd.comicbook+zip
+# application/vnd.commerce-battelle
+application/vnd.commonspace csp
+application/vnd.contact.cmsg cdbcmsg
+# application/vnd.coreos.ignition+json
+application/vnd.cosmocaller cmc
+application/vnd.crick.clicker clkx
+application/vnd.crick.clicker.keyboard clkk
+application/vnd.crick.clicker.palette clkp
+application/vnd.crick.clicker.template clkt
+application/vnd.crick.clicker.wordbank clkw
+application/vnd.criticaltools.wbs+xml wbs
+application/vnd.ctc-posml pml
+# application/vnd.ctct.ws+xml
+# application/vnd.cups-pdf
+# application/vnd.cups-postscript
+application/vnd.cups-ppd ppd
+# application/vnd.cups-raster
+# application/vnd.cups-raw
+# application/vnd.curl
+application/vnd.curl.car car
+application/vnd.curl.pcurl pcurl
+# application/vnd.cyan.dean.root+xml
+# application/vnd.cybank
+application/vnd.dart dart
+application/vnd.data-vision.rdz rdz
+# application/vnd.debian.binary-package
+application/vnd.dece.data uvf uvvf uvd uvvd
+application/vnd.dece.ttml+xml uvt uvvt
+application/vnd.dece.unspecified uvx uvvx
+application/vnd.dece.zip uvz uvvz
+application/vnd.denovo.fcselayout-link fe_launch
+# application/vnd.desmume.movie
+# application/vnd.dir-bi.plate-dl-nosuffix
+# application/vnd.dm.delegation+xml
+application/vnd.dna dna
+# application/vnd.document+json
+application/vnd.dolby.mlp mlp
+# application/vnd.dolby.mobile.1
+# application/vnd.dolby.mobile.2
+# application/vnd.doremir.scorecloud-binary-document
+application/vnd.dpgraph dpg
+application/vnd.dreamfactory dfac
+# application/vnd.drive+json
+application/vnd.ds-keypoint kpxx
+# application/vnd.dtg.local
+# application/vnd.dtg.local.flash
+# application/vnd.dtg.local.html
+application/vnd.dvb.ait ait
+# application/vnd.dvb.dvbj
+# application/vnd.dvb.esgcontainer
+# application/vnd.dvb.ipdcdftnotifaccess
+# application/vnd.dvb.ipdcesgaccess
+# application/vnd.dvb.ipdcesgaccess2
+# application/vnd.dvb.ipdcesgpdd
+# application/vnd.dvb.ipdcroaming
+# application/vnd.dvb.iptv.alfec-base
+# application/vnd.dvb.iptv.alfec-enhancement
+# application/vnd.dvb.notif-aggregate-root+xml
+# application/vnd.dvb.notif-container+xml
+# application/vnd.dvb.notif-generic+xml
+# application/vnd.dvb.notif-ia-msglist+xml
+# application/vnd.dvb.notif-ia-registration-request+xml
+# application/vnd.dvb.notif-ia-registration-response+xml
+# application/vnd.dvb.notif-init+xml
+# application/vnd.dvb.pfr
+application/vnd.dvb.service svc
+# application/vnd.dxr
+application/vnd.dynageo geo
+# application/vnd.dzr
+# application/vnd.easykaraoke.cdgdownload
+# application/vnd.ecdis-update
+application/vnd.ecowin.chart mag
+# application/vnd.ecowin.filerequest
+# application/vnd.ecowin.fileupdate
+# application/vnd.ecowin.series
+# application/vnd.ecowin.seriesrequest
+# application/vnd.ecowin.seriesupdate
+# application/vnd.emclient.accessrequest+xml
+application/vnd.enliven nml
+# application/vnd.enphase.envoy
+# application/vnd.eprints.data+xml
+application/vnd.epson.esf esf
+application/vnd.epson.msf msf
+application/vnd.epson.quickanime qam
+application/vnd.epson.salt slt
+application/vnd.epson.ssf ssf
+# application/vnd.ericsson.quickcall
+application/vnd.eszigno3+xml es3 et3
+# application/vnd.etsi.aoc+xml
+# application/vnd.etsi.asic-e+zip
+# application/vnd.etsi.asic-s+zip
+# application/vnd.etsi.cug+xml
+# application/vnd.etsi.iptvcommand+xml
+# application/vnd.etsi.iptvdiscovery+xml
+# application/vnd.etsi.iptvprofile+xml
+# application/vnd.etsi.iptvsad-bc+xml
+# application/vnd.etsi.iptvsad-cod+xml
+# application/vnd.etsi.iptvsad-npvr+xml
+# application/vnd.etsi.iptvservice+xml
+# application/vnd.etsi.iptvsync+xml
+# application/vnd.etsi.iptvueprofile+xml
+# application/vnd.etsi.mcid+xml
+# application/vnd.etsi.mheg5
+# application/vnd.etsi.overload-control-policy-dataset+xml
+# application/vnd.etsi.pstn+xml
+# application/vnd.etsi.sci+xml
+# application/vnd.etsi.simservs+xml
+# application/vnd.etsi.timestamp-token
+# application/vnd.etsi.tsl+xml
+# application/vnd.etsi.tsl.der
+# application/vnd.eudora.data
+application/vnd.ezpix-album ez2
+application/vnd.ezpix-package ez3
+# application/vnd.f-secure.mobile
+# application/vnd.fastcopy-disk-image
+application/vnd.fdf fdf
+application/vnd.fdsn.mseed mseed
+application/vnd.fdsn.seed seed dataless
+# application/vnd.ffsns
+# application/vnd.filmit.zfc
+# application/vnd.fints
+# application/vnd.firemonkeys.cloudcell
+application/vnd.flographit gph
+application/vnd.fluxtime.clip ftc
+# application/vnd.font-fontforge-sfd
+application/vnd.framemaker fm frame maker book
+application/vnd.frogans.fnc fnc
+application/vnd.frogans.ltf ltf
+application/vnd.fsc.weblaunch fsc
+application/vnd.fujitsu.oasys oas
+application/vnd.fujitsu.oasys2 oa2
+application/vnd.fujitsu.oasys3 oa3
+application/vnd.fujitsu.oasysgp fg5
+application/vnd.fujitsu.oasysprs bh2
+# application/vnd.fujixerox.art-ex
+# application/vnd.fujixerox.art4
+application/vnd.fujixerox.ddd ddd
+application/vnd.fujixerox.docuworks xdw
+application/vnd.fujixerox.docuworks.binder xbd
+# application/vnd.fujixerox.docuworks.container
+# application/vnd.fujixerox.hbpl
+# application/vnd.fut-misnet
+application/vnd.fuzzysheet fzs
+application/vnd.genomatix.tuxedo txd
+# application/vnd.geo+json
+# application/vnd.geocube+xml
+application/vnd.geogebra.file ggb
+application/vnd.geogebra.tool ggt
+application/vnd.geometry-explorer gex gre
+application/vnd.geonext gxt
+application/vnd.geoplan g2w
+application/vnd.geospace g3w
+# application/vnd.gerber
+# application/vnd.globalplatform.card-content-mgt
+# application/vnd.globalplatform.card-content-mgt-response
+application/vnd.gmx gmx
+application/vnd.google-earth.kml+xml kml
+application/vnd.google-earth.kmz kmz
+# application/vnd.gov.sk.e-form+xml
+# application/vnd.gov.sk.e-form+zip
+# application/vnd.gov.sk.xmldatacontainer+xml
+application/vnd.grafeq gqf gqs
+# application/vnd.gridmp
+application/vnd.groove-account gac
+application/vnd.groove-help ghf
+application/vnd.groove-identity-message gim
+application/vnd.groove-injector grv
+application/vnd.groove-tool-message gtm
+application/vnd.groove-tool-template tpl
+application/vnd.groove-vcard vcg
+# application/vnd.hal+json
+application/vnd.hal+xml hal
+application/vnd.handheld-entertainment+xml zmm
+application/vnd.hbci hbci
+# application/vnd.hcl-bireports
+# application/vnd.hdt
+# application/vnd.heroku+json
+application/vnd.hhe.lesson-player les
+application/vnd.hp-hpgl hpgl
+application/vnd.hp-hpid hpid
+application/vnd.hp-hps hps
+application/vnd.hp-jlyt jlt
+application/vnd.hp-pcl pcl
+application/vnd.hp-pclxl pclxl
+# application/vnd.httphone
+application/vnd.hydrostatix.sof-data sfd-hdstx
+# application/vnd.hyperdrive+json
+# application/vnd.hzn-3d-crossword
+# application/vnd.ibm.afplinedata
+# application/vnd.ibm.electronic-media
+application/vnd.ibm.minipay mpy
+application/vnd.ibm.modcap afp listafp list3820
+application/vnd.ibm.rights-management irm
+application/vnd.ibm.secure-container sc
+application/vnd.iccprofile icc icm
+# application/vnd.ieee.1905
+application/vnd.igloader igl
+application/vnd.immervision-ivp ivp
+application/vnd.immervision-ivu ivu
+# application/vnd.ims.imsccv1p1
+# application/vnd.ims.imsccv1p2
+# application/vnd.ims.imsccv1p3
+# application/vnd.ims.lis.v2.result+json
+# application/vnd.ims.lti.v2.toolconsumerprofile+json
+# application/vnd.ims.lti.v2.toolproxy+json
+# application/vnd.ims.lti.v2.toolproxy.id+json
+# application/vnd.ims.lti.v2.toolsettings+json
+# application/vnd.ims.lti.v2.toolsettings.simple+json
+# application/vnd.informedcontrol.rms+xml
+# application/vnd.informix-visionary
+# application/vnd.infotech.project
+# application/vnd.infotech.project+xml
+# application/vnd.innopath.wamp.notification
+application/vnd.insors.igm igm
+application/vnd.intercon.formnet xpw xpx
+application/vnd.intergeo i2g
+# application/vnd.intertrust.digibox
+# application/vnd.intertrust.nncp
+application/vnd.intu.qbo qbo
+application/vnd.intu.qfx qfx
+# application/vnd.iptc.g2.catalogitem+xml
+# application/vnd.iptc.g2.conceptitem+xml
+# application/vnd.iptc.g2.knowledgeitem+xml
+# application/vnd.iptc.g2.newsitem+xml
+# application/vnd.iptc.g2.newsmessage+xml
+# application/vnd.iptc.g2.packageitem+xml
+# application/vnd.iptc.g2.planningitem+xml
+application/vnd.ipunplugged.rcprofile rcprofile
+application/vnd.irepository.package+xml irp
+application/vnd.is-xpr xpr
+application/vnd.isac.fcs fcs
+application/vnd.jam jam
+# application/vnd.japannet-directory-service
+# application/vnd.japannet-jpnstore-wakeup
+# application/vnd.japannet-payment-wakeup
+# application/vnd.japannet-registration
+# application/vnd.japannet-registration-wakeup
+# application/vnd.japannet-setstore-wakeup
+# application/vnd.japannet-verification
+# application/vnd.japannet-verification-wakeup
+application/vnd.jcp.javame.midlet-rms rms
+application/vnd.jisp jisp
+application/vnd.joost.joda-archive joda
+# application/vnd.jsk.isdn-ngn
+application/vnd.kahootz ktz ktr
+application/vnd.kde.karbon karbon
+application/vnd.kde.kchart chrt
+application/vnd.kde.kformula kfo
+application/vnd.kde.kivio flw
+application/vnd.kde.kontour kon
+application/vnd.kde.kpresenter kpr kpt
+application/vnd.kde.kspread ksp
+application/vnd.kde.kword kwd kwt
+application/vnd.kenameaapp htke
+application/vnd.kidspiration kia
+application/vnd.kinar kne knp
+application/vnd.koan skp skd skt skm
+application/vnd.kodak-descriptor sse
+application/vnd.las.las+xml lasxml
+# application/vnd.liberty-request+xml
+application/vnd.llamagraphics.life-balance.desktop lbd
+application/vnd.llamagraphics.life-balance.exchange+xml lbe
+application/vnd.lotus-1-2-3 123
+application/vnd.lotus-approach apr
+application/vnd.lotus-freelance pre
+application/vnd.lotus-notes nsf
+application/vnd.lotus-organizer org
+application/vnd.lotus-screencam scm
+application/vnd.lotus-wordpro lwp
+application/vnd.macports.portpkg portpkg
+# application/vnd.mapbox-vector-tile
+# application/vnd.marlin.drm.actiontoken+xml
+# application/vnd.marlin.drm.conftoken+xml
+# application/vnd.marlin.drm.license+xml
+# application/vnd.marlin.drm.mdcf
+# application/vnd.mason+json
+# application/vnd.maxmind.maxmind-db
+application/vnd.mcd mcd
+application/vnd.medcalcdata mc1
+application/vnd.mediastation.cdkey cdkey
+# application/vnd.meridian-slingshot
+application/vnd.mfer mwf
+application/vnd.mfmp mfm
+# application/vnd.micro+json
+application/vnd.micrografx.flo flo
+application/vnd.micrografx.igx igx
+# application/vnd.microsoft.portable-executable
+# application/vnd.miele+json
+application/vnd.mif mif
+# application/vnd.minisoft-hp3000-save
+# application/vnd.mitsubishi.misty-guard.trustweb
+application/vnd.mobius.daf daf
+application/vnd.mobius.dis dis
+application/vnd.mobius.mbk mbk
+application/vnd.mobius.mqy mqy
+application/vnd.mobius.msl msl
+application/vnd.mobius.plc plc
+application/vnd.mobius.txf txf
+application/vnd.mophun.application mpn
+application/vnd.mophun.certificate mpc
+# application/vnd.motorola.flexsuite
+# application/vnd.motorola.flexsuite.adsi
+# application/vnd.motorola.flexsuite.fis
+# application/vnd.motorola.flexsuite.gotap
+# application/vnd.motorola.flexsuite.kmr
+# application/vnd.motorola.flexsuite.ttc
+# application/vnd.motorola.flexsuite.wem
+# application/vnd.motorola.iprm
+application/vnd.mozilla.xul+xml xul
+# application/vnd.ms-3mfdocument
+application/vnd.ms-artgalry cil
+# application/vnd.ms-asf
+application/vnd.ms-cab-compressed cab
+# application/vnd.ms-color.iccprofile
+application/vnd.ms-excel xls xlm xla xlc xlt xlw
+application/vnd.ms-excel.addin.macroenabled.12 xlam
+application/vnd.ms-excel.sheet.binary.macroenabled.12 xlsb
+application/vnd.ms-excel.sheet.macroenabled.12 xlsm
+application/vnd.ms-excel.template.macroenabled.12 xltm
+application/vnd.ms-fontobject eot
+application/vnd.ms-htmlhelp chm
+application/vnd.ms-ims ims
+application/vnd.ms-lrm lrm
+# application/vnd.ms-office.activex+xml
+application/vnd.ms-officetheme thmx
+# application/vnd.ms-opentype
+# application/vnd.ms-package.obfuscated-opentype
+application/vnd.ms-pki.seccat cat
+application/vnd.ms-pki.stl stl
+# application/vnd.ms-playready.initiator+xml
+application/vnd.ms-powerpoint ppt pps pot
+application/vnd.ms-powerpoint.addin.macroenabled.12 ppam
+application/vnd.ms-powerpoint.presentation.macroenabled.12 pptm
+application/vnd.ms-powerpoint.slide.macroenabled.12 sldm
+application/vnd.ms-powerpoint.slideshow.macroenabled.12 ppsm
+application/vnd.ms-powerpoint.template.macroenabled.12 potm
+# application/vnd.ms-printdevicecapabilities+xml
+# application/vnd.ms-printing.printticket+xml
+# application/vnd.ms-printschematicket+xml
+application/vnd.ms-project mpp mpt
+# application/vnd.ms-tnef
+# application/vnd.ms-windows.devicepairing
+# application/vnd.ms-windows.nwprinting.oob
+# application/vnd.ms-windows.printerpairing
+# application/vnd.ms-windows.wsd.oob
+# application/vnd.ms-wmdrm.lic-chlg-req
+# application/vnd.ms-wmdrm.lic-resp
+# application/vnd.ms-wmdrm.meter-chlg-req
+# application/vnd.ms-wmdrm.meter-resp
+application/vnd.ms-word.document.macroenabled.12 docm
+application/vnd.ms-word.template.macroenabled.12 dotm
+application/vnd.ms-works wps wks wcm wdb
+application/vnd.ms-wpl wpl
+application/vnd.ms-xpsdocument xps
+# application/vnd.msa-disk-image
+application/vnd.mseq mseq
+# application/vnd.msign
+# application/vnd.multiad.creator
+# application/vnd.multiad.creator.cif
+# application/vnd.music-niff
+application/vnd.musician mus
+application/vnd.muvee.style msty
+application/vnd.mynfc taglet
+# application/vnd.ncd.control
+# application/vnd.ncd.reference
+# application/vnd.nervana
+# application/vnd.netfpx
+application/vnd.neurolanguage.nlu nlu
+# application/vnd.nintendo.nitro.rom
+# application/vnd.nintendo.snes.rom
+application/vnd.nitf ntf nitf
+application/vnd.noblenet-directory nnd
+application/vnd.noblenet-sealer nns
+application/vnd.noblenet-web nnw
+# application/vnd.nokia.catalogs
+# application/vnd.nokia.conml+wbxml
+# application/vnd.nokia.conml+xml
+# application/vnd.nokia.iptv.config+xml
+# application/vnd.nokia.isds-radio-presets
+# application/vnd.nokia.landmark+wbxml
+# application/vnd.nokia.landmark+xml
+# application/vnd.nokia.landmarkcollection+xml
+# application/vnd.nokia.n-gage.ac+xml
+application/vnd.nokia.n-gage.data ngdat
+application/vnd.nokia.n-gage.symbian.install n-gage
+# application/vnd.nokia.ncd
+# application/vnd.nokia.pcd+wbxml
+# application/vnd.nokia.pcd+xml
+application/vnd.nokia.radio-preset rpst
+application/vnd.nokia.radio-presets rpss
+application/vnd.novadigm.edm edm
+application/vnd.novadigm.edx edx
+application/vnd.novadigm.ext ext
+# application/vnd.ntt-local.content-share
+# application/vnd.ntt-local.file-transfer
+# application/vnd.ntt-local.ogw_remote-access
+# application/vnd.ntt-local.sip-ta_remote
+# application/vnd.ntt-local.sip-ta_tcp_stream
+application/vnd.oasis.opendocument.chart odc
+application/vnd.oasis.opendocument.chart-template otc
+application/vnd.oasis.opendocument.database odb
+application/vnd.oasis.opendocument.formula odf
+application/vnd.oasis.opendocument.formula-template odft
+application/vnd.oasis.opendocument.graphics odg
+application/vnd.oasis.opendocument.graphics-template otg
+application/vnd.oasis.opendocument.image odi
+application/vnd.oasis.opendocument.image-template oti
+application/vnd.oasis.opendocument.presentation odp
+application/vnd.oasis.opendocument.presentation-template otp
+application/vnd.oasis.opendocument.spreadsheet ods
+application/vnd.oasis.opendocument.spreadsheet-template ots
+application/vnd.oasis.opendocument.text odt
+application/vnd.oasis.opendocument.text-master odm
+application/vnd.oasis.opendocument.text-template ott
+application/vnd.oasis.opendocument.text-web oth
+# application/vnd.obn
+# application/vnd.oftn.l10n+json
+# application/vnd.oipf.contentaccessdownload+xml
+# application/vnd.oipf.contentaccessstreaming+xml
+# application/vnd.oipf.cspg-hexbinary
+# application/vnd.oipf.dae.svg+xml
+# application/vnd.oipf.dae.xhtml+xml
+# application/vnd.oipf.mippvcontrolmessage+xml
+# application/vnd.oipf.pae.gem
+# application/vnd.oipf.spdiscovery+xml
+# application/vnd.oipf.spdlist+xml
+# application/vnd.oipf.ueprofile+xml
+# application/vnd.oipf.userprofile+xml
+application/vnd.olpc-sugar xo
+# application/vnd.oma-scws-config
+# application/vnd.oma-scws-http-request
+# application/vnd.oma-scws-http-response
+# application/vnd.oma.bcast.associated-procedure-parameter+xml
+# application/vnd.oma.bcast.drm-trigger+xml
+# application/vnd.oma.bcast.imd+xml
+# application/vnd.oma.bcast.ltkm
+# application/vnd.oma.bcast.notification+xml
+# application/vnd.oma.bcast.provisioningtrigger
+# application/vnd.oma.bcast.sgboot
+# application/vnd.oma.bcast.sgdd+xml
+# application/vnd.oma.bcast.sgdu
+# application/vnd.oma.bcast.simple-symbol-container
+# application/vnd.oma.bcast.smartcard-trigger+xml
+# application/vnd.oma.bcast.sprov+xml
+# application/vnd.oma.bcast.stkm
+# application/vnd.oma.cab-address-book+xml
+# application/vnd.oma.cab-feature-handler+xml
+# application/vnd.oma.cab-pcc+xml
+# application/vnd.oma.cab-subs-invite+xml
+# application/vnd.oma.cab-user-prefs+xml
+# application/vnd.oma.dcd
+# application/vnd.oma.dcdc
+application/vnd.oma.dd2+xml dd2
+# application/vnd.oma.drm.risd+xml
+# application/vnd.oma.group-usage-list+xml
+# application/vnd.oma.lwm2m+json
+# application/vnd.oma.lwm2m+tlv
+# application/vnd.oma.pal+xml
+# application/vnd.oma.poc.detailed-progress-report+xml
+# application/vnd.oma.poc.final-report+xml
+# application/vnd.oma.poc.groups+xml
+# application/vnd.oma.poc.invocation-descriptor+xml
+# application/vnd.oma.poc.optimized-progress-report+xml
+# application/vnd.oma.push
+# application/vnd.oma.scidm.messages+xml
+# application/vnd.oma.xcap-directory+xml
+# application/vnd.omads-email+xml
+# application/vnd.omads-file+xml
+# application/vnd.omads-folder+xml
+# application/vnd.omaloc-supl-init
+# application/vnd.onepager
+# application/vnd.openblox.game+xml
+# application/vnd.openblox.game-binary
+# application/vnd.openeye.oeb
+application/vnd.openofficeorg.extension oxt
+# application/vnd.openxmlformats-officedocument.custom-properties+xml
+# application/vnd.openxmlformats-officedocument.customxmlproperties+xml
+# application/vnd.openxmlformats-officedocument.drawing+xml
+# application/vnd.openxmlformats-officedocument.drawingml.chart+xml
+# application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml
+# application/vnd.openxmlformats-officedocument.drawingml.diagramcolors+xml
+# application/vnd.openxmlformats-officedocument.drawingml.diagramdata+xml
+# application/vnd.openxmlformats-officedocument.drawingml.diagramlayout+xml
+# application/vnd.openxmlformats-officedocument.drawingml.diagramstyle+xml
+# application/vnd.openxmlformats-officedocument.extended-properties+xml
+# application/vnd.openxmlformats-officedocument.presentationml.commentauthors+xml
+# application/vnd.openxmlformats-officedocument.presentationml.comments+xml
+# application/vnd.openxmlformats-officedocument.presentationml.handoutmaster+xml
+# application/vnd.openxmlformats-officedocument.presentationml.notesmaster+xml
+# application/vnd.openxmlformats-officedocument.presentationml.notesslide+xml
+application/vnd.openxmlformats-officedocument.presentationml.presentation pptx
+# application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml
+# application/vnd.openxmlformats-officedocument.presentationml.presprops+xml
+application/vnd.openxmlformats-officedocument.presentationml.slide sldx
+# application/vnd.openxmlformats-officedocument.presentationml.slide+xml
+# application/vnd.openxmlformats-officedocument.presentationml.slidelayout+xml
+# application/vnd.openxmlformats-officedocument.presentationml.slidemaster+xml
+application/vnd.openxmlformats-officedocument.presentationml.slideshow ppsx
+# application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml
+# application/vnd.openxmlformats-officedocument.presentationml.slideupdateinfo+xml
+# application/vnd.openxmlformats-officedocument.presentationml.tablestyles+xml
+# application/vnd.openxmlformats-officedocument.presentationml.tags+xml
+application/vnd.openxmlformats-officedocument.presentationml.template potx
+# application/vnd.openxmlformats-officedocument.presentationml.template.main+xml
+# application/vnd.openxmlformats-officedocument.presentationml.viewprops+xml
+# application/vnd.openxmlformats-officedocument.spreadsheetml.calcchain+xml
+# application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml
+# application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml
+# application/vnd.openxmlformats-officedocument.spreadsheetml.connections+xml
+# application/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml
+# application/vnd.openxmlformats-officedocument.spreadsheetml.externallink+xml
+# application/vnd.openxmlformats-officedocument.spreadsheetml.pivotcachedefinition+xml
+# application/vnd.openxmlformats-officedocument.spreadsheetml.pivotcacherecords+xml
+# application/vnd.openxmlformats-officedocument.spreadsheetml.pivottable+xml
+# application/vnd.openxmlformats-officedocument.spreadsheetml.querytable+xml
+# application/vnd.openxmlformats-officedocument.spreadsheetml.revisionheaders+xml
+# application/vnd.openxmlformats-officedocument.spreadsheetml.revisionlog+xml
+# application/vnd.openxmlformats-officedocument.spreadsheetml.sharedstrings+xml
+application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx
+# application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml
+# application/vnd.openxmlformats-officedocument.spreadsheetml.sheetmetadata+xml
+# application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml
+# application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml
+# application/vnd.openxmlformats-officedocument.spreadsheetml.tablesinglecells+xml
+application/vnd.openxmlformats-officedocument.spreadsheetml.template xltx
+# application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml
+# application/vnd.openxmlformats-officedocument.spreadsheetml.usernames+xml
+# application/vnd.openxmlformats-officedocument.spreadsheetml.volatiledependencies+xml
+# application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml
+# application/vnd.openxmlformats-officedocument.theme+xml
+# application/vnd.openxmlformats-officedocument.themeoverride+xml
+# application/vnd.openxmlformats-officedocument.vmldrawing
+# application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml
+application/vnd.openxmlformats-officedocument.wordprocessingml.document docx
+# application/vnd.openxmlformats-officedocument.wordprocessingml.document.glossary+xml
+# application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml
+# application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml
+# application/vnd.openxmlformats-officedocument.wordprocessingml.fonttable+xml
+# application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml
+# application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml
+# application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml
+# application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml
+# application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml
+application/vnd.openxmlformats-officedocument.wordprocessingml.template dotx
+# application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml
+# application/vnd.openxmlformats-officedocument.wordprocessingml.websettings+xml
+# application/vnd.openxmlformats-package.core-properties+xml
+# application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml
+# application/vnd.openxmlformats-package.relationships+xml
+# application/vnd.oracle.resource+json
+# application/vnd.orange.indata
+# application/vnd.osa.netdeploy
+application/vnd.osgeo.mapguide.package mgp
+# application/vnd.osgi.bundle
+application/vnd.osgi.dp dp
+application/vnd.osgi.subsystem esa
+# application/vnd.otps.ct-kip+xml
+# application/vnd.oxli.countgraph
+# application/vnd.pagerduty+json
+application/vnd.palm pdb pqa oprc
+# application/vnd.panoply
+# application/vnd.paos.xml
+application/vnd.pawaafile paw
+# application/vnd.pcos
+application/vnd.pg.format str
+application/vnd.pg.osasli ei6
+# application/vnd.piaccess.application-licence
+application/vnd.picsel efif
+application/vnd.pmi.widget wg
+# application/vnd.poc.group-advertisement+xml
+application/vnd.pocketlearn plf
+application/vnd.powerbuilder6 pbd
+# application/vnd.powerbuilder6-s
+# application/vnd.powerbuilder7
+# application/vnd.powerbuilder7-s
+# application/vnd.powerbuilder75
+# application/vnd.powerbuilder75-s
+# application/vnd.preminet
+application/vnd.previewsystems.box box
+application/vnd.proteus.magazine mgz
+application/vnd.publishare-delta-tree qps
+application/vnd.pvi.ptid1 ptid
+# application/vnd.pwg-multiplexed
+# application/vnd.pwg-xhtml-print+xml
+# application/vnd.qualcomm.brew-app-res
+# application/vnd.quarantainenet
+application/vnd.quark.quarkxpress qxd qxt qwd qwt qxl qxb
+# application/vnd.quobject-quoxdocument
+# application/vnd.radisys.moml+xml
+# application/vnd.radisys.msml+xml
+# application/vnd.radisys.msml-audit+xml
+# application/vnd.radisys.msml-audit-conf+xml
+# application/vnd.radisys.msml-audit-conn+xml
+# application/vnd.radisys.msml-audit-dialog+xml
+# application/vnd.radisys.msml-audit-stream+xml
+# application/vnd.radisys.msml-conf+xml
+# application/vnd.radisys.msml-dialog+xml
+# application/vnd.radisys.msml-dialog-base+xml
+# application/vnd.radisys.msml-dialog-fax-detect+xml
+# application/vnd.radisys.msml-dialog-fax-sendrecv+xml
+# application/vnd.radisys.msml-dialog-group+xml
+# application/vnd.radisys.msml-dialog-speech+xml
+# application/vnd.radisys.msml-dialog-transform+xml
+# application/vnd.rainstor.data
+# application/vnd.rapid
+# application/vnd.rar
+application/vnd.realvnc.bed bed
+application/vnd.recordare.musicxml mxl
+application/vnd.recordare.musicxml+xml musicxml
+# application/vnd.renlearn.rlprint
+application/vnd.rig.cryptonote cryptonote
+application/vnd.rim.cod cod
+application/vnd.rn-realmedia rm
+application/vnd.rn-realmedia-vbr rmvb
+application/vnd.route66.link66+xml link66
+# application/vnd.rs-274x
+# application/vnd.ruckus.download
+# application/vnd.s3sms
+application/vnd.sailingtracker.track st
+# application/vnd.sbm.cid
+# application/vnd.sbm.mid2
+# application/vnd.scribus
+# application/vnd.sealed.3df
+# application/vnd.sealed.csf
+# application/vnd.sealed.doc
+# application/vnd.sealed.eml
+# application/vnd.sealed.mht
+# application/vnd.sealed.net
+# application/vnd.sealed.ppt
+# application/vnd.sealed.tiff
+# application/vnd.sealed.xls
+# application/vnd.sealedmedia.softseal.html
+# application/vnd.sealedmedia.softseal.pdf
+application/vnd.seemail see
+application/vnd.sema sema
+application/vnd.semd semd
+application/vnd.semf semf
+application/vnd.shana.informed.formdata ifm
+application/vnd.shana.informed.formtemplate itp
+application/vnd.shana.informed.interchange iif
+application/vnd.shana.informed.package ipk
+application/vnd.simtech-mindmapper twd twds
+# application/vnd.siren+json
+application/vnd.smaf mmf
+# application/vnd.smart.notebook
+application/vnd.smart.teacher teacher
+# application/vnd.software602.filler.form+xml
+# application/vnd.software602.filler.form-xml-zip
+application/vnd.solent.sdkm+xml sdkm sdkd
+application/vnd.spotfire.dxp dxp
+application/vnd.spotfire.sfs sfs
+# application/vnd.sss-cod
+# application/vnd.sss-dtf
+# application/vnd.sss-ntf
+application/vnd.stardivision.calc sdc
+application/vnd.stardivision.draw sda
+application/vnd.stardivision.impress sdd
+application/vnd.stardivision.math smf
+application/vnd.stardivision.writer sdw vor
+application/vnd.stardivision.writer-global sgl
+application/vnd.stepmania.package smzip
+application/vnd.stepmania.stepchart sm
+# application/vnd.street-stream
+# application/vnd.sun.wadl+xml
+application/vnd.sun.xml.calc sxc
+application/vnd.sun.xml.calc.template stc
+application/vnd.sun.xml.draw sxd
+application/vnd.sun.xml.draw.template std
+application/vnd.sun.xml.impress sxi
+application/vnd.sun.xml.impress.template sti
+application/vnd.sun.xml.math sxm
+application/vnd.sun.xml.writer sxw
+application/vnd.sun.xml.writer.global sxg
+application/vnd.sun.xml.writer.template stw
+application/vnd.sus-calendar sus susp
+application/vnd.svd svd
+# application/vnd.swiftview-ics
+application/vnd.symbian.install sis sisx
+application/vnd.syncml+xml xsm
+application/vnd.syncml.dm+wbxml bdm
+application/vnd.syncml.dm+xml xdm
+# application/vnd.syncml.dm.notification
+# application/vnd.syncml.dmddf+wbxml
+# application/vnd.syncml.dmddf+xml
+# application/vnd.syncml.dmtnds+wbxml
+# application/vnd.syncml.dmtnds+xml
+# application/vnd.syncml.ds.notification
+application/vnd.tao.intent-module-archive tao
+application/vnd.tcpdump.pcap pcap cap dmp
+# application/vnd.tmd.mediaflex.api+xml
+# application/vnd.tml
+application/vnd.tmobile-livetv tmo
+application/vnd.trid.tpt tpt
+application/vnd.triscape.mxs mxs
+application/vnd.trueapp tra
+# application/vnd.truedoc
+# application/vnd.ubisoft.webplayer
+application/vnd.ufdl ufd ufdl
+application/vnd.uiq.theme utz
+application/vnd.umajin umj
+application/vnd.unity unityweb
+application/vnd.uoml+xml uoml
+# application/vnd.uplanet.alert
+# application/vnd.uplanet.alert-wbxml
+# application/vnd.uplanet.bearer-choice
+# application/vnd.uplanet.bearer-choice-wbxml
+# application/vnd.uplanet.cacheop
+# application/vnd.uplanet.cacheop-wbxml
+# application/vnd.uplanet.channel
+# application/vnd.uplanet.channel-wbxml
+# application/vnd.uplanet.list
+# application/vnd.uplanet.list-wbxml
+# application/vnd.uplanet.listcmd
+# application/vnd.uplanet.listcmd-wbxml
+# application/vnd.uplanet.signal
+# application/vnd.uri-map
+# application/vnd.valve.source.material
+application/vnd.vcx vcx
+# application/vnd.vd-study
+# application/vnd.vectorworks
+# application/vnd.vel+json
+# application/vnd.verimatrix.vcas
+# application/vnd.vidsoft.vidconference
+application/vnd.visio vsd vst vss vsw
+application/vnd.visionary vis
+# application/vnd.vividence.scriptfile
+application/vnd.vsf vsf
+# application/vnd.wap.sic
+# application/vnd.wap.slc
+application/vnd.wap.wbxml wbxml
+application/vnd.wap.wmlc wmlc
+application/vnd.wap.wmlscriptc wmlsc
+application/vnd.webturbo wtb
+# application/vnd.wfa.p2p
+# application/vnd.wfa.wsc
+# application/vnd.windows.devicepairing
+# application/vnd.wmc
+# application/vnd.wmf.bootstrap
+# application/vnd.wolfram.mathematica
+# application/vnd.wolfram.mathematica.package
+application/vnd.wolfram.player nbp
+application/vnd.wordperfect wpd
+application/vnd.wqd wqd
+# application/vnd.wrq-hp3000-labelled
+application/vnd.wt.stf stf
+# application/vnd.wv.csp+wbxml
+# application/vnd.wv.csp+xml
+# application/vnd.wv.ssp+xml
+# application/vnd.xacml+json
+application/vnd.xara xar
+application/vnd.xfdl xfdl
+# application/vnd.xfdl.webform
+# application/vnd.xmi+xml
+# application/vnd.xmpie.cpkg
+# application/vnd.xmpie.dpkg
+# application/vnd.xmpie.plan
+# application/vnd.xmpie.ppkg
+# application/vnd.xmpie.xlim
+application/vnd.yamaha.hv-dic hvd
+application/vnd.yamaha.hv-script hvs
+application/vnd.yamaha.hv-voice hvp
+application/vnd.yamaha.openscoreformat osf
+application/vnd.yamaha.openscoreformat.osfpvg+xml osfpvg
+# application/vnd.yamaha.remote-setup
+application/vnd.yamaha.smaf-audio saf
+application/vnd.yamaha.smaf-phrase spf
+# application/vnd.yamaha.through-ngn
+# application/vnd.yamaha.tunnel-udpencap
+# application/vnd.yaoweme
+application/vnd.yellowriver-custom-menu cmp
+application/vnd.zul zir zirz
+application/vnd.zzazz.deck+xml zaz
+application/voicexml+xml vxml
+# application/vq-rtcpxr
+# application/watcherinfo+xml
+# application/whoispp-query
+# application/whoispp-response
+application/widget wgt
+application/winhlp hlp
+# application/wita
+# application/wordperfect5.1
+application/wsdl+xml wsdl
+application/wspolicy+xml wspolicy
+application/x-7z-compressed 7z
+application/x-abiword abw
+application/x-ace-compressed ace
+# application/x-amf
+application/x-apple-diskimage dmg
+application/x-authorware-bin aab x32 u32 vox
+application/x-authorware-map aam
+application/x-authorware-seg aas
+application/x-bcpio bcpio
+application/x-bittorrent torrent
+application/x-blorb blb blorb
+application/x-bzip bz
+application/x-bzip2 bz2 boz
+application/x-cbr cbr cba cbt cbz cb7
+application/x-cdlink vcd
+application/x-cfs-compressed cfs
+application/x-chat chat
+application/x-chess-pgn pgn
+# application/x-compress
+application/x-conference nsc
+application/x-cpio cpio
+application/x-csh csh
+application/x-debian-package deb udeb
+application/x-dgc-compressed dgc
+application/x-director dir dcr dxr cst cct cxt w3d fgd swa
+application/x-doom wad
+application/x-dtbncx+xml ncx
+application/x-dtbook+xml dtb
+application/x-dtbresource+xml res
+application/x-dvi dvi
+application/x-envoy evy
+application/x-eva eva
+application/x-font-bdf bdf
+# application/x-font-dos
+# application/x-font-framemaker
+application/x-font-ghostscript gsf
+# application/x-font-libgrx
+application/x-font-linux-psf psf
+application/x-font-pcf pcf
+application/x-font-snf snf
+# application/x-font-speedo
+# application/x-font-sunos-news
+application/x-font-type1 pfa pfb pfm afm
+# application/x-font-vfont
+application/x-freearc arc
+application/x-futuresplash spl
+application/x-gca-compressed gca
+application/x-glulx ulx
+application/x-gnumeric gnumeric
+application/x-gramps-xml gramps
+application/x-gtar gtar
+# application/x-gzip
+application/x-hdf hdf
+application/x-install-instructions install
+application/x-iso9660-image iso
+application/x-java-jnlp-file jnlp
+application/x-latex latex
+application/x-lzh-compressed lzh lha
+application/x-mie mie
+application/x-mobipocket-ebook prc mobi
+application/x-ms-application application
+application/x-ms-shortcut lnk
+application/x-ms-wmd wmd
+application/x-ms-wmz wmz
+application/x-ms-xbap xbap
+application/x-msaccess mdb
+application/x-msbinder obd
+application/x-mscardfile crd
+application/x-msclip clp
+application/x-msdownload exe dll com bat msi
+application/x-msmediaview mvb m13 m14
+application/x-msmetafile wmf wmz emf emz
+application/x-msmoney mny
+application/x-mspublisher pub
+application/x-msschedule scd
+application/x-msterminal trm
+application/x-mswrite wri
+application/x-netcdf nc cdf
+application/x-nzb nzb
+application/x-pkcs12 p12 pfx
+application/x-pkcs7-certificates p7b spc
+application/x-pkcs7-certreqresp p7r
+application/x-rar-compressed rar
+application/x-research-info-systems ris
+application/x-sh sh
+application/x-shar shar
+application/x-shockwave-flash swf
+application/x-silverlight-app xap
+application/x-sql sql
+application/x-stuffit sit
+application/x-stuffitx sitx
+application/x-subrip srt
+application/x-sv4cpio sv4cpio
+application/x-sv4crc sv4crc
+application/x-t3vm-image t3
+application/x-tads gam
+application/x-tar tar
+application/x-tcl tcl
+application/x-tex tex
+application/x-tex-tfm tfm
+application/x-texinfo texinfo texi
+application/x-tgif obj
+application/x-ustar ustar
+application/x-wais-source src
+# application/x-www-form-urlencoded
+application/x-x509-ca-cert der crt
+application/x-xfig fig
+application/x-xliff+xml xlf
+application/x-xpinstall xpi
+application/x-xz xz
+application/x-zmachine z1 z2 z3 z4 z5 z6 z7 z8
+# application/x400-bp
+# application/xacml+xml
+application/xaml+xml xaml
+# application/xcap-att+xml
+# application/xcap-caps+xml
+application/xcap-diff+xml xdf
+# application/xcap-el+xml
+# application/xcap-error+xml
+# application/xcap-ns+xml
+# application/xcon-conference-info+xml
+# application/xcon-conference-info-diff+xml
+application/xenc+xml xenc
+application/xhtml+xml xhtml xht
+# application/xhtml-voice+xml
+application/xml xml xsl
+application/xml-dtd dtd
+# application/xml-external-parsed-entity
+# application/xml-patch+xml
+# application/xmpp+xml
+application/xop+xml xop
+application/xproc+xml xpl
+application/xslt+xml xslt
+application/xspf+xml xspf
+application/xv+xml mxml xhvml xvml xvm
+application/yang yang
+application/yin+xml yin
+application/zip zip
+# application/zlib
+# audio/1d-interleaved-parityfec
+# audio/32kadpcm
+# audio/3gpp
+# audio/3gpp2
+# audio/ac3
+audio/adpcm adp
+# audio/amr
+# audio/amr-wb
+# audio/amr-wb+
+# audio/aptx
+# audio/asc
+# audio/atrac-advanced-lossless
+# audio/atrac-x
+# audio/atrac3
+audio/basic au snd
+# audio/bv16
+# audio/bv32
+# audio/clearmode
+# audio/cn
+# audio/dat12
+# audio/dls
+# audio/dsr-es201108
+# audio/dsr-es202050
+# audio/dsr-es202211
+# audio/dsr-es202212
+# audio/dv
+# audio/dvi4
+# audio/eac3
+# audio/encaprtp
+# audio/evrc
+# audio/evrc-qcp
+# audio/evrc0
+# audio/evrc1
+# audio/evrcb
+# audio/evrcb0
+# audio/evrcb1
+# audio/evrcnw
+# audio/evrcnw0
+# audio/evrcnw1
+# audio/evrcwb
+# audio/evrcwb0
+# audio/evrcwb1
+# audio/evs
+# audio/example
+# audio/fwdred
+# audio/g711-0
+# audio/g719
+# audio/g722
+# audio/g7221
+# audio/g723
+# audio/g726-16
+# audio/g726-24
+# audio/g726-32
+# audio/g726-40
+# audio/g728
+# audio/g729
+# audio/g7291
+# audio/g729d
+# audio/g729e
+# audio/gsm
+# audio/gsm-efr
+# audio/gsm-hr-08
+# audio/ilbc
+# audio/ip-mr_v2.5
+# audio/isac
+# audio/l16
+# audio/l20
+# audio/l24
+# audio/l8
+# audio/lpc
+audio/midi mid midi kar rmi
+# audio/mobile-xmf
+audio/mp4 m4a mp4a
+# audio/mp4a-latm
+# audio/mpa
+# audio/mpa-robust
+audio/mpeg mp3 mpga mp2 mp2a m2a m3a
+# audio/mpeg4-generic
+# audio/musepack
+audio/ogg ogg oga spx
+# audio/opus
+# audio/parityfec
+# audio/pcma
+# audio/pcma-wb
+# audio/pcmu
+# audio/pcmu-wb
+# audio/prs.sid
+# audio/qcelp
+# audio/raptorfec
+# audio/red
+# audio/rtp-enc-aescm128
+# audio/rtp-midi
+# audio/rtploopback
+# audio/rtx
+audio/s3m s3m
+audio/silk sil
+# audio/smv
+# audio/smv-qcp
+# audio/smv0
+# audio/sp-midi
+# audio/speex
+# audio/t140c
+# audio/t38
+# audio/telephone-event
+# audio/tone
+# audio/uemclip
+# audio/ulpfec
+# audio/vdvi
+# audio/vmr-wb
+# audio/vnd.3gpp.iufp
+# audio/vnd.4sb
+# audio/vnd.audiokoz
+# audio/vnd.celp
+# audio/vnd.cisco.nse
+# audio/vnd.cmles.radio-events
+# audio/vnd.cns.anp1
+# audio/vnd.cns.inf1
+audio/vnd.dece.audio uva uvva
+audio/vnd.digital-winds eol
+# audio/vnd.dlna.adts
+# audio/vnd.dolby.heaac.1
+# audio/vnd.dolby.heaac.2
+# audio/vnd.dolby.mlp
+# audio/vnd.dolby.mps
+# audio/vnd.dolby.pl2
+# audio/vnd.dolby.pl2x
+# audio/vnd.dolby.pl2z
+# audio/vnd.dolby.pulse.1
+audio/vnd.dra dra
+audio/vnd.dts dts
+audio/vnd.dts.hd dtshd
+# audio/vnd.dvb.file
+# audio/vnd.everad.plj
+# audio/vnd.hns.audio
+audio/vnd.lucent.voice lvp
+audio/vnd.ms-playready.media.pya pya
+# audio/vnd.nokia.mobile-xmf
+# audio/vnd.nortel.vbk
+audio/vnd.nuera.ecelp4800 ecelp4800
+audio/vnd.nuera.ecelp7470 ecelp7470
+audio/vnd.nuera.ecelp9600 ecelp9600
+# audio/vnd.octel.sbc
+# audio/vnd.qcelp
+# audio/vnd.rhetorex.32kadpcm
+audio/vnd.rip rip
+# audio/vnd.sealedmedia.softseal.mpeg
+# audio/vnd.vmx.cvsd
+# audio/vorbis
+# audio/vorbis-config
+audio/webm weba
+audio/x-aac aac
+audio/x-aiff aif aiff aifc
+audio/x-caf caf
+audio/x-flac flac
+audio/x-matroska mka
+audio/x-mpegurl m3u
+audio/x-ms-wax wax
+audio/x-ms-wma wma
+audio/x-pn-realaudio ram ra
+audio/x-pn-realaudio-plugin rmp
+# audio/x-tta
+audio/x-wav wav
+audio/xm xm
+chemical/x-cdx cdx
+chemical/x-cif cif
+chemical/x-cmdf cmdf
+chemical/x-cml cml
+chemical/x-csml csml
+# chemical/x-pdb
+chemical/x-xyz xyz
+font/collection ttc
+font/otf otf
+# font/sfnt
+font/ttf ttf
+font/woff woff
+font/woff2 woff2
+image/bmp bmp
+image/cgm cgm
+# image/dicom-rle
+# image/emf
+# image/example
+# image/fits
+image/g3fax g3
+image/gif gif
+image/ief ief
+# image/jls
+# image/jp2
+image/jpeg jpg jpeg jpe
+# image/jpm
+# image/jpx
+image/ktx ktx
+# image/naplps
+image/png png
+image/prs.btif btif
+# image/prs.pti
+# image/pwg-raster
+image/sgi sgi
+image/svg+xml svg svgz
+# image/t38
+image/tiff tiff tif
+# image/tiff-fx
+image/vnd.adobe.photoshop psd
+# image/vnd.airzip.accelerator.azv
+# image/vnd.cns.inf2
+image/vnd.dece.graphic uvi uvvi uvg uvvg
+image/vnd.djvu djvu djv
+image/vnd.dvb.subtitle sub
+image/vnd.dwg dwg
+image/vnd.dxf dxf
+image/vnd.fastbidsheet fbs
+image/vnd.fpx fpx
+image/vnd.fst fst
+image/vnd.fujixerox.edmics-mmr mmr
+image/vnd.fujixerox.edmics-rlc rlc
+# image/vnd.globalgraphics.pgb
+# image/vnd.microsoft.icon
+# image/vnd.mix
+# image/vnd.mozilla.apng
+image/vnd.ms-modi mdi
+image/vnd.ms-photo wdp
+image/vnd.net-fpx npx
+# image/vnd.radiance
+# image/vnd.sealed.png
+# image/vnd.sealedmedia.softseal.gif
+# image/vnd.sealedmedia.softseal.jpg
+# image/vnd.svf
+# image/vnd.tencent.tap
+# image/vnd.valve.source.texture
+image/vnd.wap.wbmp wbmp
+image/vnd.xiff xif
+# image/vnd.zbrush.pcx
+image/webp webp
+# image/wmf
+image/x-3ds 3ds
+image/x-cmu-raster ras
+image/x-cmx cmx
+image/x-freehand fh fhc fh4 fh5 fh7
+image/x-icon ico
+image/x-mrsid-image sid
+image/x-pcx pcx
+image/x-pict pic pct
+image/x-portable-anymap pnm
+image/x-portable-bitmap pbm
+image/x-portable-graymap pgm
+image/x-portable-pixmap ppm
+image/x-rgb rgb
+image/x-tga tga
+image/x-xbitmap xbm
+image/x-xpixmap xpm
+image/x-xwindowdump xwd
+# message/cpim
+# message/delivery-status
+# message/disposition-notification
+# message/example
+# message/external-body
+# message/feedback-report
+# message/global
+# message/global-delivery-status
+# message/global-disposition-notification
+# message/global-headers
+# message/http
+# message/imdn+xml
+# message/news
+# message/partial
+message/rfc822 eml mime
+# message/s-http
+# message/sip
+# message/sipfrag
+# message/tracking-status
+# message/vnd.si.simp
+# message/vnd.wfa.wsc
+# model/example
+# model/gltf+json
+model/iges igs iges
+model/mesh msh mesh silo
+model/vnd.collada+xml dae
+model/vnd.dwf dwf
+# model/vnd.flatland.3dml
+model/vnd.gdl gdl
+# model/vnd.gs-gdl
+# model/vnd.gs.gdl
+model/vnd.gtw gtw
+# model/vnd.moml+xml
+model/vnd.mts mts
+# model/vnd.opengex
+# model/vnd.parasolid.transmit.binary
+# model/vnd.parasolid.transmit.text
+# model/vnd.rosette.annotated-data-model
+# model/vnd.valve.source.compiled-map
+model/vnd.vtu vtu
+model/vrml wrl vrml
+model/x3d+binary x3db x3dbz
+# model/x3d+fastinfoset
+model/x3d+vrml x3dv x3dvz
+model/x3d+xml x3d x3dz
+# model/x3d-vrml
+# multipart/alternative
+# multipart/appledouble
+# multipart/byteranges
+# multipart/digest
+# multipart/encrypted
+# multipart/example
+# multipart/form-data
+# multipart/header-set
+# multipart/mixed
+# multipart/parallel
+# multipart/related
+# multipart/report
+# multipart/signed
+# multipart/voice-message
+# multipart/x-mixed-replace
+# text/1d-interleaved-parityfec
+text/cache-manifest appcache
+text/calendar ics ifb
+text/css css
+text/csv csv
+# text/csv-schema
+# text/directory
+# text/dns
+# text/ecmascript
+# text/encaprtp
+# text/enriched
+# text/example
+# text/fwdred
+# text/grammar-ref-list
+text/html html htm
+# text/javascript
+# text/jcr-cnd
+# text/markdown
+# text/mizar
+text/n3 n3
+# text/parameters
+# text/parityfec
+text/plain txt text conf def list log in
+# text/provenance-notation
+# text/prs.fallenstein.rst
+text/prs.lines.tag dsc
+# text/prs.prop.logic
+# text/raptorfec
+# text/red
+# text/rfc822-headers
+text/richtext rtx
+# text/rtf
+# text/rtp-enc-aescm128
+# text/rtploopback
+# text/rtx
+text/sgml sgml sgm
+# text/t140
+text/tab-separated-values tsv
+text/troff t tr roff man me ms
+text/turtle ttl
+# text/ulpfec
+text/uri-list uri uris urls
+text/vcard vcard
+# text/vnd.a
+# text/vnd.abc
+text/vnd.curl curl
+text/vnd.curl.dcurl dcurl
+text/vnd.curl.mcurl mcurl
+text/vnd.curl.scurl scurl
+# text/vnd.debian.copyright
+# text/vnd.dmclientscript
+text/vnd.dvb.subtitle sub
+# text/vnd.esmertec.theme-descriptor
+text/vnd.fly fly
+text/vnd.fmi.flexstor flx
+text/vnd.graphviz gv
+text/vnd.in3d.3dml 3dml
+text/vnd.in3d.spot spot
+# text/vnd.iptc.newsml
+# text/vnd.iptc.nitf
+# text/vnd.latex-z
+# text/vnd.motorola.reflex
+# text/vnd.ms-mediapackage
+# text/vnd.net2phone.commcenter.command
+# text/vnd.radisys.msml-basic-layout
+# text/vnd.si.uricatalogue
+text/vnd.sun.j2me.app-descriptor jad
+# text/vnd.trolltech.linguist
+# text/vnd.wap.si
+# text/vnd.wap.sl
+text/vnd.wap.wml wml
+text/vnd.wap.wmlscript wmls
+text/x-asm s asm
+text/x-c c cc cxx cpp h hh dic
+text/x-fortran f for f77 f90
+text/x-java-source java
+text/x-nfo nfo
+text/x-opml opml
+text/x-pascal p pas
+text/x-setext etx
+text/x-sfv sfv
+text/x-uuencode uu
+text/x-vcalendar vcs
+text/x-vcard vcf
+# text/xml
+# text/xml-external-parsed-entity
+# video/1d-interleaved-parityfec
+video/3gpp 3gp
+# video/3gpp-tt
+video/3gpp2 3g2
+# video/bmpeg
+# video/bt656
+# video/celb
+# video/dv
+# video/encaprtp
+# video/example
+video/h261 h261
+video/h263 h263
+# video/h263-1998
+# video/h263-2000
+video/h264 h264
+# video/h264-rcdo
+# video/h264-svc
+# video/h265
+# video/iso.segment
+video/jpeg jpgv
+# video/jpeg2000
+video/jpm jpm jpgm
+video/mj2 mj2 mjp2
+# video/mp1s
+# video/mp2p
+# video/mp2t
+video/mp4 mp4 mp4v mpg4
+# video/mp4v-es
+video/mpeg mpeg mpg mpe m1v m2v
+# video/mpeg4-generic
+# video/mpv
+# video/nv
+video/ogg ogv
+# video/parityfec
+# video/pointer
+video/quicktime qt mov
+# video/raptorfec
+# video/raw
+# video/rtp-enc-aescm128
+# video/rtploopback
+# video/rtx
+# video/smpte292m
+# video/ulpfec
+# video/vc1
+# video/vnd.cctv
+video/vnd.dece.hd uvh uvvh
+video/vnd.dece.mobile uvm uvvm
+# video/vnd.dece.mp4
+video/vnd.dece.pd uvp uvvp
+video/vnd.dece.sd uvs uvvs
+video/vnd.dece.video uvv uvvv
+# video/vnd.directv.mpeg
+# video/vnd.directv.mpeg-tts
+# video/vnd.dlna.mpeg-tts
+video/vnd.dvb.file dvb
+video/vnd.fvt fvt
+# video/vnd.hns.video
+# video/vnd.iptvforum.1dparityfec-1010
+# video/vnd.iptvforum.1dparityfec-2005
+# video/vnd.iptvforum.2dparityfec-1010
+# video/vnd.iptvforum.2dparityfec-2005
+# video/vnd.iptvforum.ttsavc
+# video/vnd.iptvforum.ttsmpeg2
+# video/vnd.motorola.video
+# video/vnd.motorola.videop
+video/vnd.mpegurl mxu m4u
+video/vnd.ms-playready.media.pyv pyv
+# video/vnd.nokia.interleaved-multimedia
+# video/vnd.nokia.videovoip
+# video/vnd.objectvideo
+# video/vnd.radgamettools.bink
+# video/vnd.radgamettools.smacker
+# video/vnd.sealed.mpeg1
+# video/vnd.sealed.mpeg4
+# video/vnd.sealed.swf
+# video/vnd.sealedmedia.softseal.mov
+video/vnd.uvvu.mp4 uvu uvvu
+video/vnd.vivo viv
+# video/vp8
+video/webm webm
+video/x-f4v f4v
+video/x-fli fli
+video/x-flv flv
+video/x-m4v m4v
+video/x-matroska mkv mk3d mks
+video/x-mng mng
+video/x-ms-asf asf asx
+video/x-ms-vob vob
+video/x-ms-wm wm
+video/x-ms-wmv wmv
+video/x-ms-wmx wmx
+video/x-ms-wvx wvx
+video/x-msvideo avi
+video/x-sgi-movie movie
+video/x-smv smv
+x-conference/x-cooltalk ice
+
+# Telegram animated stickers
+application/x-tgsticker tgs
\ No newline at end of file
diff --git a/pyrogram/client/ext/utils.py b/pyrogram/client/ext/utils.py
index 981752fa..fa107fab 100644
--- a/pyrogram/client/ext/utils.py
+++ b/pyrogram/client/ext/utils.py
@@ -16,8 +16,13 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see .
+import base64
+import struct
from base64 import b64decode, b64encode
+from typing import Union, List
+import pyrogram
+from . import BaseClient
from ...api import types
@@ -82,3 +87,119 @@ def get_offset_date(dialogs):
return m.date
else:
return 0
+
+
+def get_input_media_from_file_id(
+ file_id_str: str,
+ expected_media_type: int = None
+) -> Union[types.InputMediaPhoto, types.InputMediaDocument]:
+ try:
+ decoded = decode(file_id_str)
+ except Exception:
+ raise ValueError("Failed to decode file_id: {}".format(file_id_str))
+ else:
+ media_type = decoded[0]
+
+ if expected_media_type is not None:
+ if media_type != expected_media_type:
+ media_type_str = BaseClient.MEDIA_TYPE_ID.get(media_type, None)
+ expected_media_type_str = BaseClient.MEDIA_TYPE_ID.get(expected_media_type, None)
+
+ raise ValueError(
+ 'Expected: "{}", got "{}" file_id instead'.format(expected_media_type_str, media_type_str)
+ )
+
+ if media_type in (0, 1, 14):
+ raise ValueError("This file_id can only be used for download: {}".format(file_id_str))
+
+ if media_type == 2:
+ unpacked = struct.unpack(" List["pyrogram.Message"]:
+ users = {i.id: i for i in messages.users}
+ chats = {i.id: i for i in messages.chats}
+
+ if not messages.messages:
+ return pyrogram.List()
+
+ parsed_messages = [
+ pyrogram.Message._parse(client, message, users, chats, replies=0)
+ for message in messages.messages
+ ]
+
+ if replies:
+ messages_with_replies = {i.id: getattr(i, "reply_to_msg_id", None) for i in messages.messages}
+ reply_message_ids = [i[0] for i in filter(lambda x: x[1] is not None, messages_with_replies.items())]
+
+ if reply_message_ids:
+ reply_messages = client.get_messages(
+ parsed_messages[0].chat.id,
+ reply_to_message_ids=reply_message_ids,
+ replies=replies - 1
+ )
+
+ for message in parsed_messages:
+ reply_id = messages_with_replies[message.message_id]
+
+ for reply in reply_messages:
+ if reply.message_id == reply_id:
+ message.reply_to_message = reply
+
+ return pyrogram.List(parsed_messages)
+
+
+def parse_deleted_messages(client, update) -> List["pyrogram.Message"]:
+ messages = update.messages
+ channel_id = getattr(update, "channel_id", None)
+
+ parsed_messages = []
+
+ for message in messages:
+ parsed_messages.append(
+ pyrogram.Message(
+ message_id=message,
+ chat=pyrogram.Chat(
+ id=int("-100" + str(channel_id)),
+ type="channel",
+ client=client
+ ) if channel_id is not None else None,
+ client=client
+ )
+ )
+
+ return pyrogram.List(parsed_messages)
+
+
+def unpack_inline_message_id(inline_message_id: str) -> types.InputBotInlineMessageID:
+ r = inline_message_id + "=" * (-len(inline_message_id) % 4)
+ r = struct.unpack(" type:
- """Use this method to create a Filter.
+ """Create a Filter.
Custom filters give you extra control over which updates are allowed or not to be processed by your handlers.
- Args:
+ Parameters:
name (``str``):
Your filter's name. Can be anything you like.
@@ -35,14 +35,14 @@ def create(name: str, func: callable, **kwargs) -> type:
A function that accepts two arguments *(filter, update)* and returns a Boolean: True if the update should be
handled, False otherwise.
The "update" argument type will vary depending on which `Handler `_ is coming from.
- For example, in a :obj:`MessageHandler ` the update type will be
- a :obj:`Message `; in a :obj:`CallbackQueryHandler ` the
- update type will be a :obj:`CallbackQuery `. Your function body can then access the
+ For example, in a :obj:`MessageHandler` the update type will be
+ a :obj:`Message`; in a :obj:`CallbackQueryHandler` the
+ update type will be a :obj:`CallbackQuery`. Your function body can then access the
incoming update and decide whether to allow it or not.
**kwargs (``any``, *optional*):
Any keyword argument you would like to pass. Useful for custom filters that accept parameters (e.g.:
- :meth:`Filters.command`, :meth:`Filters.regex`).
+ :meth:`~Filters.command`, :meth:`~Filters.regex`).
"""
# TODO: unpack kwargs using **kwargs into the dict itself. For Python 3.5+ only
d = {"__call__": func}
@@ -54,9 +54,9 @@ def create(name: str, func: callable, **kwargs) -> type:
class Filters:
"""This class provides access to all library-defined Filters available in Pyrogram.
- The Filters listed here are intended to be used with the :obj:`MessageHandler ` only.
+ The Filters listed here are intended to be used with the :obj:`MessageHandler` only.
At the moment, if you want to filter updates coming from different `Handlers `_ you have to create
- your own filters with :meth:`Filters.create` and use them in the same way.
+ your own filters with :meth:`~Filters.create` and use them in the same way.
"""
create = create
@@ -89,49 +89,49 @@ class Filters:
"""Filter edited messages."""
audio = create("Audio", lambda _, m: bool(m.audio))
- """Filter messages that contain :obj:`Audio ` objects."""
+ """Filter messages that contain :obj:`Audio` objects."""
document = create("Document", lambda _, m: bool(m.document))
- """Filter messages that contain :obj:`Document ` objects."""
+ """Filter messages that contain :obj:`Document` objects."""
photo = create("Photo", lambda _, m: bool(m.photo))
- """Filter messages that contain :obj:`Photo ` objects."""
+ """Filter messages that contain :obj:`Photo` objects."""
sticker = create("Sticker", lambda _, m: bool(m.sticker))
- """Filter messages that contain :obj:`Sticker ` objects."""
+ """Filter messages that contain :obj:`Sticker` objects."""
animation = create("Animation", lambda _, m: bool(m.animation))
- """Filter messages that contain :obj:`Animation ` objects."""
+ """Filter messages that contain :obj:`Animation` objects."""
game = create("Game", lambda _, m: bool(m.game))
- """Filter messages that contain :obj:`Game ` objects."""
+ """Filter messages that contain :obj:`Game` objects."""
video = create("Video", lambda _, m: bool(m.video))
- """Filter messages that contain :obj:`Video ` objects."""
+ """Filter messages that contain :obj:`Video` objects."""
media_group = create("MediaGroup", lambda _, m: bool(m.media_group_id))
"""Filter messages containing photos or videos being part of an album."""
voice = create("Voice", lambda _, m: bool(m.voice))
- """Filter messages that contain :obj:`Voice ` note objects."""
+ """Filter messages that contain :obj:`Voice` note objects."""
video_note = create("VideoNote", lambda _, m: bool(m.video_note))
- """Filter messages that contain :obj:`VideoNote ` objects."""
+ """Filter messages that contain :obj:`VideoNote` objects."""
contact = create("Contact", lambda _, m: bool(m.contact))
- """Filter messages that contain :obj:`Contact ` objects."""
+ """Filter messages that contain :obj:`Contact` objects."""
location = create("Location", lambda _, m: bool(m.location))
- """Filter messages that contain :obj:`Location ` objects."""
+ """Filter messages that contain :obj:`Location` objects."""
venue = create("Venue", lambda _, m: bool(m.venue))
- """Filter messages that contain :obj:`Venue ` objects."""
+ """Filter messages that contain :obj:`Venue` objects."""
web_page = create("WebPage", lambda _, m: m.web_page)
"""Filter messages sent with a webpage preview."""
poll = create("Poll", lambda _, m: m.poll)
- """Filter messages that contain :obj:`Poll ` objects."""
+ """Filter messages that contain :obj:`Poll` objects."""
private = create("Private", lambda _, m: bool(m.chat and m.chat.type == "private"))
"""Filter messages sent in private chats."""
@@ -191,35 +191,19 @@ class Filters:
"""Filter messages sent via inline bots"""
service = create("Service", lambda _, m: bool(m.service))
- """Filter service messages. A service message contains any of the following fields set
+ """Filter service messages.
- - left_chat_member
- - new_chat_title
- - new_chat_photo
- - delete_chat_photo
- - group_chat_created
- - supergroup_chat_created
- - channel_chat_created
- - migrate_to_chat_id
- - migrate_from_chat_id
- - pinned_message
- - game_score"""
+ A service message contains any of the following fields set: *left_chat_member*,
+ *new_chat_title*, *new_chat_photo*, *delete_chat_photo*, *group_chat_created*, *supergroup_chat_created*,
+ *channel_chat_created*, *migrate_to_chat_id*, *migrate_from_chat_id*, *pinned_message*, *game_score*.
+ """
media = create("Media", lambda _, m: bool(m.media))
- """Filter media messages. A media message contains any of the following fields set
+ """Filter media messages.
- - audio
- - document
- - photo
- - sticker
- - video
- - animation
- - voice
- - video_note
- - contact
- - location
- - venue
- - poll"""
+ A media message contains any of the following fields set: *audio*, *document*, *photo*, *sticker*, *video*,
+ *animation*, *voice*, *video_note*, *contact*, *location*, *venue*, *poll*.
+ """
@staticmethod
def command(
@@ -230,12 +214,12 @@ class Filters:
):
"""Filter commands, i.e.: text messages starting with "/" or any other custom prefix.
- Args:
+ Parameters:
commands (``str`` | ``list``):
The command or list of commands as string the filter should look for.
Examples: "start", ["start", "help", "settings"]. When a message text containing
a command arrives, the command itself and its arguments will be stored in the *command*
- field of the :class:`Message `.
+ field of the :obj:`Message`.
prefix (``str`` | ``list``, *optional*):
A prefix or a list of prefixes as string the filter should look for.
@@ -275,11 +259,11 @@ class Filters:
def regex(pattern, flags: int = 0):
"""Filter messages that match a given RegEx pattern.
- Args:
+ Parameters:
pattern (``str``):
The RegEx pattern as string, it will be applied to the text of a message. When a pattern matches,
all the `Match Objects `_
- are stored in the *matches* field of the :class:`Message ` itself.
+ are stored in the *matches* field of the :obj:`Message` itself.
flags (``int``, *optional*):
RegEx flags.
@@ -298,7 +282,7 @@ class Filters:
You can use `set bound methods `_ to manipulate the
users container.
- Args:
+ Parameters:
users (``int`` | ``str`` | ``list``):
Pass one or more user ids/usernames to filter users.
For you yourself, "me" or "self" can be used as well.
@@ -329,7 +313,7 @@ class Filters:
You can use `set bound methods `_ to manipulate the
chats container.
- Args:
+ Parameters:
chats (``int`` | ``str`` | ``list``):
Pass one or more chat ids/usernames to filter chats.
For your personal cloud (Saved Messages) you can simply use "me" or "self".
@@ -355,4 +339,15 @@ class Filters:
and message.from_user.is_self
and not message.outgoing)))
+ @staticmethod
+ def callback_data(data: str or bytes):
+ """Filter callback queries for their data.
+
+ Parameters:
+ data (``str`` | ``bytes``):
+ Pass the data you want to filter for.
+ """
+
+ return create("CallbackData", lambda flt, cb: cb.data == flt.data, data=data)
+
dan = create("Dan", lambda _, m: bool(m.from_user and m.from_user.id == 23122162))
diff --git a/pyrogram/client/handlers/__init__.py b/pyrogram/client/handlers/__init__.py
index 5e392949..c88c12fe 100644
--- a/pyrogram/client/handlers/__init__.py
+++ b/pyrogram/client/handlers/__init__.py
@@ -21,10 +21,11 @@ from .deleted_messages_handler import DeletedMessagesHandler
from .disconnect_handler import DisconnectHandler
from .inline_query_handler import InlineQueryHandler
from .message_handler import MessageHandler
+from .poll_handler import PollHandler
from .raw_update_handler import RawUpdateHandler
from .user_status_handler import UserStatusHandler
__all__ = [
"MessageHandler", "DeletedMessagesHandler", "CallbackQueryHandler", "RawUpdateHandler", "DisconnectHandler",
- "UserStatusHandler", "InlineQueryHandler"
+ "UserStatusHandler", "InlineQueryHandler", "PollHandler"
]
diff --git a/pyrogram/client/handlers/callback_query_handler.py b/pyrogram/client/handlers/callback_query_handler.py
index 88ddd5a0..9e17296b 100644
--- a/pyrogram/client/handlers/callback_query_handler.py
+++ b/pyrogram/client/handlers/callback_query_handler.py
@@ -21,22 +21,22 @@ from .handler import Handler
class CallbackQueryHandler(Handler):
"""The CallbackQuery handler class. Used to handle callback queries coming from inline buttons.
- It is intended to be used with :meth:`add_handler()