diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md deleted file mode 100644 index 27bb02a37dc..00000000000 --- a/.github/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,76 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, sex characteristics, gender identity and expression, -level of experience, education, socio-economic status, nationality, personal -appearance, race, religion, or sexual identity and orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment -include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or - advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at admin@pyrogram.ml. All -complaints will be reviewed and investigated and will result in a response that -is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html - -[homepage]: https://www.contributor-covenant.org - -For answers to common questions about this code of conduct, see -https://www.contributor-covenant.org/faq diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md deleted file mode 100644 index e721fd9829b..00000000000 --- a/.github/CONTRIBUTING.md +++ /dev/null @@ -1 +0,0 @@ -# How to Contribute diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index e0556d549bd..00000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -name: Bug Report -about: Create a bug report affecting the library -labels: "bug" ---- - - - -## Checklist -- [ ] I am sure the error is coming from Pyrogram's code and not elsewhere. -- [ ] I have searched in the issue tracker for similar bug reports, including closed ones. -- [ ] I ran `pip3 install -U https://github.com/pyrogram/pyrogram/archive/develop.zip` and reproduced the issue using the latest development version. - -## Description -A clear and concise description of the problem. - -## Steps to Reproduce -[A minimal, complete and verifiable example](https://stackoverflow.com/help/mcve). - -## Traceback -The full traceback (if applicable). \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000000..3b0ff4ee2df --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,52 @@ +name: Bug report +description: Report issues affecting the framework or the documentation +body: + - type: checkboxes + attributes: + label: Checklist + description: Invalid, incomplete or inadequate issue reports may not be taken into consideration + options: + - label: I am sure the error is coming from Pyrogram's code and not elsewhere + required: true + - label: I have searched in the issue tracker for similar bug reports, including closed ones + required: true + - label: I ran `pip3 install -U https://github.com/pyrogram/pyrogram/archive/master.zip` and reproduced the issue using the latest development version + required: true + + - type: textarea + attributes: + label: Description + description: Provide a clear and concise description of the issue + placeholder: Description... + validations: + required: true + + - type: textarea + attributes: + label: Steps to reproduce + description: Explain precisely how to reproduce the issue + placeholder: | + 1. + 2. + 3. + validations: + required: true + + - type: textarea + attributes: + label: Code example + description: Provide a [minimal, complete, consistently reproducible](https://stackoverflow.com/help/minimal-reproducible-example) and properly formatted example involving normal usages (if applicable) + placeholder: | + from pyrogram import Client + ... + render: python + + - type: textarea + attributes: + label: Logs + description: Provide the complete traceback (if applicable) + placeholder: | + Traceback (most recent call last): + File "main.py", line 1, in + ... + render: shell \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000000..80d40a188d0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Ask Pyrogram related questions + url: https://stackoverflow.com/questions/tagged/pyrogram + about: This place is only for reporting issues about Pyrogram. You can ask questions on StackOverflow. + - name: Join the Telegram channel + url: https://t.me/pyrogram + about: Join the official channel and stay tuned for news, updates and announcements. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 70a39192ced..00000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -name: Feature Request -about: Suggest ideas, new features or enhancements -labels: "enhancement" ---- - - - -## Checklist -- [ ] I believe the idea is awesome and would benefit the library. -- [ ] I have searched in the issue tracker for similar requests, including closed ones. - -## Description -A detailed description of the request. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000000..59202d14a93 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,20 @@ +name: Feature request +description: Suggest ideas, new features or enhancements +labels: [enhancement] +body: + - type: checkboxes + attributes: + label: Checklist + options: + - label: I believe the idea is awesome and would benefit the framework + required: true + - label: I have searched in the issue tracker for similar requests, including closed ones + required: true + + - type: textarea + attributes: + label: Description + description: Provide a detailed description of the request + placeholder: Description... + validations: + required: true \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md deleted file mode 100644 index 737304d9e49..00000000000 --- a/.github/ISSUE_TEMPLATE/question.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -name: Ask Question -about: Ask a Pyrogram related question -title: For Q&A purposes, please read this template body -labels: "question" ---- - - - -# Important -This place is for issues about Pyrogram, it's **not a forum**. - -If you'd like to post a question, please move to https://stackoverflow.com or join the Telegram community by following the description in https://t.me/pyrogram. - -Thanks. \ No newline at end of file diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml new file mode 100644 index 00000000000..e12233fb20b --- /dev/null +++ b/.github/workflows/python.yml @@ -0,0 +1,34 @@ +name: Pyrogram + +on: [push, pull_request] + +jobs: + build: + + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-latest] + python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] + + steps: + - uses: actions/checkout@v3 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install tox + + - name: Generate API + run: | + make venv + make api + + - name: Run tests + run: | + tox \ No newline at end of file diff --git a/.gitignore b/.gitignore index 0b1a0699e56..7a99a5db850 100644 --- a/.gitignore +++ b/.gitignore @@ -1,16 +1,28 @@ -# User's personal information +# Development +docs *.session config.ini +main.py +unknown_errors.txt +.DS_Store # Pyrogram generated code pyrogram/errors/exceptions/ -pyrogram/api/functions/ -pyrogram/api/types/ -pyrogram/api/all.py +pyrogram/raw/functions/ +pyrogram/raw/types/ +pyrogram/raw/base/ +pyrogram/raw/all.py +docs/source/telegram +docs/source/api/methods/ +docs/source/api/bound-methods/ +docs/source/api/types/ # PyCharm stuff .idea/ +# VS Code +.vscode/ + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] @@ -78,6 +90,7 @@ instance/ # Sphinx documentation docs/_build/ +docs/source/_build # PyBuilder target/ diff --git a/MANIFEST.in b/MANIFEST.in index 80c061ff473..395ca54ee9e 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,10 +1,13 @@ -## Include -include COPYING COPYING.lesser NOTICE requirements.txt +# Include files +include README.md COPYING COPYING.lesser NOTICE requirements.txt recursive-include compiler *.py *.tl *.tsv *.txt -recursive-include pyrogram mime.types +recursive-include tests *.py -## Exclude -prune pyrogram/api/errors/exceptions -prune pyrogram/api/functions -prune pyrogram/api/types -exclude pyrogram/api/all.py \ No newline at end of file +# Exclude files +exclude pyrogram/raw/all.py + +# Prune directories +prune pyrogram/errors/exceptions +prune pyrogram/raw/functions +prune pyrogram/raw/types +prune pyrogram/raw/base diff --git a/Makefile b/Makefile new file mode 100644 index 00000000000..32f30a85205 --- /dev/null +++ b/Makefile @@ -0,0 +1,42 @@ +VENV := venv +PYTHON := $(VENV)/bin/python +HOST = $(shell ifconfig | grep "inet " | tail -1 | cut -d\ -f2) +TAG = v$(shell grep -E '__version__ = ".*"' pyrogram/__init__.py | cut -d\" -f2) + +RM := rm -rf + +.PHONY: venv clean-build clean-api clean api build tag dtag + +venv: + $(RM) $(VENV) + python3 -m venv $(VENV) + $(PYTHON) -m pip install -U pip wheel setuptools + $(PYTHON) -m pip install -U -r requirements.txt -r dev-requirements.txt + @echo "Created venv with $$($(PYTHON) --version)" + +clean-build: + $(RM) *.egg-info build dist + +clean-api: + $(RM) pyrogram/errors/exceptions pyrogram/raw/all.py pyrogram/raw/base pyrogram/raw/functions pyrogram/raw/types + +clean: + make clean-build + make clean-api + +api: + cd compiler/api && ../../$(PYTHON) compiler.py + cd compiler/errors && ../../$(PYTHON) compiler.py + +build: + make clean + $(PYTHON) setup.py sdist + $(PYTHON) setup.py bdist_wheel + +tag: + git tag $(TAG) + git push origin $(TAG) + +dtag: + git tag -d $(TAG) + git push origin -d $(TAG) \ No newline at end of file diff --git a/NOTICE b/NOTICE index dd0fdf546b3..7a9aaab647c 100644 --- a/NOTICE +++ b/NOTICE @@ -1,5 +1,5 @@ Pyrogram - Telegram MTProto API Client Library for Python -Copyright (C) 2017-2019 Dan Tès +Copyright (C) 2017-present Dan This file is part of Pyrogram. diff --git a/README.md b/README.md new file mode 100644 index 00000000000..e250b406686 --- /dev/null +++ b/README.md @@ -0,0 +1,70 @@ +

+ + Pyrogram + +
+ Telegram MTProto API Framework for Python +
+ + Homepage + + • + + Documentation + + • + + Releases + + • + + News + +

+ +## Pyrogram + +> [!NOTE] +> The project is no longer maintained or supported. Thanks for appreciating it. + +> Elegant, modern and asynchronous Telegram MTProto API framework in Python for users and bots + +``` python +from pyrogram import Client, filters + +app = Client("my_account") + + +@app.on_message(filters.private) +async def hello(client, message): + await message.reply("Hello from Pyrogram!") + + +app.run() +``` + +**Pyrogram** is a modern, elegant and asynchronous [MTProto API](https://docs.pyrogram.org/topics/mtproto-vs-botapi) +framework. It enables you to easily interact with the main Telegram API through a user account (custom client) or a bot +identity (bot API alternative) using Python. + +### Key Features + +- **Ready**: Install Pyrogram with pip and start building your applications right away. +- **Easy**: Makes the Telegram API simple and intuitive, while still allowing advanced usages. +- **Elegant**: Low-level details are abstracted and re-presented in a more convenient way. +- **Fast**: Boosted up by [TgCrypto](https://github.com/pyrogram/tgcrypto), a high-performance cryptography library written in C. +- **Type-hinted**: Types and methods are all type-hinted, enabling excellent editor support. +- **Async**: Fully asynchronous (also usable synchronously if wanted, for convenience). +- **Powerful**: Full access to Telegram's API to execute any official client action and more. + +### Installing + +``` bash +pip3 install pyrogram +``` + +### Resources + +- Check out the docs at https://docs.pyrogram.org to learn more about Pyrogram, get started right +away and discover more in-depth material for building your client applications. +- Join the official channel at https://t.me/pyrogram and stay tuned for news, updates and announcements. diff --git a/README.rst b/README.rst deleted file mode 100644 index dfb03abcf45..00000000000 --- a/README.rst +++ /dev/null @@ -1,131 +0,0 @@ -|header| - -Pyrogram -======== - -.. code-block:: python - - from pyrogram import Client, Filters - - app = Client("my_account") - - - @app.on_message(Filters.private) - def hello(client, message): - message.reply("Hello {}".format(message.from_user.first_name)) - - - app.run() - -**Pyrogram** is an elegant, easy-to-use Telegram_ client library and framework written from the ground up in Python and C. -It enables you to easily create custom apps using both user and bot identities (bot API alternative) via the `MTProto API`_. - - `Pyrogram in fully-asynchronous mode is also available » `_ - - `Working PoC of Telegram voice calls using Pyrogram » `_ - -Features --------- - -- **Easy**: You can install Pyrogram with pip and start building your applications right away. -- **Elegant**: Low-level details are abstracted and re-presented in a much nicer and easier way. -- **Fast**: Crypto parts are boosted up by TgCrypto_, a high-performance library written in pure C. -- **Documented**: Pyrogram API methods, types and public interfaces are well documented. -- **Type-hinted**: Exposed Pyrogram types and method parameters are all type-hinted. -- **Updated**, to the latest Telegram API version, currently Layer 97 on top of `MTProto 2.0`_. -- **Pluggable**: The Smart Plugin system allows to write components with minimal boilerplate code. -- **Comprehensive**: Execute any advanced action an official client is able to do, and even more. - -Requirements ------------- - -- Python 3.4 or higher. -- A `Telegram API key`_. - -Installing ----------- - -.. code:: shell - - pip3 install pyrogram - -Resources ---------- - -- The Docs contain lots of resources to help you getting started with Pyrogram: https://docs.pyrogram.ml. -- Reading `Examples in this repository`_ is also a good way for learning how Pyrogram works. -- Seeking extra help? Don't be shy, come join and ask our Community_! -- For other requests you can send an Email_ or a Message_. - -Contributing ------------- - -Pyrogram is brand new, and **you are welcome to try it and help make it even better** by either submitting pull -requests or reporting issues/bugs as well as suggesting best practices, ideas, enhancements on both code -and documentation. Any help is appreciated! - -Copyright & License -------------------- - -- Copyright (C) 2017-2019 Dan Tès -- Licensed under the terms of the `GNU Lesser General Public License v3 or later (LGPLv3+)`_ - -.. _`Telegram`: https://telegram.org/ -.. _`MTProto API`: https://core.telegram.org/api#telegram-api -.. _`Telegram API key`: https://docs.pyrogram.ml/start/ProjectSetup#api-keys -.. _`Community`: https://t.me/PyrogramChat -.. _`Examples in this repository`: https://github.com/pyrogram/pyrogram/tree/master/examples -.. _`GitHub`: https://github.com/pyrogram/pyrogram/issues -.. _`Email`: admin@pyrogram.ml -.. _`Message`: https://t.me/haskell -.. _TgCrypto: https://github.com/pyrogram/tgcrypto -.. _`MTProto 2.0`: https://core.telegram.org/mtproto -.. _`GNU Lesser General Public License v3 or later (LGPLv3+)`: COPYING.lesser - -.. |header| raw:: html - -

- -
Pyrogram Logo
-
-

- -

- Telegram MTProto API Framework for Python - -
- - Documentation - - • - - Changelog - - • - - Community - -
- - Schema Layer - - - TgCrypto Version - -

- -.. |logo| image:: https://raw.githubusercontent.com/pyrogram/logos/master/logos/pyrogram_logo2.png - :target: https://pyrogram.ml - :alt: Pyrogram - -.. |description| replace:: **Telegram MTProto API Framework for Python** - -.. |schema| image:: https://img.shields.io/badge/schema-layer%2097-eda738.svg?longCache=true&colorA=262b30 - :target: compiler/api/source/main_api.tl - :alt: Schema Layer - -.. |tgcrypto| image:: https://img.shields.io/badge/tgcrypto-v1.1.1-eda738.svg?longCache=true&colorA=262b30 - :target: https://github.com/pyrogram/tgcrypto - :alt: TgCrypto Version diff --git a/compiler/__init__.py b/compiler/__init__.py index f3769dd4d0d..46887cb7a57 100644 --- a/compiler/__init__.py +++ b/compiler/__init__.py @@ -1,17 +1,17 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2019 Dan Tès +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan # -# This file is part of Pyrogram. +# 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 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. +# 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 . +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . diff --git a/compiler/api/__init__.py b/compiler/api/__init__.py index f3769dd4d0d..46887cb7a57 100644 --- a/compiler/api/__init__.py +++ b/compiler/api/__init__.py @@ -1,17 +1,17 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2019 Dan Tès +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan # -# This file is part of Pyrogram. +# 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 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. +# 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 . +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . diff --git a/compiler/api/compiler.py b/compiler/api/compiler.py index 53b35929aa3..c29b7ed87f4 100644 --- a/compiler/api/compiler.py +++ b/compiler/api/compiler.py @@ -1,158 +1,137 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2019 Dan Tès +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan # -# This file is part of Pyrogram. +# 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 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. +# 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 . +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . +import json import os import re import shutil +from functools import partial +from pathlib import Path +from typing import NamedTuple, List, Tuple -HOME = "compiler/api" -DESTINATION = "pyrogram/api" +# from autoflake import fix_code +# from black import format_str, FileMode + +HOME_PATH = Path("compiler/api") +DESTINATION_PATH = Path("pyrogram/raw") NOTICE_PATH = "NOTICE" + SECTION_RE = re.compile(r"---(\w+)---") LAYER_RE = re.compile(r"//\sLAYER\s(\d+)") -COMBINATOR_RE = re.compile(r"^([\w.]+)#([0-9a-f]+)\s(?:.*)=\s([\w<>.]+);(?: // Docs: (.+))?$", re.MULTILINE) -ARGS_RE = re.compile("[^{](\w+):([\w?!.<>#]+)") -FLAGS_RE = re.compile(r"flags\.(\d+)\?") -FLAGS_RE_2 = re.compile(r"flags\.(\d+)\?([\w<>.]+)") -FLAGS_RE_3 = re.compile(r"flags:#") +COMBINATOR_RE = re.compile(r"^([\w.]+)#([0-9a-f]+)\s(?:.*)=\s([\w<>.]+);$", re.MULTILINE) +ARGS_RE = re.compile(r"[^{](\w+):([\w?!.<>#]+)") +FLAGS_RE = re.compile(r"flags(\d?)\.(\d+)\?") +FLAGS_RE_2 = re.compile(r"flags(\d?)\.(\d+)\?([\w<>.]+)") +FLAGS_RE_3 = re.compile(r"flags(\d?):#") INT_RE = re.compile(r"int(\d+)") -core_types = ["int", "long", "int128", "int256", "double", "bytes", "string", "Bool"] -types_to_constructors = {} -types_to_functions = {} -constructors_to_functions = {} - - -def get_docstring_arg_type(t: str, is_list: bool = False, is_pyrogram_type: bool = False): - if t in core_types: - if t == "long": - return "``int`` ``64-bit``" - elif "int" in t: - size = INT_RE.match(t) - return "``int`` ``{}-bit``".format(size.group(1)) if size else "``int`` ``32-bit``" - elif t == "double": - return "``float`` ``64-bit``" - elif t == "string": - return "``str``" - else: - return "``{}``".format(t.lower()) - elif t == "true": - return "``bool``" - elif t == "Object" or t == "X": - return "Any object from :obj:`pyrogram.api.types`" - elif t == "!X": - return "Any method from :obj:`pyrogram.api.functions`" - elif t.startswith("Vector"): - return "List of " + get_docstring_arg_type(t.split("<", 1)[1][:-1], True, is_pyrogram_type) - else: - if is_pyrogram_type: - t = "pyrogram." + t +CORE_TYPES = ["int", "long", "int128", "int256", "double", "bytes", "string", "Bool", "true"] - t = types_to_constructors.get(t, [t]) - n = len(t) - 1 +WARNING = """ +# # # # # # # # # # # # # # # # # # # # # # # # +# !!! WARNING !!! # +# This is a generated file! # +# All changes made in this file will be lost! # +# # # # # # # # # # # # # # # # # # # # # # # # +""".strip() - t = (("e" if is_list else "E") + "ither " if n else "") + ", ".join( - ":obj:`{1} <{0}.{1}>`".format( - "pyrogram.types" if is_pyrogram_type else "pyrogram.api.types", - i.replace("pyrogram.", "") - ) - for i in t - ) +# noinspection PyShadowingBuiltins +open = partial(open, encoding="utf-8") - if n: - t = t.split(", ") - t = ", ".join(t[:-1]) + " or " + t[-1] +types_to_constructors = {} +types_to_functions = {} +constructors_to_functions = {} +namespaces_to_types = {} +namespaces_to_constructors = {} +namespaces_to_functions = {} + +try: + with open("docs.json") as f: + docs = json.load(f) +except FileNotFoundError: + docs = { + "type": {}, + "constructor": {}, + "method": {} + } + + +class Combinator(NamedTuple): + section: str + qualname: str + namespace: str + name: str + id: str + has_flags: bool + args: List[Tuple[str, str]] + qualtype: str + typespace: str + type: str + + +def snake(s: str): + # https://stackoverflow.com/q/1175208 + s = re.sub(r"(.)([A-Z][a-z]+)", r"\1_\2", s) + return re.sub(r"([a-z0-9])([A-Z])", r"\1_\2", s).lower() - return t +def camel(s: str): + return "".join([i[0].upper() + i[1:] for i in s.split("_")]) -def get_references(t: str): - t = constructors_to_functions.get(t) - if t: - n = len(t) - 1 +# noinspection PyShadowingBuiltins, PyShadowingNames +def get_type_hint(type: str) -> str: + is_flag = FLAGS_RE.match(type) + is_core = False - t = ", ".join( - ":obj:`{0} `".format(i) - for i in t - ) + if is_flag: + type = type.split("?")[1] - if n: - t = t.split(", ") - t = ", ".join(t[:-1]) + " and " + t[-1] + if type in CORE_TYPES: + is_core = True - return t + if type == "long" or "int" in type: + type = "int" + elif type == "double": + type = "float" + elif type == "string": + type = "str" + elif type in ["Bool", "true"]: + type = "bool" + else: # bytes and object + type = "bytes" + if type in ["Object", "!X"]: + return "TLObject" -def get_argument_type(arg): - is_flag = FLAGS_RE.match(arg[1]) - name, t = arg + if re.match("^vector", type, re.I): + is_core = True - if is_flag: - t = t.split("?")[1] + sub_type = type.split("<")[1][:-1] + type = f"List[{get_type_hint(sub_type)}]" - if t in core_types: - if t == "long" or "int" in t: - t = ": int" - elif t == "double": - t = ": float" - elif t == "string": - t = ": str" - else: - t = ": {}".format(t.lower()) - elif t == "true": - t = ": bool" - elif t.startswith("Vector"): - t = ": list" + if is_core: + return f"Optional[{type}] = None" if is_flag else type else: - return name + ("=None" if is_flag else "") - - return name + t + (" = None" if is_flag else "") - - -class Combinator: - def __init__(self, - section: str, - namespace: str, - name: str, - id: str, - args: list, - has_flags: bool, - return_type: str, - docs: str): - self.section = section - self.namespace = namespace - self.name = name - self.id = id - self.args = args - self.has_flags = has_flags - self.return_type = return_type - self.docs = docs - - -def snek(s: str): - # https://stackoverflow.com/questions/1175208/elegant-python-function-to-convert-camelcase-to-snake-case - s = re.sub(r"(.)([A-Z][a-z]+)", r"\1_\2", s) - return re.sub(r"([a-z0-9])([A-Z])", r"\1_\2", s).lower() - + ns, name = type.split(".") if "." in type else ("", type) + type = f'"raw.base.' + ".".join([ns, name]).strip(".") + '"' -def capit(s: str): - return "".join([i[0].upper() + i[1:] for i in s.split("_")]) + return f'{type}{" = None" if is_flag else ""}' def sort_args(args): @@ -163,99 +142,163 @@ def sort_args(args): for i in flags: args.remove(i) + for i in args[:]: + if re.match(r"flags\d?", i[0]) and i[1] == "#": + args.remove(i) + return args + flags -def start(): - shutil.rmtree("{}/types".format(DESTINATION), ignore_errors=True) - shutil.rmtree("{}/functions".format(DESTINATION), ignore_errors=True) +def remove_whitespaces(source: str) -> str: + """Remove whitespaces from blank lines""" + lines = source.split("\n") + + for i, _ in enumerate(lines): + if re.match(r"^\s+$", lines[i]): + lines[i] = "" + + return "\n".join(lines) + + +def get_docstring_arg_type(t: str): + if t in CORE_TYPES: + if t == "long": + return "``int`` ``64-bit``" + elif "int" in t: + size = INT_RE.match(t) + return f"``int`` ``{size.group(1)}-bit``" if size else "``int`` ``32-bit``" + elif t == "double": + return "``float`` ``64-bit``" + elif t == "string": + return "``str``" + elif t == "true": + return "``bool``" + else: + return f"``{t.lower()}``" + elif t == "TLObject" or t == "X": + return "Any object from :obj:`~pyrogram.raw.types`" + elif t == "!X": + return "Any function from :obj:`~pyrogram.raw.functions`" + elif t.lower().startswith("vector"): + return "List of " + get_docstring_arg_type(t.split("<", 1)[1][:-1]) + else: + return f":obj:`{t} `" + + +def get_references(t: str, kind: str): + if kind == "constructors": + t = constructors_to_functions.get(t) + elif kind == "types": + t = types_to_functions.get(t) + else: + raise ValueError("Invalid kind") + + if t: + return "\n ".join(t), len(t) + + return None, 0 + - with open("{}/source/auth_key.tl".format(HOME), encoding="utf-8") as auth, \ - open("{}/source/sys_msgs.tl".format(HOME), encoding="utf-8") as system, \ - open("{}/source/main_api.tl".format(HOME), encoding="utf-8") as api: - schema = (auth.read() + system.read() + api.read()).splitlines() +# noinspection PyShadowingBuiltins +def start(format: bool = False): + shutil.rmtree(DESTINATION_PATH / "types", ignore_errors=True) + shutil.rmtree(DESTINATION_PATH / "functions", ignore_errors=True) + shutil.rmtree(DESTINATION_PATH / "base", ignore_errors=True) - with open("{}/template/mtproto.txt".format(HOME), encoding="utf-8") as f: - mtproto_template = f.read() + with open(HOME_PATH / "source/auth_key.tl") as f1, \ + open(HOME_PATH / "source/sys_msgs.tl") as f2, \ + open(HOME_PATH / "source/main_api.tl") as f3: + schema = (f1.read() + f2.read() + f3.read()).splitlines() - with open("{}/template/pyrogram.txt".format(HOME), encoding="utf-8") as f: - pyrogram_template = f.read() + with open(HOME_PATH / "template/type.txt") as f1, \ + open(HOME_PATH / "template/combinator.txt") as f2: + type_tmpl = f1.read() + combinator_tmpl = f2.read() with open(NOTICE_PATH, encoding="utf-8") as f: notice = [] for line in f.readlines(): - notice.append("# {}".format(line).strip()) + notice.append(f"# {line}".strip()) notice = "\n".join(notice) section = None layer = None - namespaces = {"types": set(), "functions": set()} combinators = [] for line in schema: # Check for section changer lines - s = SECTION_RE.match(line) - if s: - section = s.group(1) + section_match = SECTION_RE.match(line) + if section_match: + section = section_match.group(1) continue # Save the layer version - l = LAYER_RE.match(line) - if l: - layer = l.group(1) + layer_match = LAYER_RE.match(line) + if layer_match: + layer = layer_match.group(1) continue - combinator = COMBINATOR_RE.match(line) - if combinator: - name, id, return_type, docs = combinator.groups() - namespace, name = name.split(".") if "." in name else ("", name) - args = ARGS_RE.findall(line.split(" //")[0]) + combinator_match = COMBINATOR_RE.match(line) + if combinator_match: + # noinspection PyShadowingBuiltins + qualname, id, qualtype = combinator_match.groups() + + namespace, name = qualname.split(".") if "." in qualname else ("", qualname) + name = camel(name) + qualname = ".".join([namespace, name]).lstrip(".") + + typespace, type = qualtype.split(".") if "." in qualtype else ("", qualtype) + type = camel(type) + qualtype = ".".join([typespace, type]).lstrip(".") # Pingu! has_flags = not not FLAGS_RE_3.findall(line) - # Fix file and folder name collision - if name == "updates": - name = "update" + args = ARGS_RE.findall(line) - # Fix arg name being "self" (reserved keyword) + # Fix arg name being "self" (reserved python keyword) for i, item in enumerate(args): if item[0] == "self": args[i] = ("is_self", item[1]) - if namespace: - namespaces[section].add(namespace) - - combinators.append( - Combinator( - section, - namespace, - capit(name), - "0x{}".format(id.zfill(8)), - args, - has_flags, - ".".join( - return_type.split(".")[:-1] - + [capit(return_type.split(".")[-1])] - ), - docs - ) + combinator = Combinator( + section=section, + qualname=qualname, + namespace=namespace, + name=name, + id=f"0x{id}", + has_flags=has_flags, + args=args, + qualtype=qualtype, + typespace=typespace, + type=type ) + combinators.append(combinator) + for c in combinators: - return_type = c.return_type + qualtype = c.qualtype - if return_type.startswith("Vector"): - return_type = return_type.split("<")[1][:-1] + if qualtype.startswith("Vector"): + qualtype = qualtype.split("<")[1][:-1] d = types_to_constructors if c.section == "types" else types_to_functions - if return_type not in d: - d[return_type] = [] + if qualtype not in d: + d[qualtype] = [] - d[return_type].append(".".join(filter(None, [c.namespace, c.name]))) + d[qualtype].append(c.qualname) + + if c.section == "types": + key = c.namespace + + if key not in namespaces_to_types: + namespaces_to_types[key] = [] + + if c.type not in namespaces_to_types[key]: + namespaces_to_types[key].append(c.type) for k, v in types_to_constructors.items(): for i in v: @@ -264,237 +307,346 @@ def start(): except KeyError: pass - total = len(combinators) - current = 0 - for c in combinators: # type: Combinator - print("Compiling APIs... [{}%] {}".format( - str(round(current * 100 / total)).rjust(3), - ".".join(filter(None, [c.section, c.namespace, c.name])) - ), end=" \r", flush=True) - current += 1 + # import json + # print(json.dumps(namespaces_to_types, indent=2)) + + for qualtype in types_to_constructors: + typespace, type = qualtype.split(".") if "." in qualtype else ("", qualtype) + dir_path = DESTINATION_PATH / "base" / typespace - path = "{}/{}/{}".format(DESTINATION, c.section, c.namespace) - os.makedirs(path, exist_ok=True) + module = type - init = "{}/__init__.py".format(path) + if module == "Updates": + module = "UpdatesT" - if not os.path.exists(init): - with open(init, "w", encoding="utf-8") as f: - f.write(notice + "\n\n") + os.makedirs(dir_path, exist_ok=True) - with open(init, "a", encoding="utf-8") as f: - f.write("from .{} import {}\n".format(snek(c.name), capit(c.name))) + constructors = sorted(types_to_constructors[qualtype]) + constr_count = len(constructors) + items = "\n ".join([f"{c}" for c in constructors]) + + type_docs = docs["type"].get(qualtype, None) + + if type_docs: + type_docs = type_docs["desc"] + else: + type_docs = "Telegram API base type." + + docstring = type_docs + + docstring += f"\n\n Constructors:\n" \ + f" This base type has {constr_count} constructor{'s' if constr_count > 1 else ''} available.\n\n" \ + f" .. currentmodule:: pyrogram.raw.types\n\n" \ + f" .. autosummary::\n" \ + f" :nosignatures:\n\n" \ + f" {items}" + + references, ref_count = get_references(qualtype, "types") + + if references: + docstring += f"\n\n Functions:\n This object can be returned by " \ + f"{ref_count} function{'s' if ref_count > 1 else ''}.\n\n" \ + f" .. currentmodule:: pyrogram.raw.functions\n\n" \ + f" .. autosummary::\n" \ + f" :nosignatures:\n\n" \ + f" " + references + + with open(dir_path / f"{snake(module)}.py", "w") as f: + f.write( + type_tmpl.format( + notice=notice, + warning=WARNING, + docstring=docstring, + name=type, + qualname=qualtype, + types=", ".join([f"raw.types.{c}" for c in constructors]), + doc_name=snake(type).replace("_", "-") + ) + ) + for c in combinators: sorted_args = sort_args(c.args) arguments = ( - ", " - + ("*, " if c.args else "") - + (", ".join([get_argument_type(i) for i in sorted_args if i != ("flags", "#")]) if c.args else "") + (", *, " if c.args else "") + + (", ".join( + [f"{i[0]}: {get_type_hint(i[1])}" + for i in sorted_args] + ) if sorted_args else "") ) fields = "\n ".join( - ["self.{0} = {0} # {1}".format(i[0], i[1]) for i in c.args if i != ("flags", "#")] - ) if c.args else "pass" + [f"self.{i[0]} = {i[0]} # {i[1]}" + for i in sorted_args] + ) if sorted_args else "pass" + docstring = "" docstring_args = [] - docs = c.docs.split("|")[1:] if c.docs else None - for i, arg in enumerate(sorted_args): - if arg == ("flags", "#"): - continue + if c.section == "functions": + combinator_docs = docs["method"] + else: + combinator_docs = docs["constructor"] + for i, arg in enumerate(sorted_args): arg_name, arg_type = arg is_optional = FLAGS_RE.match(arg_type) flag_number = is_optional.group(1) if is_optional else -1 arg_type = arg_type.split("?")[-1] - if docs: - docstring_args.append( - "{} ({}{}):\n {}\n".format( - arg_name, - get_docstring_arg_type(arg_type, is_pyrogram_type=True), - ", optional" if "Optional" in docs[i] else "", - re.sub("Optional\. ", "", docs[i].split("§")[1].rstrip(".") + ".") - ) - ) + arg_docs = combinator_docs.get(c.qualname, None) + + if arg_docs: + arg_docs = arg_docs["params"].get(arg_name, "N/A") else: - docstring_args.append( - "{}{}: {}".format( - arg_name, - " (optional)".format(flag_number) if is_optional else "", - get_docstring_arg_type(arg_type, is_pyrogram_type=c.namespace == "pyrogram") - ) + arg_docs = "N/A" + + docstring_args.append( + "{} ({}{}):\n {}\n".format( + arg_name, + get_docstring_arg_type(arg_type), + ", *optional*".format(flag_number) if is_optional else "", + arg_docs ) + ) - if docstring_args: - docstring_args = "Parameters:\n " + "\n ".join(docstring_args) + if c.section == "types": + constructor_docs = docs["constructor"].get(c.qualname, None) + + if constructor_docs: + constructor_docs = constructor_docs["desc"] + else: + constructor_docs = "Telegram API type." + + docstring += constructor_docs + "\n" + docstring += f"\n Constructor of :obj:`~pyrogram.raw.base.{c.qualtype}`." else: - docstring_args = "No parameters required." + function_docs = docs["method"].get(c.qualname, None) + + if function_docs: + docstring += function_docs["desc"] + "\n" + else: + docstring += f"Telegram API function." - docstring_args = "Attributes:\n ID: ``{}``\n\n ".format(c.id) + docstring_args + docstring += f"\n\n Details:\n - Layer: ``{layer}``\n - ID: ``{c.id[2:].upper()}``\n\n" + docstring += f" Parameters:\n " + \ + (f"\n ".join(docstring_args) if docstring_args else "No parameters required.\n") if c.section == "functions": - docstring_args += "\n\n Returns:\n " + get_docstring_arg_type(c.return_type) - docstring_args += "\n\n Raises:\n RPCError: In case of a Telegram RPC error." - + docstring += "\n Returns:\n " + get_docstring_arg_type(c.qualtype) else: - references = get_references(".".join(filter(None, [c.namespace, c.name]))) + references, count = get_references(c.qualname, "constructors") if references: - docstring_args += "\n\n See Also:\n This object can be returned by " + references + "." + docstring += f"\n Functions:\n This object can be returned by " \ + f"{count} function{'s' if count > 1 else ''}.\n\n" \ + f" .. currentmodule:: pyrogram.raw.functions\n\n" \ + f" .. autosummary::\n" \ + f" :nosignatures:\n\n" \ + f" " + references write_types = read_types = "" if c.has_flags else "# No flags\n " for arg_name, arg_type in c.args: - flag = FLAGS_RE_2.findall(arg_type) + flag = FLAGS_RE_2.match(arg_type) - if arg_name == "flags" and arg_type == "#": + if re.match(r"flags\d?", arg_name) and arg_type == "#": write_flags = [] for i in c.args: - flag = FLAGS_RE.match(i[1]) + flag = FLAGS_RE_2.match(i[1]) + if flag: - write_flags.append( - "flags |= (1 << {}) if self.{} is not None else 0".format(flag.group(1), i[0])) + if arg_name != f"flags{flag.group(1)}": + continue + + if flag.group(3) == "true" or flag.group(3).startswith("Vector"): + write_flags.append(f"{arg_name} |= (1 << {flag.group(2)}) if self.{i[0]} else 0") + else: + write_flags.append( + f"{arg_name} |= (1 << {flag.group(2)}) if self.{i[0]} is not None else 0") write_flags = "\n ".join([ - "flags = 0", + f"{arg_name} = 0", "\n ".join(write_flags), - "b.write(Int(flags))\n " + f"b.write(Int({arg_name}))\n " ]) write_types += write_flags - read_types += "flags = Int.read(b)\n " + read_types += f"\n {arg_name} = Int.read(b)\n " continue if flag: - index, flag_type = flag[0] + number, index, flag_type = flag.groups() if flag_type == "true": read_types += "\n " - read_types += "{} = True if flags & (1 << {}) else False".format(arg_name, index) - elif flag_type in core_types: + read_types += f"{arg_name} = True if flags{number} & (1 << {index}) else False" + elif flag_type in CORE_TYPES: write_types += "\n " - write_types += "if self.{} is not None:\n ".format(arg_name) - write_types += "b.write({}(self.{}))\n ".format(flag_type.title(), arg_name) + write_types += f"if self.{arg_name} is not None:\n " + write_types += f"b.write({flag_type.title()}(self.{arg_name}))\n " read_types += "\n " - read_types += "{} = {}.read(b) if flags & (1 << {}) else None".format( - arg_name, flag_type.title(), index - ) + read_types += f"{arg_name} = {flag_type.title()}.read(b) if flags{number} & (1 << {index}) else None" elif "vector" in flag_type.lower(): sub_type = arg_type.split("<")[1][:-1] write_types += "\n " - write_types += "if self.{} is not None:\n ".format(arg_name) + write_types += f"if self.{arg_name} is not None:\n " write_types += "b.write(Vector(self.{}{}))\n ".format( - arg_name, ", {}".format(sub_type.title()) if sub_type in core_types else "" + arg_name, f", {sub_type.title()}" if sub_type in CORE_TYPES else "" ) read_types += "\n " - read_types += "{} = Object.read(b{}) if flags & (1 << {}) else []\n ".format( - arg_name, ", {}".format(sub_type.title()) if sub_type in core_types else "", index + read_types += "{} = TLObject.read(b{}) if flags{} & (1 << {}) else []\n ".format( + arg_name, f", {sub_type.title()}" if sub_type in CORE_TYPES else "", number, index ) else: write_types += "\n " - write_types += "if self.{} is not None:\n ".format(arg_name) - write_types += "b.write(self.{}.write())\n ".format(arg_name) + write_types += f"if self.{arg_name} is not None:\n " + write_types += f"b.write(self.{arg_name}.write())\n " read_types += "\n " - read_types += "{} = Object.read(b) if flags & (1 << {}) else None\n ".format( - arg_name, index - ) + read_types += f"{arg_name} = TLObject.read(b) if flags{number} & (1 << {index}) else None\n " else: - if arg_type in core_types: + if arg_type in CORE_TYPES: write_types += "\n " - write_types += "b.write({}(self.{}))\n ".format(arg_type.title(), arg_name) + write_types += f"b.write({arg_type.title()}(self.{arg_name}))\n " read_types += "\n " - read_types += "{} = {}.read(b)\n ".format(arg_name, arg_type.title()) + read_types += f"{arg_name} = {arg_type.title()}.read(b)\n " elif "vector" in arg_type.lower(): sub_type = arg_type.split("<")[1][:-1] write_types += "\n " write_types += "b.write(Vector(self.{}{}))\n ".format( - arg_name, ", {}".format(sub_type.title()) if sub_type in core_types else "" + arg_name, f", {sub_type.title()}" if sub_type in CORE_TYPES else "" ) read_types += "\n " - read_types += "{} = Object.read(b{})\n ".format( - arg_name, ", {}".format(sub_type.title()) if sub_type in core_types else "" + read_types += "{} = TLObject.read(b{})\n ".format( + arg_name, f", {sub_type.title()}" if sub_type in CORE_TYPES else "" ) else: write_types += "\n " - write_types += "b.write(self.{}.write())\n ".format(arg_name) + write_types += f"b.write(self.{arg_name}.write())\n " read_types += "\n " - read_types += "{} = Object.read(b)\n ".format(arg_name) - - if c.docs: - description = c.docs.split("|")[0].split("§")[1] - docstring_args = description + "\n\n " + docstring_args - - with open("{}/{}.py".format(path, snek(c.name)), "w", encoding="utf-8") as f: - if c.docs: - f.write( - pyrogram_template.format( - notice=notice, - class_name=capit(c.name), - docstring_args=docstring_args, - object_id=c.id, - arguments=arguments, - fields=fields - ) - ) - else: - f.write( - mtproto_template.format( - notice=notice, - class_name=capit(c.name), - docstring_args=docstring_args, - object_id=c.id, - arguments=arguments, - fields=fields, - read_types=read_types, - write_types=write_types, - return_arguments=", ".join( - ["{0}={0}".format(i[0]) for i in sorted_args if i != ("flags", "#")] - ), - slots=", ".join(['"{}"'.format(i[0]) for i in sorted_args if i != ("flags", "#")]), - qualname="{}.{}{}".format(c.section, "{}.".format(c.namespace) if c.namespace else "", c.name) - ) - ) + read_types += f"{arg_name} = TLObject.read(b)\n " + + slots = ", ".join([f'"{i[0]}"' for i in sorted_args]) + return_arguments = ", ".join([f"{i[0]}={i[0]}" for i in sorted_args]) + + compiled_combinator = combinator_tmpl.format( + notice=notice, + warning=WARNING, + name=c.name, + docstring=docstring, + slots=slots, + id=c.id, + qualname=f"{c.section}.{c.qualname}", + arguments=arguments, + fields=fields, + read_types=read_types, + write_types=write_types, + return_arguments=return_arguments + ) + + directory = "types" if c.section == "types" else c.section + + dir_path = DESTINATION_PATH / directory / c.namespace + + os.makedirs(dir_path, exist_ok=True) + + module = c.name + + if module == "Updates": + module = "UpdatesT" + + with open(dir_path / f"{snake(module)}.py", "w") as f: + f.write(compiled_combinator) + + d = namespaces_to_constructors if c.section == "types" else namespaces_to_functions + + if c.namespace not in d: + d[c.namespace] = [] + + d[c.namespace].append(c.name) + + for namespace, types in namespaces_to_types.items(): + with open(DESTINATION_PATH / "base" / namespace / "__init__.py", "w") as f: + f.write(f"{notice}\n\n") + f.write(f"{WARNING}\n\n") + + for t in types: + module = t + + if module == "Updates": + module = "UpdatesT" - with open("{}/all.py".format(DESTINATION), "w", encoding="utf-8") as f: + f.write(f"from .{snake(module)} import {t}\n") + + if not namespace: + f.write(f"from . import {', '.join(filter(bool, namespaces_to_types))}") + + for namespace, types in namespaces_to_constructors.items(): + with open(DESTINATION_PATH / "types" / namespace / "__init__.py", "w") as f: + f.write(f"{notice}\n\n") + f.write(f"{WARNING}\n\n") + + for t in types: + module = t + + if module == "Updates": + module = "UpdatesT" + + f.write(f"from .{snake(module)} import {t}\n") + + if not namespace: + f.write(f"from . import {', '.join(filter(bool, namespaces_to_constructors))}\n") + + for namespace, types in namespaces_to_functions.items(): + with open(DESTINATION_PATH / "functions" / namespace / "__init__.py", "w") as f: + f.write(f"{notice}\n\n") + f.write(f"{WARNING}\n\n") + + for t in types: + module = t + + if module == "Updates": + module = "UpdatesT" + + f.write(f"from .{snake(module)} import {t}\n") + + if not namespace: + f.write(f"from . import {', '.join(filter(bool, namespaces_to_functions))}") + + with open(DESTINATION_PATH / "all.py", "w", encoding="utf-8") as f: f.write(notice + "\n\n") - f.write("layer = {}\n\n".format(layer)) + f.write(WARNING + "\n\n") + f.write(f"layer = {layer}\n\n") f.write("objects = {") for c in combinators: - path = ".".join(filter(None, [c.section, c.namespace, capit(c.name)])) - f.write("\n {}: \"pyrogram.api.{}\",".format(c.id, path)) - - f.write("\n 0xbc799737: \"pyrogram.api.core.BoolFalse\",") - f.write("\n 0x997275b5: \"pyrogram.api.core.BoolTrue\",") - f.write("\n 0x56730bcc: \"pyrogram.api.core.Null\",") - f.write("\n 0x1cb5c415: \"pyrogram.api.core.Vector\",") - f.write("\n 0x73f1f8dc: \"pyrogram.api.core.MsgContainer\",") - f.write("\n 0xae500895: \"pyrogram.api.core.FutureSalts\",") - f.write("\n 0x0949d9dc: \"pyrogram.api.core.FutureSalt\",") - f.write("\n 0x3072cfa1: \"pyrogram.api.core.GzipPacked\",") - f.write("\n 0x5bb8e511: \"pyrogram.api.core.Message\",") + f.write(f'\n {c.id}: "pyrogram.raw.{c.section}.{c.qualname}",') - f.write("\n}\n") + f.write('\n 0xbc799737: "pyrogram.raw.core.BoolFalse",') + f.write('\n 0x997275b5: "pyrogram.raw.core.BoolTrue",') + f.write('\n 0x1cb5c415: "pyrogram.raw.core.Vector",') + f.write('\n 0x73f1f8dc: "pyrogram.raw.core.MsgContainer",') + f.write('\n 0xae500895: "pyrogram.raw.core.FutureSalts",') + f.write('\n 0x0949d9dc: "pyrogram.raw.core.FutureSalt",') + f.write('\n 0x3072cfa1: "pyrogram.raw.core.GzipPacked",') + f.write('\n 0x5bb8e511: "pyrogram.raw.core.Message",') - for k, v in namespaces.items(): - with open("{}/{}/__init__.py".format(DESTINATION, k), "a", encoding="utf-8") as f: - f.write("from . import {}\n".format(", ".join([i for i in v])) if v else "") + f.write("\n}\n") if "__main__" == __name__: - HOME = "." - DESTINATION = "../../pyrogram/api" - NOTICE_PATH = "../../NOTICE" - start() + HOME_PATH = Path(".") + DESTINATION_PATH = Path("../../pyrogram/raw") + NOTICE_PATH = Path("../../NOTICE") + + start(format=False) diff --git a/compiler/api/source/main_api.tl b/compiler/api/source/main_api.tl index 61c9faf6459..7fd74edb3d7 100644 --- a/compiler/api/source/main_api.tl +++ b/compiler/api/source/main_api.tl @@ -1,4 +1,4 @@ -// https://raw.githubusercontent.com/telegramdesktop/tdesktop/dev/Telegram/Resources/scheme.tl +// https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/Resources/tl/api.tl /////////////////////////////// ///////// Main application API @@ -19,13 +19,16 @@ inputPeerEmpty#7f3b18ea = InputPeer; inputPeerSelf#7da07ec9 = InputPeer; -inputPeerChat#179be863 chat_id:int = InputPeer; -inputPeerUser#7b8e7de6 user_id:int access_hash:long = InputPeer; -inputPeerChannel#20adaef8 channel_id:int access_hash:long = InputPeer; +inputPeerChat#35a95cb9 chat_id:long = InputPeer; +inputPeerUser#dde8a54c user_id:long access_hash:long = InputPeer; +inputPeerChannel#27bcbbfc channel_id:long access_hash:long = InputPeer; +inputPeerUserFromMessage#a87b0a1c peer:InputPeer msg_id:int user_id:long = InputPeer; +inputPeerChannelFromMessage#bd2a0840 peer:InputPeer msg_id:int channel_id:long = InputPeer; inputUserEmpty#b98886cf = InputUser; inputUserSelf#f7c1b13f = InputUser; -inputUser#d8292816 user_id:int access_hash:long = InputUser; +inputUser#f21158c6 user_id:long access_hash:long = InputUser; +inputUserFromMessage#1da448e2 peer:InputPeer msg_id:int user_id:long = InputUser; inputPhoneContact#f392b7f4 client_id:long phone:string first_name:string last_name:string = InputContact; @@ -33,40 +36,45 @@ inputFile#f52ff27f id:long parts:int name:string md5_checksum:string = InputFile inputFileBig#fa4f0bb5 id:long parts:int name:string = InputFile; inputMediaEmpty#9664f57f = InputMedia; -inputMediaUploadedPhoto#1e287d04 flags:# file:InputFile stickers:flags.0?Vector ttl_seconds:flags.1?int = InputMedia; -inputMediaPhoto#b3ba0635 flags:# id:InputPhoto ttl_seconds:flags.0?int = InputMedia; +inputMediaUploadedPhoto#1e287d04 flags:# spoiler:flags.2?true file:InputFile stickers:flags.0?Vector ttl_seconds:flags.1?int = InputMedia; +inputMediaPhoto#b3ba0635 flags:# spoiler:flags.1?true id:InputPhoto ttl_seconds:flags.0?int = InputMedia; inputMediaGeoPoint#f9c44144 geo_point:InputGeoPoint = InputMedia; inputMediaContact#f8ab7dfb phone_number:string first_name:string last_name:string vcard:string = InputMedia; -inputMediaUploadedDocument#5b38c6c1 flags:# nosound_video:flags.3?true file:InputFile thumb:flags.2?InputFile mime_type:string attributes:Vector stickers:flags.0?Vector ttl_seconds:flags.1?int = InputMedia; -inputMediaDocument#23ab23d2 flags:# id:InputDocument ttl_seconds:flags.0?int = InputMedia; +inputMediaUploadedDocument#5b38c6c1 flags:# nosound_video:flags.3?true force_file:flags.4?true spoiler:flags.5?true file:InputFile thumb:flags.2?InputFile mime_type:string attributes:Vector stickers:flags.0?Vector ttl_seconds:flags.1?int = InputMedia; +inputMediaDocument#33473058 flags:# spoiler:flags.2?true id:InputDocument ttl_seconds:flags.0?int query:flags.1?string = InputMedia; inputMediaVenue#c13d1c11 geo_point:InputGeoPoint title:string address:string provider:string venue_id:string venue_type:string = InputMedia; -inputMediaGifExternal#4843b0fd url:string q:string = InputMedia; -inputMediaPhotoExternal#e5bbfe1a flags:# url:string ttl_seconds:flags.0?int = InputMedia; -inputMediaDocumentExternal#fb52dc99 flags:# url:string ttl_seconds:flags.0?int = InputMedia; +inputMediaPhotoExternal#e5bbfe1a flags:# spoiler:flags.1?true url:string ttl_seconds:flags.0?int = InputMedia; +inputMediaDocumentExternal#fb52dc99 flags:# spoiler:flags.1?true url:string ttl_seconds:flags.0?int = InputMedia; inputMediaGame#d33f43f3 id:InputGame = InputMedia; -inputMediaInvoice#f4e096c3 flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string provider_data:DataJSON start_param:string = InputMedia; -inputMediaGeoLive#ce4e82fd flags:# stopped:flags.0?true geo_point:InputGeoPoint period:flags.1?int = InputMedia; -inputMediaPoll#6b3765b poll:Poll = InputMedia; +inputMediaInvoice#8eb5a6d5 flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string provider_data:DataJSON start_param:flags.1?string extended_media:flags.2?InputMedia = InputMedia; +inputMediaGeoLive#971fa843 flags:# stopped:flags.0?true geo_point:InputGeoPoint heading:flags.2?int period:flags.1?int proximity_notification_radius:flags.3?int = InputMedia; +inputMediaPoll#f94e5f1 flags:# poll:Poll correct_answers:flags.0?Vector solution:flags.1?string solution_entities:flags.1?Vector = InputMedia; +inputMediaDice#e66fbf7b emoticon:string = InputMedia; inputChatPhotoEmpty#1ca48f57 = InputChatPhoto; -inputChatUploadedPhoto#927c55b4 file:InputFile = InputChatPhoto; +inputChatUploadedPhoto#bdcdaec0 flags:# file:flags.0?InputFile video:flags.1?InputFile video_start_ts:flags.2?double video_emoji_markup:flags.3?VideoSize = InputChatPhoto; inputChatPhoto#8953ad37 id:InputPhoto = InputChatPhoto; inputGeoPointEmpty#e4c123d6 = InputGeoPoint; -inputGeoPoint#f3b7acc9 lat:double long:double = InputGeoPoint; +inputGeoPoint#48222faf flags:# lat:double long:double accuracy_radius:flags.0?int = InputGeoPoint; inputPhotoEmpty#1cd7bf0d = InputPhoto; inputPhoto#3bb3b94a id:long access_hash:long file_reference:bytes = InputPhoto; inputFileLocation#dfdaabe1 volume_id:long local_id:int secret:long file_reference:bytes = InputFileLocation; inputEncryptedFileLocation#f5235d55 id:long access_hash:long = InputFileLocation; -inputDocumentFileLocation#196683d9 id:long access_hash:long file_reference:bytes = InputFileLocation; +inputDocumentFileLocation#bad07584 id:long access_hash:long file_reference:bytes thumb_size:string = InputFileLocation; inputSecureFileLocation#cbc7ee28 id:long access_hash:long = InputFileLocation; inputTakeoutFileLocation#29be5899 = InputFileLocation; +inputPhotoFileLocation#40181ffe id:long access_hash:long file_reference:bytes thumb_size:string = InputFileLocation; +inputPhotoLegacyFileLocation#d83466f3 id:long access_hash:long file_reference:bytes volume_id:long local_id:int secret:long = InputFileLocation; +inputPeerPhotoFileLocation#37257e99 flags:# big:flags.0?true peer:InputPeer photo_id:long = InputFileLocation; +inputStickerSetThumb#9d84f3db stickerset:InputStickerSet thumb_version:int = InputFileLocation; +inputGroupCallStream#598a92a flags:# call:InputGroupCall time_ms:long scale:int video_channel:flags.0?int video_quality:flags.0?int = InputFileLocation; -peerUser#9db1bc6d user_id:int = Peer; -peerChat#bad0e5bb chat_id:int = Peer; -peerChannel#bddde532 channel_id:int = Peer; +peerUser#59511722 user_id:long = Peer; +peerChat#36c6019a chat_id:long = Peer; +peerChannel#a2a5371e channel_id:long = Peer; storage.fileUnknown#aa963b05 = storage.FileType; storage.filePartial#40bc6f52 = storage.FileType; @@ -79,14 +87,11 @@ storage.fileMov#4b09ebbc = storage.FileType; storage.fileMp4#b3cea0e4 = storage.FileType; storage.fileWebp#1081464c = storage.FileType; -fileLocationUnavailable#7c596b46 volume_id:long local_id:int secret:long = FileLocation; -fileLocation#91d11eb dc_id:int volume_id:long local_id:int secret:long file_reference:bytes = FileLocation; - -userEmpty#200250ba id:int = User; -user#2e13f4c3 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?string bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User; +userEmpty#d3bc4b7a id:long = User; +user#8f97c628 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true fake:flags.26?true bot_attach_menu:flags.27?true premium:flags.28?true attach_menu_enabled:flags.29?true flags2:# bot_can_edit:flags2.1?true id:long access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector bot_inline_placeholder:flags.19?string lang_code:flags.22?string emoji_status:flags.30?EmojiStatus usernames:flags2.0?Vector = User; userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto; -userProfilePhoto#d559d8c8 photo_id:long photo_small:FileLocation photo_big:FileLocation = UserProfilePhoto; +userProfilePhoto#82d1f706 flags:# has_video:flags.0?true personal:flags.2?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = UserProfilePhoto; userStatusEmpty#9d05049 = UserStatus; userStatusOnline#edb93949 expires:int = UserStatus; @@ -95,134 +100,156 @@ userStatusRecently#e26f42f1 = UserStatus; userStatusLastWeek#7bf09fc = UserStatus; userStatusLastMonth#77ebc742 = UserStatus; -chatEmpty#9ba2d800 id:int = Chat; -chat#3bda1bde flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true deactivated:flags.5?true id:int title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat; -chatForbidden#7328bdb id:int title:string = Chat; -channel#4df30834 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?string admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat; -channelForbidden#289da732 flags:# broadcast:flags.5?true megagroup:flags.8?true id:int access_hash:long title:string until_date:flags.16?int = Chat; +chatEmpty#29562865 id:long = Chat; +chat#41cbf256 flags:# creator:flags.0?true left:flags.2?true deactivated:flags.5?true call_active:flags.23?true call_not_empty:flags.24?true noforwards:flags.25?true id:long title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat; +chatForbidden#6592a1a7 id:long title:string = Chat; +channel#83259464 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true noforwards:flags.27?true join_to_send:flags.28?true join_request:flags.29?true forum:flags.30?true flags2:# id:long access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int restriction_reason:flags.9?Vector admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int usernames:flags2.0?Vector = Chat; +channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true id:long access_hash:long title:string until_date:flags.16?int = Chat; -chatFull#22a235da flags:# can_set_username:flags.7?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:flags.3?Vector pinned_msg_id:flags.6?int = ChatFull; -channelFull#1c87a71a flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_view_stats:flags.12?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int = ChatFull; +chatFull#c9d31138 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true translations_disabled:flags.19?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector available_reactions:flags.18?ChatReactions = ChatFull; +channelFull#f2355507 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true flags2:# can_delete_channel:flags2.0?true antispam:flags2.1?true participants_hidden:flags2.2?true translations_disabled:flags2.3?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector default_send_as:flags.29?Peer available_reactions:flags.30?ChatReactions = ChatFull; -chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant; -chatParticipantCreator#da13538a user_id:int = ChatParticipant; -chatParticipantAdmin#e2d6e436 user_id:int inviter_id:int date:int = ChatParticipant; +chatParticipant#c02d4007 user_id:long inviter_id:long date:int = ChatParticipant; +chatParticipantCreator#e46bcee4 user_id:long = ChatParticipant; +chatParticipantAdmin#a0933f5b user_id:long inviter_id:long date:int = ChatParticipant; -chatParticipantsForbidden#fc900c2b flags:# chat_id:int self_participant:flags.0?ChatParticipant = ChatParticipants; -chatParticipants#3f460fed chat_id:int participants:Vector version:int = ChatParticipants; +chatParticipantsForbidden#8763d3e1 flags:# chat_id:long self_participant:flags.0?ChatParticipant = ChatParticipants; +chatParticipants#3cbc93f8 chat_id:long participants:Vector version:int = ChatParticipants; chatPhotoEmpty#37c1011c = ChatPhoto; -chatPhoto#6153276a photo_small:FileLocation photo_big:FileLocation = ChatPhoto; +chatPhoto#1c6e1c11 flags:# has_video:flags.0?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = ChatPhoto; -messageEmpty#83e5de54 id:int = Message; -message#44f9b43d flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true id:int from_id:flags.8?int to_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector views:flags.10?int edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long = Message; -messageService#9e19a1f6 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true id:int from_id:flags.8?int to_id:Peer reply_to_msg_id:flags.3?int date:int action:MessageAction = Message; +messageEmpty#90a6ca84 flags:# id:int peer_id:flags.0?Peer = Message; +message#38116ee0 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true noforwards:flags.26?true id:int from_id:flags.8?Peer peer_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long reactions:flags.20?MessageReactions restriction_reason:flags.22?Vector ttl_period:flags.25?int = Message; +messageService#2b085862 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction ttl_period:flags.25?int = Message; messageMediaEmpty#3ded6320 = MessageMedia; -messageMediaPhoto#695150d7 flags:# photo:flags.0?Photo ttl_seconds:flags.2?int = MessageMedia; +messageMediaPhoto#695150d7 flags:# spoiler:flags.3?true photo:flags.0?Photo ttl_seconds:flags.2?int = MessageMedia; messageMediaGeo#56e0d474 geo:GeoPoint = MessageMedia; -messageMediaContact#cbf24940 phone_number:string first_name:string last_name:string vcard:string user_id:int = MessageMedia; +messageMediaContact#70322949 phone_number:string first_name:string last_name:string vcard:string user_id:long = MessageMedia; messageMediaUnsupported#9f84f49e = MessageMedia; -messageMediaDocument#9cb070d7 flags:# document:flags.0?Document ttl_seconds:flags.2?int = MessageMedia; +messageMediaDocument#9cb070d7 flags:# nopremium:flags.3?true spoiler:flags.4?true document:flags.0?Document ttl_seconds:flags.2?int = MessageMedia; messageMediaWebPage#a32dd600 webpage:WebPage = MessageMedia; messageMediaVenue#2ec0533f geo:GeoPoint title:string address:string provider:string venue_id:string venue_type:string = MessageMedia; messageMediaGame#fdb19008 game:Game = MessageMedia; -messageMediaInvoice#84551347 flags:# shipping_address_requested:flags.1?true test:flags.3?true title:string description:string photo:flags.0?WebDocument receipt_msg_id:flags.2?int currency:string total_amount:long start_param:string = MessageMedia; -messageMediaGeoLive#7c3c2609 geo:GeoPoint period:int = MessageMedia; +messageMediaInvoice#f6a548d3 flags:# shipping_address_requested:flags.1?true test:flags.3?true title:string description:string photo:flags.0?WebDocument receipt_msg_id:flags.2?int currency:string total_amount:long start_param:string extended_media:flags.4?MessageExtendedMedia = MessageMedia; +messageMediaGeoLive#b940c666 flags:# geo:GeoPoint heading:flags.0?int period:int proximity_notification_radius:flags.1?int = MessageMedia; messageMediaPoll#4bd6e798 poll:Poll results:PollResults = MessageMedia; +messageMediaDice#3f7ee58b value:int emoticon:string = MessageMedia; messageActionEmpty#b6aef7b0 = MessageAction; -messageActionChatCreate#a6638b9a title:string users:Vector = MessageAction; +messageActionChatCreate#bd47cbad title:string users:Vector = MessageAction; messageActionChatEditTitle#b5a1ce5a title:string = MessageAction; messageActionChatEditPhoto#7fcb13a8 photo:Photo = MessageAction; messageActionChatDeletePhoto#95e3fbef = MessageAction; -messageActionChatAddUser#488a7337 users:Vector = MessageAction; -messageActionChatDeleteUser#b2ae9b0c user_id:int = MessageAction; -messageActionChatJoinedByLink#f89cf5e8 inviter_id:int = MessageAction; +messageActionChatAddUser#15cefd00 users:Vector = MessageAction; +messageActionChatDeleteUser#a43f30cc user_id:long = MessageAction; +messageActionChatJoinedByLink#31224c3 inviter_id:long = MessageAction; messageActionChannelCreate#95d2ac92 title:string = MessageAction; -messageActionChatMigrateTo#51bdb021 channel_id:int = MessageAction; -messageActionChannelMigrateFrom#b055eaee title:string chat_id:int = MessageAction; +messageActionChatMigrateTo#e1037f92 channel_id:long = MessageAction; +messageActionChannelMigrateFrom#ea3948e9 title:string chat_id:long = MessageAction; messageActionPinMessage#94bd38ed = MessageAction; messageActionHistoryClear#9fbab604 = MessageAction; messageActionGameScore#92a72876 game_id:long score:int = MessageAction; -messageActionPaymentSentMe#8f31b327 flags:# currency:string total_amount:long payload:bytes info:flags.0?PaymentRequestedInfo shipping_option_id:flags.1?string charge:PaymentCharge = MessageAction; -messageActionPaymentSent#40699cd0 currency:string total_amount:long = MessageAction; -messageActionPhoneCall#80e11a7f flags:# call_id:long reason:flags.0?PhoneCallDiscardReason duration:flags.1?int = MessageAction; +messageActionPaymentSentMe#8f31b327 flags:# recurring_init:flags.2?true recurring_used:flags.3?true currency:string total_amount:long payload:bytes info:flags.0?PaymentRequestedInfo shipping_option_id:flags.1?string charge:PaymentCharge = MessageAction; +messageActionPaymentSent#96163f56 flags:# recurring_init:flags.2?true recurring_used:flags.3?true currency:string total_amount:long invoice_slug:flags.0?string = MessageAction; +messageActionPhoneCall#80e11a7f flags:# video:flags.2?true call_id:long reason:flags.0?PhoneCallDiscardReason duration:flags.1?int = MessageAction; messageActionScreenshotTaken#4792929b = MessageAction; messageActionCustomAction#fae69f56 message:string = MessageAction; -messageActionBotAllowed#abe9affe domain:string = MessageAction; +messageActionBotAllowed#c516d679 flags:# attach_menu:flags.1?true domain:flags.0?string app:flags.2?BotApp = MessageAction; messageActionSecureValuesSentMe#1b287353 values:Vector credentials:SecureCredentialsEncrypted = MessageAction; messageActionSecureValuesSent#d95c6154 types:Vector = MessageAction; messageActionContactSignUp#f3f25f76 = MessageAction; - -dialog#e4def5db flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage = Dialog; +messageActionGeoProximityReached#98e0d697 from_id:Peer to_id:Peer distance:int = MessageAction; +messageActionGroupCall#7a0d7f42 flags:# call:InputGroupCall duration:flags.0?int = MessageAction; +messageActionInviteToGroupCall#502f92f7 call:InputGroupCall users:Vector = MessageAction; +messageActionSetMessagesTTL#3c134d7b flags:# period:int auto_setting_from:flags.0?long = MessageAction; +messageActionGroupCallScheduled#b3a07661 call:InputGroupCall schedule_date:int = MessageAction; +messageActionSetChatTheme#aa786345 emoticon:string = MessageAction; +messageActionChatJoinedByRequest#ebbca3cb = MessageAction; +messageActionWebViewDataSentMe#47dd8079 text:string data:string = MessageAction; +messageActionWebViewDataSent#b4c38cb5 text:string = MessageAction; +messageActionGiftPremium#c83d6aec flags:# currency:string amount:long months:int crypto_currency:flags.0?string crypto_amount:flags.0?long = MessageAction; +messageActionTopicCreate#d999256 flags:# title:string icon_color:int icon_emoji_id:flags.0?long = MessageAction; +messageActionTopicEdit#c0944820 flags:# title:flags.0?string icon_emoji_id:flags.1?long closed:flags.2?Bool hidden:flags.3?Bool = MessageAction; +messageActionSuggestProfilePhoto#57de635e photo:Photo = MessageAction; +messageActionRequestedPeer#fe77345d button_id:int peer:Peer = MessageAction; +messageActionSetChatWallPaper#bc44a927 wallpaper:WallPaper = MessageAction; +messageActionSetSameChatWallPaper#c0787d6d wallpaper:WallPaper = MessageAction; + +dialog#d58a08c6 flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int ttl_period:flags.5?int = Dialog; +dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog; photoEmpty#2331b22d id:long = Photo; -photo#9c477dd8 flags:# has_stickers:flags.0?true id:long access_hash:long file_reference:bytes date:int sizes:Vector = Photo; +photo#fb197a65 flags:# has_stickers:flags.0?true id:long access_hash:long file_reference:bytes date:int sizes:Vector video_sizes:flags.1?Vector dc_id:int = Photo; photoSizeEmpty#e17e23c type:string = PhotoSize; -photoSize#77bfb61b type:string location:FileLocation w:int h:int size:int = PhotoSize; -photoCachedSize#e9a734fa type:string location:FileLocation w:int h:int bytes:bytes = PhotoSize; +photoSize#75c78e60 type:string w:int h:int size:int = PhotoSize; +photoCachedSize#21e1ad6 type:string w:int h:int bytes:bytes = PhotoSize; photoStrippedSize#e0b0bc2e type:string bytes:bytes = PhotoSize; +photoSizeProgressive#fa3efb95 type:string w:int h:int sizes:Vector = PhotoSize; +photoPathSize#d8214d41 type:string bytes:bytes = PhotoSize; geoPointEmpty#1117dd5f = GeoPoint; -geoPoint#296f104 long:double lat:double access_hash:long = GeoPoint; - -auth.checkedPhone#811ea28e phone_registered:Bool = auth.CheckedPhone; +geoPoint#b2a2f663 flags:# long:double lat:double access_hash:long accuracy_radius:flags.0?int = GeoPoint; -auth.sentCode#38faab5f flags:# phone_registered:flags.0?true type:auth.SentCodeType phone_code_hash:string next_type:flags.1?auth.CodeType timeout:flags.2?int terms_of_service:flags.3?help.TermsOfService = auth.SentCode; +auth.sentCode#5e002502 flags:# type:auth.SentCodeType phone_code_hash:string next_type:flags.1?auth.CodeType timeout:flags.2?int = auth.SentCode; +auth.sentCodeSuccess#2390fe44 authorization:auth.Authorization = auth.SentCode; -auth.authorization#cd050916 flags:# tmp_sessions:flags.0?int user:User = auth.Authorization; +auth.authorization#2ea2c0d4 flags:# setup_password_required:flags.1?true otherwise_relogin_days:flags.1?int tmp_sessions:flags.0?int future_auth_token:flags.2?bytes user:User = auth.Authorization; +auth.authorizationSignUpRequired#44747e9a flags:# terms_of_service:flags.0?help.TermsOfService = auth.Authorization; -auth.exportedAuthorization#df969c2d id:int bytes:bytes = auth.ExportedAuthorization; +auth.exportedAuthorization#b434e2b8 id:long bytes:bytes = auth.ExportedAuthorization; inputNotifyPeer#b8bc5b0c peer:InputPeer = InputNotifyPeer; inputNotifyUsers#193b4417 = InputNotifyPeer; inputNotifyChats#4a95e84e = InputNotifyPeer; inputNotifyBroadcasts#b1db7c7e = InputNotifyPeer; +inputNotifyForumTopic#5c467992 peer:InputPeer top_msg_id:int = InputNotifyPeer; -inputPeerNotifySettings#9c3d198e flags:# show_previews:flags.0?Bool silent:flags.1?Bool mute_until:flags.2?int sound:flags.3?string = InputPeerNotifySettings; +inputPeerNotifySettings#df1f002b flags:# show_previews:flags.0?Bool silent:flags.1?Bool mute_until:flags.2?int sound:flags.3?NotificationSound = InputPeerNotifySettings; -peerNotifySettings#af509d20 flags:# show_previews:flags.0?Bool silent:flags.1?Bool mute_until:flags.2?int sound:flags.3?string = PeerNotifySettings; +peerNotifySettings#a83b0426 flags:# show_previews:flags.0?Bool silent:flags.1?Bool mute_until:flags.2?int ios_sound:flags.3?NotificationSound android_sound:flags.4?NotificationSound other_sound:flags.5?NotificationSound = PeerNotifySettings; -peerSettings#818426cd flags:# report_spam:flags.0?true = PeerSettings; +peerSettings#a518110d flags:# report_spam:flags.0?true add_contact:flags.1?true block_contact:flags.2?true share_contact:flags.3?true need_contacts_exception:flags.4?true report_geo:flags.5?true autoarchived:flags.7?true invite_members:flags.8?true request_chat_broadcast:flags.10?true geo_distance:flags.6?int request_chat_title:flags.9?string request_chat_date:flags.9?int = PeerSettings; wallPaper#a437c3ed id:long flags:# creator:flags.0?true default:flags.1?true pattern:flags.3?true dark:flags.4?true access_hash:long slug:string document:Document settings:flags.2?WallPaperSettings = WallPaper; +wallPaperNoFile#e0804116 id:long flags:# default:flags.1?true dark:flags.4?true settings:flags.2?WallPaperSettings = WallPaper; inputReportReasonSpam#58dbcab8 = ReportReason; inputReportReasonViolence#1e22c78d = ReportReason; inputReportReasonPornography#2e59d922 = ReportReason; inputReportReasonChildAbuse#adf44ee3 = ReportReason; -inputReportReasonOther#e1746d0a text:string = ReportReason; +inputReportReasonOther#c1e4a2b1 = ReportReason; inputReportReasonCopyright#9b89f93a = ReportReason; +inputReportReasonGeoIrrelevant#dbd4feed = ReportReason; +inputReportReasonFake#f5ddd6e7 = ReportReason; +inputReportReasonIllegalDrugs#a8eb2be = ReportReason; +inputReportReasonPersonalDetails#9ec7863d = ReportReason; -userFull#8ea4a881 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true user:User about:flags.1?string link:contacts.Link profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int = UserFull; +userFull#93eadb53 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true translations_disabled:flags.23?true id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights premium_gifts:flags.19?Vector wallpaper:flags.24?WallPaper = UserFull; -contact#f911c994 user_id:int mutual:Bool = Contact; +contact#145ade0b user_id:long mutual:Bool = Contact; -importedContact#d0028438 user_id:int client_id:long = ImportedContact; +importedContact#c13e3c50 user_id:long client_id:long = ImportedContact; -contactBlocked#561bc879 user_id:int date:int = ContactBlocked; - -contactStatus#d3680c61 user_id:int status:UserStatus = ContactStatus; - -contacts.link#3ace484c my_link:ContactLink foreign_link:ContactLink user:User = contacts.Link; +contactStatus#16d9703b user_id:long status:UserStatus = ContactStatus; contacts.contactsNotModified#b74ba9d2 = contacts.Contacts; contacts.contacts#eae87e42 contacts:Vector saved_count:int users:Vector = contacts.Contacts; contacts.importedContacts#77d01c3b imported:Vector popular_invites:Vector retry_contacts:Vector users:Vector = contacts.ImportedContacts; -contacts.blocked#1c138d15 blocked:Vector users:Vector = contacts.Blocked; -contacts.blockedSlice#900802a1 count:int blocked:Vector users:Vector = contacts.Blocked; +contacts.blocked#ade1591 blocked:Vector chats:Vector users:Vector = contacts.Blocked; +contacts.blockedSlice#e1664194 count:int blocked:Vector chats:Vector users:Vector = contacts.Blocked; messages.dialogs#15ba6c40 dialogs:Vector messages:Vector chats:Vector users:Vector = messages.Dialogs; messages.dialogsSlice#71e094f3 count:int dialogs:Vector messages:Vector chats:Vector users:Vector = messages.Dialogs; messages.dialogsNotModified#f0e3e596 count:int = messages.Dialogs; messages.messages#8c718e87 messages:Vector chats:Vector users:Vector = messages.Messages; -messages.messagesSlice#a6c47aaa flags:# inexact:flags.1?true count:int messages:Vector chats:Vector users:Vector = messages.Messages; -messages.channelMessages#99262e37 flags:# inexact:flags.1?true pts:int count:int messages:Vector chats:Vector users:Vector = messages.Messages; +messages.messagesSlice#3a54685e flags:# inexact:flags.1?true count:int next_rate:flags.0?int offset_id_offset:flags.2?int messages:Vector chats:Vector users:Vector = messages.Messages; +messages.channelMessages#c776ba4e flags:# inexact:flags.1?true pts:int count:int offset_id_offset:flags.2?int messages:Vector topics:Vector chats:Vector users:Vector = messages.Messages; messages.messagesNotModified#74535f21 count:int = messages.Messages; messages.chats#64ff9fd5 chats:Vector = messages.Chats; @@ -248,76 +275,119 @@ inputMessagesFilterRoundVideo#b549da53 = MessagesFilter; inputMessagesFilterMyMentions#c1f8e69a = MessagesFilter; inputMessagesFilterGeo#e7026d0d = MessagesFilter; inputMessagesFilterContacts#e062db83 = MessagesFilter; +inputMessagesFilterPinned#1bb00451 = MessagesFilter; updateNewMessage#1f2b0afd message:Message pts:int pts_count:int = Update; updateMessageID#4e90bfd6 id:int random_id:long = Update; updateDeleteMessages#a20db0e5 messages:Vector pts:int pts_count:int = Update; -updateUserTyping#5c486927 user_id:int action:SendMessageAction = Update; -updateChatUserTyping#9a65ea1f chat_id:int user_id:int action:SendMessageAction = Update; +updateUserTyping#c01e857f user_id:long action:SendMessageAction = Update; +updateChatUserTyping#83487af0 chat_id:long from_id:Peer action:SendMessageAction = Update; updateChatParticipants#7761198 participants:ChatParticipants = Update; -updateUserStatus#1bfbd823 user_id:int status:UserStatus = Update; -updateUserName#a7332b73 user_id:int first_name:string last_name:string username:string = Update; -updateUserPhoto#95313b0c user_id:int date:int photo:UserProfilePhoto previous:Bool = Update; -updateContactLink#9d2e67c5 user_id:int my_link:ContactLink foreign_link:ContactLink = Update; +updateUserStatus#e5bdf8de user_id:long status:UserStatus = Update; +updateUserName#a7848924 user_id:long first_name:string last_name:string usernames:Vector = Update; updateNewEncryptedMessage#12bcbd9a message:EncryptedMessage qts:int = Update; updateEncryptedChatTyping#1710f156 chat_id:int = Update; updateEncryption#b4a2e88d chat:EncryptedChat date:int = Update; updateEncryptedMessagesRead#38fe25b7 chat_id:int max_date:int date:int = Update; -updateChatParticipantAdd#ea4b0e5c chat_id:int user_id:int inviter_id:int date:int version:int = Update; -updateChatParticipantDelete#6e5f8c22 chat_id:int user_id:int version:int = Update; +updateChatParticipantAdd#3dda5451 chat_id:long user_id:long inviter_id:long date:int version:int = Update; +updateChatParticipantDelete#e32f3d77 chat_id:long user_id:long version:int = Update; updateDcOptions#8e5e9873 dc_options:Vector = Update; -updateUserBlocked#80ece81a user_id:int blocked:Bool = Update; updateNotifySettings#bec268ef peer:NotifyPeer notify_settings:PeerNotifySettings = Update; updateServiceNotification#ebe46819 flags:# popup:flags.0?true inbox_date:flags.1?int type:string message:string media:MessageMedia entities:Vector = Update; updatePrivacy#ee3b272a key:PrivacyKey rules:Vector = Update; -updateUserPhone#12b9417b user_id:int phone:string = Update; -updateReadHistoryInbox#9961fd5c peer:Peer max_id:int pts:int pts_count:int = Update; +updateUserPhone#5492a13 user_id:long phone:string = Update; +updateReadHistoryInbox#9c974fdf flags:# folder_id:flags.0?int peer:Peer max_id:int still_unread_count:int pts:int pts_count:int = Update; updateReadHistoryOutbox#2f2f21bf peer:Peer max_id:int pts:int pts_count:int = Update; updateWebPage#7f891213 webpage:WebPage pts:int pts_count:int = Update; updateReadMessagesContents#68c13933 messages:Vector pts:int pts_count:int = Update; -updateChannelTooLong#eb0467fb flags:# channel_id:int pts:flags.0?int = Update; -updateChannel#b6d45656 channel_id:int = Update; +updateChannelTooLong#108d941f flags:# channel_id:long pts:flags.0?int = Update; +updateChannel#635b4c09 channel_id:long = Update; updateNewChannelMessage#62ba04d9 message:Message pts:int pts_count:int = Update; -updateReadChannelInbox#4214f37f channel_id:int max_id:int = Update; -updateDeleteChannelMessages#c37521c9 channel_id:int messages:Vector pts:int pts_count:int = Update; -updateChannelMessageViews#98a12b4b channel_id:int id:int views:int = Update; -updateChatParticipantAdmin#b6901959 chat_id:int user_id:int is_admin:Bool version:int = Update; +updateReadChannelInbox#922e6e10 flags:# folder_id:flags.0?int channel_id:long max_id:int still_unread_count:int pts:int = Update; +updateDeleteChannelMessages#c32d5b12 channel_id:long messages:Vector pts:int pts_count:int = Update; +updateChannelMessageViews#f226ac08 channel_id:long id:int views:int = Update; +updateChatParticipantAdmin#d7ca61a2 chat_id:long user_id:long is_admin:Bool version:int = Update; updateNewStickerSet#688a30aa stickerset:messages.StickerSet = Update; -updateStickerSetsOrder#bb2d201 flags:# masks:flags.0?true order:Vector = Update; -updateStickerSets#43ae3dec = Update; +updateStickerSetsOrder#bb2d201 flags:# masks:flags.0?true emojis:flags.1?true order:Vector = Update; +updateStickerSets#31c24808 flags:# masks:flags.0?true emojis:flags.1?true = Update; updateSavedGifs#9375341e = Update; -updateBotInlineQuery#54826690 flags:# query_id:long user_id:int query:string geo:flags.0?GeoPoint offset:string = Update; -updateBotInlineSend#e48f964 flags:# user_id:int query:string geo:flags.0?GeoPoint id:string msg_id:flags.1?InputBotInlineMessageID = Update; +updateBotInlineQuery#496f379c flags:# query_id:long user_id:long query:string geo:flags.0?GeoPoint peer_type:flags.1?InlineQueryPeerType offset:string = Update; +updateBotInlineSend#12f12a07 flags:# user_id:long query:string geo:flags.0?GeoPoint id:string msg_id:flags.1?InputBotInlineMessageID = Update; updateEditChannelMessage#1b3f4df7 message:Message pts:int pts_count:int = Update; -updateChannelPinnedMessage#98592475 channel_id:int id:int = Update; -updateBotCallbackQuery#e73547e1 flags:# query_id:long user_id:int peer:Peer msg_id:int chat_instance:long data:flags.0?bytes game_short_name:flags.1?string = Update; +updateBotCallbackQuery#b9cfc48d flags:# query_id:long user_id:long peer:Peer msg_id:int chat_instance:long data:flags.0?bytes game_short_name:flags.1?string = Update; updateEditMessage#e40370a3 message:Message pts:int pts_count:int = Update; -updateInlineBotCallbackQuery#f9d27a5a flags:# query_id:long user_id:int msg_id:InputBotInlineMessageID chat_instance:long data:flags.0?bytes game_short_name:flags.1?string = Update; -updateReadChannelOutbox#25d6c9c7 channel_id:int max_id:int = Update; -updateDraftMessage#ee2bb969 peer:Peer draft:DraftMessage = Update; +updateInlineBotCallbackQuery#691e9052 flags:# query_id:long user_id:long msg_id:InputBotInlineMessageID chat_instance:long data:flags.0?bytes game_short_name:flags.1?string = Update; +updateReadChannelOutbox#b75f99a9 channel_id:long max_id:int = Update; +updateDraftMessage#1b49ec6d flags:# peer:Peer top_msg_id:flags.0?int draft:DraftMessage = Update; updateReadFeaturedStickers#571d2742 = Update; updateRecentStickers#9a422c20 = Update; updateConfig#a229dd06 = Update; updatePtsChanged#3354678f = Update; -updateChannelWebPage#40771900 channel_id:int webpage:WebPage pts:int pts_count:int = Update; -updateDialogPinned#19d27f3c flags:# pinned:flags.0?true peer:DialogPeer = Update; -updatePinnedDialogs#ea4cb65b flags:# order:flags.0?Vector = Update; +updateChannelWebPage#2f2ba99f channel_id:long webpage:WebPage pts:int pts_count:int = Update; +updateDialogPinned#6e6fe51c flags:# pinned:flags.0?true folder_id:flags.1?int peer:DialogPeer = Update; +updatePinnedDialogs#fa0f3ca2 flags:# folder_id:flags.1?int order:flags.0?Vector = Update; updateBotWebhookJSON#8317c0c3 data:DataJSON = Update; updateBotWebhookJSONQuery#9b9240a6 query_id:long data:DataJSON timeout:int = Update; -updateBotShippingQuery#e0cdc940 query_id:long user_id:int payload:bytes shipping_address:PostAddress = Update; -updateBotPrecheckoutQuery#5d2f3aa9 flags:# query_id:long user_id:int payload:bytes info:flags.0?PaymentRequestedInfo shipping_option_id:flags.1?string currency:string total_amount:long = Update; +updateBotShippingQuery#b5aefd7d query_id:long user_id:long payload:bytes shipping_address:PostAddress = Update; +updateBotPrecheckoutQuery#8caa9a96 flags:# query_id:long user_id:long payload:bytes info:flags.0?PaymentRequestedInfo shipping_option_id:flags.1?string currency:string total_amount:long = Update; updatePhoneCall#ab0f6b1e phone_call:PhoneCall = Update; updateLangPackTooLong#46560264 lang_code:string = Update; updateLangPack#56022f4d difference:LangPackDifference = Update; updateFavedStickers#e511996d = Update; -updateChannelReadMessagesContents#89893b45 channel_id:int messages:Vector = Update; +updateChannelReadMessagesContents#ea29055d flags:# channel_id:long top_msg_id:flags.0?int messages:Vector = Update; updateContactsReset#7084a7be = Update; -updateChannelAvailableMessages#70db6837 channel_id:int available_min_id:int = Update; +updateChannelAvailableMessages#b23fc698 channel_id:long available_min_id:int = Update; updateDialogUnreadMark#e16459c3 flags:# unread:flags.0?true peer:DialogPeer = Update; -updateUserPinnedMessage#4c43da18 user_id:int id:int = Update; -updateChatPinnedMessage#e10db349 chat_id:int id:int version:int = Update; updateMessagePoll#aca1657b flags:# poll_id:long poll:flags.0?Poll results:PollResults = Update; updateChatDefaultBannedRights#54c01850 peer:Peer default_banned_rights:ChatBannedRights version:int = Update; +updateFolderPeers#19360dc0 folder_peers:Vector pts:int pts_count:int = Update; +updatePeerSettings#6a7e7366 peer:Peer settings:PeerSettings = Update; +updatePeerLocated#b4afcfb0 peers:Vector = Update; +updateNewScheduledMessage#39a51dfb message:Message = Update; +updateDeleteScheduledMessages#90866cee peer:Peer messages:Vector = Update; +updateTheme#8216fba3 theme:Theme = Update; +updateGeoLiveViewed#871fb939 peer:Peer msg_id:int = Update; +updateLoginToken#564fe691 = Update; +updateMessagePollVote#106395c9 poll_id:long user_id:long options:Vector qts:int = Update; +updateDialogFilter#26ffde7d flags:# id:int filter:flags.0?DialogFilter = Update; +updateDialogFilterOrder#a5d72105 order:Vector = Update; +updateDialogFilters#3504914f = Update; +updatePhoneCallSignalingData#2661bf09 phone_call_id:long data:bytes = Update; +updateChannelMessageForwards#d29a27f4 channel_id:long id:int forwards:int = Update; +updateReadChannelDiscussionInbox#d6b19546 flags:# channel_id:long top_msg_id:int read_max_id:int broadcast_id:flags.0?long broadcast_post:flags.0?int = Update; +updateReadChannelDiscussionOutbox#695c9e7c channel_id:long top_msg_id:int read_max_id:int = Update; +updatePeerBlocked#246a4b22 peer_id:Peer blocked:Bool = Update; +updateChannelUserTyping#8c88c923 flags:# channel_id:long top_msg_id:flags.0?int from_id:Peer action:SendMessageAction = Update; +updatePinnedMessages#ed85eab5 flags:# pinned:flags.0?true peer:Peer messages:Vector pts:int pts_count:int = Update; +updatePinnedChannelMessages#5bb98608 flags:# pinned:flags.0?true channel_id:long messages:Vector pts:int pts_count:int = Update; +updateChat#f89a6a4e chat_id:long = Update; +updateGroupCallParticipants#f2ebdb4e call:InputGroupCall participants:Vector version:int = Update; +updateGroupCall#14b24500 chat_id:long call:GroupCall = Update; +updatePeerHistoryTTL#bb9bb9a5 flags:# peer:Peer ttl_period:flags.0?int = Update; +updateChatParticipant#d087663a flags:# chat_id:long date:int actor_id:long user_id:long prev_participant:flags.0?ChatParticipant new_participant:flags.1?ChatParticipant invite:flags.2?ExportedChatInvite qts:int = Update; +updateChannelParticipant#985d3abb flags:# via_chatlist:flags.3?true channel_id:long date:int actor_id:long user_id:long prev_participant:flags.0?ChannelParticipant new_participant:flags.1?ChannelParticipant invite:flags.2?ExportedChatInvite qts:int = Update; +updateBotStopped#c4870a49 user_id:long date:int stopped:Bool qts:int = Update; +updateGroupCallConnection#b783982 flags:# presentation:flags.0?true params:DataJSON = Update; +updateBotCommands#4d712f2e peer:Peer bot_id:long commands:Vector = Update; +updatePendingJoinRequests#7063c3db peer:Peer requests_pending:int recent_requesters:Vector = Update; +updateBotChatInviteRequester#11dfa986 peer:Peer date:int user_id:long about:string invite:ExportedChatInvite qts:int = Update; +updateMessageReactions#5e1b3cb8 flags:# peer:Peer msg_id:int top_msg_id:flags.0?int reactions:MessageReactions = Update; +updateAttachMenuBots#17b7a20b = Update; +updateWebViewResultSent#1592b79d query_id:long = Update; +updateBotMenuButton#14b85813 bot_id:long button:BotMenuButton = Update; +updateSavedRingtones#74d8be99 = Update; +updateTranscribedAudio#84cd5a flags:# pending:flags.0?true peer:Peer msg_id:int transcription_id:long text:string = Update; +updateReadFeaturedEmojiStickers#fb4c496c = Update; +updateUserEmojiStatus#28373599 user_id:long emoji_status:EmojiStatus = Update; +updateRecentEmojiStatuses#30f443db = Update; +updateRecentReactions#6f7863f4 = Update; +updateMoveStickerSetToTop#86fccf85 flags:# masks:flags.0?true emojis:flags.1?true stickerset:long = Update; +updateMessageExtendedMedia#5a73a98c peer:Peer msg_id:int extended_media:MessageExtendedMedia = Update; +updateChannelPinnedTopic#192efbe3 flags:# pinned:flags.0?true channel_id:long topic_id:int = Update; +updateChannelPinnedTopics#fe198602 flags:# channel_id:long order:flags.0?Vector = Update; +updateUser#20529438 user_id:long = Update; +updateAutoSaveSettings#ec05b097 = Update; +updateGroupInvitePrivacyForbidden#ccf08ad6 user_id:long = Update; updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State; @@ -327,12 +397,12 @@ updates.differenceSlice#a8fb1981 new_messages:Vector new_encrypted_mess updates.differenceTooLong#4afe8f6d pts:int = updates.Difference; updatesTooLong#e317af7e = Updates; -updateShortMessage#914fbf11 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true id:int user_id:int message:string pts:int pts_count:int date:int fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int entities:flags.7?Vector = Updates; -updateShortChatMessage#16812688 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true id:int from_id:int chat_id:int message:string pts:int pts_count:int date:int fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int entities:flags.7?Vector = Updates; +updateShortMessage#313bc7f8 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true id:int user_id:long message:string pts:int pts_count:int date:int fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long reply_to:flags.3?MessageReplyHeader entities:flags.7?Vector ttl_period:flags.25?int = Updates; +updateShortChatMessage#4d6deea5 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true id:int from_id:long chat_id:long message:string pts:int pts_count:int date:int fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long reply_to:flags.3?MessageReplyHeader entities:flags.7?Vector ttl_period:flags.25?int = Updates; updateShort#78d4dec1 update:Update date:int = Updates; updatesCombined#725b04c3 updates:Vector users:Vector chats:Vector date:int seq_start:int seq:int = Updates; updates#74ae4240 updates:Vector users:Vector chats:Vector date:int seq:int = Updates; -updateShortSentMessage#11f1331c flags:# out:flags.1?true id:int pts:int pts_count:int date:int media:flags.9?MessageMedia entities:flags.7?Vector = Updates; +updateShortSentMessage#9015e101 flags:# out:flags.1?true id:int pts:int pts_count:int date:int media:flags.9?MessageMedia entities:flags.7?Vector ttl_period:flags.25?int = Updates; photos.photos#8dca6aa5 photos:Vector users:Vector = photos.Photos; photos.photosSlice#15051f54 count:int photos:Vector users:Vector = photos.Photos; @@ -342,27 +412,27 @@ photos.photo#20212ca8 photo:Photo users:Vector = photos.Photo; upload.file#96a18d5 type:storage.FileType mtime:int bytes:bytes = upload.File; upload.fileCdnRedirect#f18cda44 dc_id:int file_token:bytes encryption_key:bytes encryption_iv:bytes file_hashes:Vector = upload.File; -dcOption#18b7a10d flags:# ipv6:flags.0?true media_only:flags.1?true tcpo_only:flags.2?true cdn:flags.3?true static:flags.4?true id:int ip_address:string port:int secret:flags.10?bytes = DcOption; +dcOption#18b7a10d flags:# ipv6:flags.0?true media_only:flags.1?true tcpo_only:flags.2?true cdn:flags.3?true static:flags.4?true this_port_only:flags.5?true id:int ip_address:string port:int secret:flags.10?bytes = DcOption; -config#e6ca25f6 flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:flags.3?true preload_featured_stickers:flags.4?true ignore_phone_entities:flags.5?true revoke_pm_inbox:flags.6?true blocked_mode:flags.8?true pfs_enabled:flags.13?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector dc_txt_domain_name:string chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int revoke_time_limit:int revoke_pm_time_limit:int rating_e_decay:int stickers_recent_limit:int stickers_faved_limit:int channels_read_media_period:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string autoupdate_url_prefix:flags.7?string gif_search_username:flags.9?string venue_search_username:flags.10?string img_search_username:flags.11?string static_maps_provider:flags.12?string caption_length_max:int message_length_max:int webfile_dc_id:int suggested_lang_code:flags.2?string lang_pack_version:flags.2?int base_lang_pack_version:flags.2?int = Config; +config#cc1a241e flags:# default_p2p_contacts:flags.3?true preload_featured_stickers:flags.4?true revoke_pm_inbox:flags.6?true blocked_mode:flags.8?true force_try_ipv6:flags.14?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector dc_txt_domain_name:string chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int push_chat_period_ms:int push_chat_limit:int edit_time_limit:int revoke_time_limit:int revoke_pm_time_limit:int rating_e_decay:int stickers_recent_limit:int channels_read_media_period:int tmp_sessions:flags.0?int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string autoupdate_url_prefix:flags.7?string gif_search_username:flags.9?string venue_search_username:flags.10?string img_search_username:flags.11?string static_maps_provider:flags.12?string caption_length_max:int message_length_max:int webfile_dc_id:int suggested_lang_code:flags.2?string lang_pack_version:flags.2?int base_lang_pack_version:flags.2?int reactions_default:flags.15?Reaction autologin_token:flags.16?string = Config; nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc; -help.appUpdate#1da7158f flags:# popup:flags.0?true id:int version:string text:string entities:Vector document:flags.1?Document url:flags.2?string = help.AppUpdate; +help.appUpdate#ccbbce30 flags:# can_not_skip:flags.0?true id:int version:string text:string entities:Vector document:flags.1?Document url:flags.2?string sticker:flags.3?Document = help.AppUpdate; help.noAppUpdate#c45a6536 = help.AppUpdate; help.inviteText#18cb9f78 message:string = help.InviteText; encryptedChatEmpty#ab7ec0a0 id:int = EncryptedChat; -encryptedChatWaiting#3bf703dc id:int access_hash:long date:int admin_id:int participant_id:int = EncryptedChat; -encryptedChatRequested#c878527e id:int access_hash:long date:int admin_id:int participant_id:int g_a:bytes = EncryptedChat; -encryptedChat#fa56ce36 id:int access_hash:long date:int admin_id:int participant_id:int g_a_or_b:bytes key_fingerprint:long = EncryptedChat; -encryptedChatDiscarded#13d6dd27 id:int = EncryptedChat; +encryptedChatWaiting#66b25953 id:int access_hash:long date:int admin_id:long participant_id:long = EncryptedChat; +encryptedChatRequested#48f1d94c flags:# folder_id:flags.0?int id:int access_hash:long date:int admin_id:long participant_id:long g_a:bytes = EncryptedChat; +encryptedChat#61f0d4c7 id:int access_hash:long date:int admin_id:long participant_id:long g_a_or_b:bytes key_fingerprint:long = EncryptedChat; +encryptedChatDiscarded#1e1c7c45 flags:# history_deleted:flags.0?true id:int = EncryptedChat; inputEncryptedChat#f141b5e1 chat_id:int access_hash:long = InputEncryptedChat; encryptedFileEmpty#c21f497e = EncryptedFile; -encryptedFile#4a70994c id:long access_hash:long size:int dc_id:int key_fingerprint:int = EncryptedFile; +encryptedFile#a8008cd8 id:long access_hash:long size:long dc_id:int key_fingerprint:int = EncryptedFile; inputEncryptedFileEmpty#1837c364 = InputEncryptedFile; inputEncryptedFileUploaded#64bd0306 id:long parts:int md5_checksum:string key_fingerprint:int = InputEncryptedFile; @@ -382,7 +452,7 @@ inputDocumentEmpty#72f0eaae = InputDocument; inputDocument#1abfb575 id:long access_hash:long file_reference:bytes = InputDocument; documentEmpty#36f8c871 id:long = Document; -document#9ba29cc1 flags:# id:long access_hash:long file_reference:bytes date:int mime_type:string size:int thumbs:flags.0?Vector dc_id:int attributes:Vector = Document; +document#8fd4c4d8 flags:# id:long access_hash:long file_reference:bytes date:int mime_type:string size:long thumbs:flags.0?Vector video_thumbs:flags.1?Vector dc_id:int attributes:Vector = Document; help.support#17c6b5f6 phone_number:string user:User = help.Support; @@ -390,6 +460,7 @@ notifyPeer#9fd40bd8 peer:Peer = NotifyPeer; notifyUsers#b4c83b4c = NotifyPeer; notifyChats#c007cec3 = NotifyPeer; notifyBroadcasts#d612e8ef = NotifyPeer; +notifyForumTopic#226e6308 peer:Peer top_msg_id:int = NotifyPeer; sendMessageTypingAction#16bf744e = SendMessageAction; sendMessageCancelAction#fd5ec8f5 = SendMessageAction; @@ -404,6 +475,11 @@ sendMessageChooseContactAction#628cbc6f = SendMessageAction; sendMessageGamePlayAction#dd6a8f48 = SendMessageAction; sendMessageRecordRoundAction#88f27fbc = SendMessageAction; sendMessageUploadRoundAction#243e1c66 progress:int = SendMessageAction; +speakingInGroupCallAction#d92c2285 = SendMessageAction; +sendMessageHistoryImportAction#dbda9246 progress:int = SendMessageAction; +sendMessageChooseStickerAction#b05ac6b1 = SendMessageAction; +sendMessageEmojiInteraction#25972bcb emoticon:string msg_id:int interaction:DataJSON = SendMessageAction; +sendMessageEmojiInteractionSeen#b665902e emoticon:string = SendMessageAction; contacts.found#b3134d9d my_results:Vector results:Vector chats:Vector users:Vector = contacts.Found; @@ -413,6 +489,9 @@ inputPrivacyKeyPhoneCall#fabadc5f = InputPrivacyKey; inputPrivacyKeyPhoneP2P#db9e70d2 = InputPrivacyKey; inputPrivacyKeyForwards#a4dd4c08 = InputPrivacyKey; inputPrivacyKeyProfilePhoto#5719bacc = InputPrivacyKey; +inputPrivacyKeyPhoneNumber#352dafa = InputPrivacyKey; +inputPrivacyKeyAddedByPhone#d1219bdd = InputPrivacyKey; +inputPrivacyKeyVoiceMessages#aee69d68 = InputPrivacyKey; privacyKeyStatusTimestamp#bc2eab30 = PrivacyKey; privacyKeyChatInvite#500e6dfa = PrivacyKey; @@ -420,6 +499,9 @@ privacyKeyPhoneCall#3d662b7b = PrivacyKey; privacyKeyPhoneP2P#39491cc8 = PrivacyKey; privacyKeyForwards#69ec56a3 = PrivacyKey; privacyKeyProfilePhoto#96151fed = PrivacyKey; +privacyKeyPhoneNumber#d19ae46d = PrivacyKey; +privacyKeyAddedByPhone#42ffd42b = PrivacyKey; +privacyKeyVoiceMessages#697f414 = PrivacyKey; inputPrivacyValueAllowContacts#d09e07b = InputPrivacyRule; inputPrivacyValueAllowAll#184b35ce = InputPrivacyRule; @@ -427,15 +509,19 @@ inputPrivacyValueAllowUsers#131cc67f users:Vector = InputPrivacyRule; inputPrivacyValueDisallowContacts#ba52007 = InputPrivacyRule; inputPrivacyValueDisallowAll#d66b66c9 = InputPrivacyRule; inputPrivacyValueDisallowUsers#90110467 users:Vector = InputPrivacyRule; +inputPrivacyValueAllowChatParticipants#840649cf chats:Vector = InputPrivacyRule; +inputPrivacyValueDisallowChatParticipants#e94f0f86 chats:Vector = InputPrivacyRule; privacyValueAllowContacts#fffe1bac = PrivacyRule; privacyValueAllowAll#65427b82 = PrivacyRule; -privacyValueAllowUsers#4d5bbe0c users:Vector = PrivacyRule; +privacyValueAllowUsers#b8905fb2 users:Vector = PrivacyRule; privacyValueDisallowContacts#f888fa1a = PrivacyRule; privacyValueDisallowAll#8b73e763 = PrivacyRule; -privacyValueDisallowUsers#c7f49b7 users:Vector = PrivacyRule; +privacyValueDisallowUsers#e4621141 users:Vector = PrivacyRule; +privacyValueAllowChatParticipants#6b134e8e chats:Vector = PrivacyRule; +privacyValueDisallowChatParticipants#41c87565 chats:Vector = PrivacyRule; -account.privacyRules#554abb6f rules:Vector users:Vector = account.PrivacyRules; +account.privacyRules#50a04e45 rules:Vector chats:Vector users:Vector = account.PrivacyRules; accountDaysTTL#b8d0afdf days:int = AccountDaysTTL; @@ -446,32 +532,28 @@ documentAttributeVideo#ef02ce6 flags:# round_message:flags.0?true supports_strea documentAttributeAudio#9852f9c6 flags:# voice:flags.10?true duration:int title:flags.0?string performer:flags.1?string waveform:flags.2?bytes = DocumentAttribute; documentAttributeFilename#15590068 file_name:string = DocumentAttribute; documentAttributeHasStickers#9801d2f7 = DocumentAttribute; +documentAttributeCustomEmoji#fd149899 flags:# free:flags.0?true text_color:flags.1?true alt:string stickerset:InputStickerSet = DocumentAttribute; messages.stickersNotModified#f1749a22 = messages.Stickers; -messages.stickers#e4599bbd hash:int stickers:Vector = messages.Stickers; +messages.stickers#30a6ec7e hash:long stickers:Vector = messages.Stickers; stickerPack#12b299d4 emoticon:string documents:Vector = StickerPack; messages.allStickersNotModified#e86602c3 = messages.AllStickers; -messages.allStickers#edfd405f hash:int sets:Vector = messages.AllStickers; +messages.allStickers#cdbbcebb hash:long sets:Vector = messages.AllStickers; messages.affectedMessages#84d19185 pts:int pts_count:int = messages.AffectedMessages; -contactLinkUnknown#5f4f9247 = ContactLink; -contactLinkNone#feedd3ad = ContactLink; -contactLinkHasPhone#268f3f59 = ContactLink; -contactLinkContact#d502c2d0 = ContactLink; - webPageEmpty#eb1477e8 id:long = WebPage; webPagePending#c586da1c id:long date:int = WebPage; -webPage#5f07b4bc flags:# id:long url:string display_url:string hash:int type:flags.0?string site_name:flags.1?string title:flags.2?string description:flags.3?string photo:flags.4?Photo embed_url:flags.5?string embed_type:flags.5?string embed_width:flags.6?int embed_height:flags.6?int duration:flags.7?int author:flags.8?string document:flags.9?Document cached_page:flags.10?Page = WebPage; -webPageNotModified#85849473 = WebPage; +webPage#e89c45b2 flags:# id:long url:string display_url:string hash:int type:flags.0?string site_name:flags.1?string title:flags.2?string description:flags.3?string photo:flags.4?Photo embed_url:flags.5?string embed_type:flags.5?string embed_width:flags.6?int embed_height:flags.6?int duration:flags.7?int author:flags.8?string document:flags.9?Document cached_page:flags.10?Page attributes:flags.12?Vector = WebPage; +webPageNotModified#7311ca11 flags:# cached_page_views:flags.0?int = WebPage; -authorization#ad01d61d flags:# current:flags.0?true official_app:flags.1?true password_pending:flags.2?true hash:long device_model:string platform:string system_version:string api_id:int app_name:string app_version:string date_created:int date_active:int ip:string country:string region:string = Authorization; +authorization#ad01d61d flags:# current:flags.0?true official_app:flags.1?true password_pending:flags.2?true encrypted_requests_disabled:flags.3?true call_requests_disabled:flags.4?true hash:long device_model:string platform:string system_version:string api_id:int app_name:string app_version:string date_created:int date_active:int ip:string country:string region:string = Authorization; -account.authorizations#1250abde authorizations:Vector = account.Authorizations; +account.authorizations#4bff8ea0 authorization_ttl_days:int authorizations:Vector = account.Authorizations; -account.password#ad2641f8 flags:# has_recovery:flags.0?true has_secure_values:flags.1?true has_password:flags.2?true current_algo:flags.2?PasswordKdfAlgo srp_B:flags.2?bytes srp_id:flags.2?long hint:flags.3?string email_unconfirmed_pattern:flags.4?string new_algo:PasswordKdfAlgo new_secure_algo:SecurePasswordKdfAlgo secure_random:bytes = account.Password; +account.password#957b50fb flags:# has_recovery:flags.0?true has_secure_values:flags.1?true has_password:flags.2?true current_algo:flags.2?PasswordKdfAlgo srp_B:flags.2?bytes srp_id:flags.2?long hint:flags.3?string email_unconfirmed_pattern:flags.4?string new_algo:PasswordKdfAlgo new_secure_algo:SecurePasswordKdfAlgo secure_random:bytes pending_reset_date:flags.5?int login_email_pattern:flags.6?string = account.Password; account.passwordSettings#9a5c33e5 flags:# email:flags.0?string secure_settings:flags.1?SecureSecretSettings = account.PasswordSettings; @@ -481,38 +563,55 @@ auth.passwordRecovery#137948a5 email_pattern:string = auth.PasswordRecovery; receivedNotifyMessage#a384b779 id:int flags:int = ReceivedNotifyMessage; -chatInviteEmpty#69df3769 = ExportedChatInvite; -chatInviteExported#fc2e05bc link:string = ExportedChatInvite; +chatInviteExported#ab4a819 flags:# revoked:flags.0?true permanent:flags.5?true request_needed:flags.6?true link:string admin_id:long date:int start_date:flags.4?int expire_date:flags.1?int usage_limit:flags.2?int usage:flags.3?int requested:flags.7?int title:flags.8?string = ExportedChatInvite; +chatInvitePublicJoinRequests#ed107ab7 = ExportedChatInvite; chatInviteAlready#5a686d7c chat:Chat = ChatInvite; -chatInvite#db74f558 flags:# channel:flags.0?true broadcast:flags.1?true public:flags.2?true megagroup:flags.3?true title:string photo:ChatPhoto participants_count:int participants:flags.4?Vector = ChatInvite; +chatInvite#300c44c1 flags:# channel:flags.0?true broadcast:flags.1?true public:flags.2?true megagroup:flags.3?true request_needed:flags.6?true title:string about:flags.5?string photo:Photo participants_count:int participants:flags.4?Vector = ChatInvite; +chatInvitePeek#61695cb0 chat:Chat expires:int = ChatInvite; inputStickerSetEmpty#ffb62b95 = InputStickerSet; inputStickerSetID#9de7a269 id:long access_hash:long = InputStickerSet; inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet; +inputStickerSetAnimatedEmoji#28703c8 = InputStickerSet; +inputStickerSetDice#e67f520e emoticon:string = InputStickerSet; +inputStickerSetAnimatedEmojiAnimations#cde3739 = InputStickerSet; +inputStickerSetPremiumGifts#c88b3b02 = InputStickerSet; +inputStickerSetEmojiGenericAnimations#4c4d4ce = InputStickerSet; +inputStickerSetEmojiDefaultStatuses#29d0f5ee = InputStickerSet; +inputStickerSetEmojiDefaultTopicIcons#44c1f8e9 = InputStickerSet; -stickerSet#6a90bcb7 flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumb:flags.4?PhotoSize count:int hash:int = StickerSet; +stickerSet#2dd14edc flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true videos:flags.6?true emojis:flags.7?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumbs:flags.4?Vector thumb_dc_id:flags.4?int thumb_version:flags.4?int thumb_document_id:flags.8?long count:int hash:int = StickerSet; -messages.stickerSet#b60a24a6 set:StickerSet packs:Vector documents:Vector = messages.StickerSet; +messages.stickerSet#6e153f16 set:StickerSet packs:Vector keywords:Vector documents:Vector = messages.StickerSet; +messages.stickerSetNotModified#d3f924eb = messages.StickerSet; botCommand#c27ac8c7 command:string description:string = BotCommand; -botInfo#98e81d3a user_id:int description:string commands:Vector = BotInfo; +botInfo#8f300b57 flags:# user_id:flags.0?long description:flags.1?string description_photo:flags.4?Photo description_document:flags.5?Document commands:flags.2?Vector menu_button:flags.3?BotMenuButton = BotInfo; keyboardButton#a2fa4880 text:string = KeyboardButton; keyboardButtonUrl#258aff05 text:string url:string = KeyboardButton; -keyboardButtonCallback#683a5e46 text:string data:bytes = KeyboardButton; +keyboardButtonCallback#35bbdb6b flags:# requires_password:flags.0?true text:string data:bytes = KeyboardButton; keyboardButtonRequestPhone#b16a6c29 text:string = KeyboardButton; keyboardButtonRequestGeoLocation#fc796b3f text:string = KeyboardButton; -keyboardButtonSwitchInline#568a748 flags:# same_peer:flags.0?true text:string query:string = KeyboardButton; +keyboardButtonSwitchInline#93b9fbb5 flags:# same_peer:flags.0?true text:string query:string peer_types:flags.1?Vector = KeyboardButton; keyboardButtonGame#50f41ccf text:string = KeyboardButton; keyboardButtonBuy#afd93fbb text:string = KeyboardButton; +keyboardButtonUrlAuth#10b78d29 flags:# text:string fwd_text:flags.0?string url:string button_id:int = KeyboardButton; +inputKeyboardButtonUrlAuth#d02e7fd4 flags:# request_write_access:flags.0?true text:string fwd_text:flags.1?string url:string bot:InputUser = KeyboardButton; +keyboardButtonRequestPoll#bbc7515d flags:# quiz:flags.0?Bool text:string = KeyboardButton; +inputKeyboardButtonUserProfile#e988037b text:string user_id:InputUser = KeyboardButton; +keyboardButtonUserProfile#308660c1 text:string user_id:long = KeyboardButton; +keyboardButtonWebView#13767230 text:string url:string = KeyboardButton; +keyboardButtonSimpleWebView#a0c0505c text:string url:string = KeyboardButton; +keyboardButtonRequestPeer#d0b468c text:string button_id:int peer_type:RequestPeerType = KeyboardButton; keyboardButtonRow#77608b83 buttons:Vector = KeyboardButtonRow; replyKeyboardHide#a03e5b85 flags:# selective:flags.2?true = ReplyMarkup; -replyKeyboardForceReply#f4108aa0 flags:# single_use:flags.1?true selective:flags.2?true = ReplyMarkup; -replyKeyboardMarkup#3502758c flags:# resize:flags.0?true single_use:flags.1?true selective:flags.2?true rows:Vector = ReplyMarkup; +replyKeyboardForceReply#86b40b08 flags:# single_use:flags.1?true selective:flags.2?true placeholder:flags.3?string = ReplyMarkup; +replyKeyboardMarkup#85dd99d1 flags:# resize:flags.0?true single_use:flags.1?true selective:flags.2?true persistent:flags.4?true rows:Vector placeholder:flags.3?string = ReplyMarkup; replyInlineMarkup#48a30254 rows:Vector = ReplyMarkup; messageEntityUnknown#bb92ba95 offset:int length:int = MessageEntity; @@ -526,30 +625,38 @@ messageEntityItalic#826f8b60 offset:int length:int = MessageEntity; messageEntityCode#28a20571 offset:int length:int = MessageEntity; messageEntityPre#73924be0 offset:int length:int language:string = MessageEntity; messageEntityTextUrl#76a6d327 offset:int length:int url:string = MessageEntity; -messageEntityMentionName#352dca58 offset:int length:int user_id:int = MessageEntity; +messageEntityMentionName#dc7b1140 offset:int length:int user_id:long = MessageEntity; inputMessageEntityMentionName#208e68c9 offset:int length:int user_id:InputUser = MessageEntity; messageEntityPhone#9b69e34b offset:int length:int = MessageEntity; messageEntityCashtag#4c4e743f offset:int length:int = MessageEntity; +messageEntityUnderline#9c4e7e8b offset:int length:int = MessageEntity; +messageEntityStrike#bf0693d4 offset:int length:int = MessageEntity; +messageEntityBlockquote#20df5d0 offset:int length:int = MessageEntity; +messageEntityBankCard#761e6af4 offset:int length:int = MessageEntity; +messageEntitySpoiler#32ca960f offset:int length:int = MessageEntity; +messageEntityCustomEmoji#c8cf05f8 offset:int length:int document_id:long = MessageEntity; inputChannelEmpty#ee8c1e86 = InputChannel; -inputChannel#afeb712e channel_id:int access_hash:long = InputChannel; +inputChannel#f35aec28 channel_id:long access_hash:long = InputChannel; +inputChannelFromMessage#5b934f9d peer:InputPeer msg_id:int channel_id:long = InputChannel; contacts.resolvedPeer#7f077ad9 peer:Peer chats:Vector users:Vector = contacts.ResolvedPeer; messageRange#ae30253 min_id:int max_id:int = MessageRange; updates.channelDifferenceEmpty#3e11affb flags:# final:flags.0?true pts:int timeout:flags.1?int = updates.ChannelDifference; -updates.channelDifferenceTooLong#6a9d7b35 flags:# final:flags.0?true pts:int timeout:flags.1?int top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int messages:Vector chats:Vector users:Vector = updates.ChannelDifference; +updates.channelDifferenceTooLong#a4bcc6fe flags:# final:flags.0?true timeout:flags.1?int dialog:Dialog messages:Vector chats:Vector users:Vector = updates.ChannelDifference; updates.channelDifference#2064674e flags:# final:flags.0?true pts:int timeout:flags.1?int new_messages:Vector other_updates:Vector chats:Vector users:Vector = updates.ChannelDifference; channelMessagesFilterEmpty#94d42ee7 = ChannelMessagesFilter; channelMessagesFilter#cd77d957 flags:# exclude_new_messages:flags.1?true ranges:Vector = ChannelMessagesFilter; -channelParticipant#15ebac1d user_id:int date:int = ChannelParticipant; -channelParticipantSelf#a3289a6d user_id:int inviter_id:int date:int = ChannelParticipant; -channelParticipantCreator#e3e2e1f9 user_id:int = ChannelParticipant; -channelParticipantAdmin#5daa6e23 flags:# can_edit:flags.0?true self:flags.1?true user_id:int inviter_id:flags.1?int promoted_by:int date:int admin_rights:ChatAdminRights = ChannelParticipant; -channelParticipantBanned#1c0facaf flags:# left:flags.0?true user_id:int kicked_by:int date:int banned_rights:ChatBannedRights = ChannelParticipant; +channelParticipant#c00c07c0 user_id:long date:int = ChannelParticipant; +channelParticipantSelf#35a8bfa7 flags:# via_request:flags.0?true user_id:long inviter_id:long date:int = ChannelParticipant; +channelParticipantCreator#2fe601d3 flags:# user_id:long admin_rights:ChatAdminRights rank:flags.0?string = ChannelParticipant; +channelParticipantAdmin#34c3bb53 flags:# can_edit:flags.0?true self:flags.1?true user_id:long inviter_id:flags.1?long promoted_by:long date:int admin_rights:ChatAdminRights rank:flags.2?string = ChannelParticipant; +channelParticipantBanned#6df8014e flags:# left:flags.0?true peer:Peer kicked_by:long date:int banned_rights:ChatBannedRights = ChannelParticipant; +channelParticipantLeft#1b03f006 peer:Peer = ChannelParticipant; channelParticipantsRecent#de3f3c79 = ChannelParticipantsFilter; channelParticipantsAdmins#b4608969 = ChannelParticipantsFilter; @@ -558,28 +665,25 @@ channelParticipantsBots#b0d1865b = ChannelParticipantsFilter; channelParticipantsBanned#1427a5e1 q:string = ChannelParticipantsFilter; channelParticipantsSearch#656ac4b q:string = ChannelParticipantsFilter; channelParticipantsContacts#bb6ae88d q:string = ChannelParticipantsFilter; +channelParticipantsMentions#e04b5ceb flags:# q:flags.0?string top_msg_id:flags.1?int = ChannelParticipantsFilter; -channels.channelParticipants#f56ee2a8 count:int participants:Vector users:Vector = channels.ChannelParticipants; +channels.channelParticipants#9ab0feaf count:int participants:Vector chats:Vector users:Vector = channels.ChannelParticipants; channels.channelParticipantsNotModified#f0173fe9 = channels.ChannelParticipants; -channels.channelParticipant#d0d9b163 participant:ChannelParticipant users:Vector = channels.ChannelParticipant; +channels.channelParticipant#dfb80317 participant:ChannelParticipant chats:Vector users:Vector = channels.ChannelParticipant; help.termsOfService#780a0310 flags:# popup:flags.0?true id:DataJSON text:string entities:Vector min_age_confirm:flags.1?int = help.TermsOfService; -foundGif#162ecc1f url:string thumb_url:string content_url:string content_type:string w:int h:int = FoundGif; -foundGifCached#9c750409 url:string photo:Photo document:Document = FoundGif; - -messages.foundGifs#450a1c0a next_offset:int results:Vector = messages.FoundGifs; - messages.savedGifsNotModified#e8025ca2 = messages.SavedGifs; -messages.savedGifs#2e0709a5 hash:int gifs:Vector = messages.SavedGifs; +messages.savedGifs#84a02a0d hash:long gifs:Vector = messages.SavedGifs; inputBotInlineMessageMediaAuto#3380c786 flags:# message:string entities:flags.1?Vector reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage; inputBotInlineMessageText#3dcd7a87 flags:# no_webpage:flags.0?true message:string entities:flags.1?Vector reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage; -inputBotInlineMessageMediaGeo#c1b15d65 flags:# geo_point:InputGeoPoint period:int reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage; +inputBotInlineMessageMediaGeo#96929a85 flags:# geo_point:InputGeoPoint heading:flags.0?int period:flags.1?int proximity_notification_radius:flags.3?int reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage; inputBotInlineMessageMediaVenue#417bbf11 flags:# geo_point:InputGeoPoint title:string address:string provider:string venue_id:string venue_type:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage; inputBotInlineMessageMediaContact#a6edbffd flags:# phone_number:string first_name:string last_name:string vcard:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage; inputBotInlineMessageGame#4b425864 flags:# reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage; +inputBotInlineMessageMediaInvoice#d7e78225 flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string provider_data:DataJSON reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage; inputBotInlineResult#88bf9319 flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb:flags.4?InputWebDocument content:flags.5?InputWebDocument send_message:InputBotInlineMessage = InputBotInlineResult; inputBotInlineResultPhoto#a8d864a7 id:string type:string photo:InputPhoto send_message:InputBotInlineMessage = InputBotInlineResult; @@ -588,33 +692,42 @@ inputBotInlineResultGame#4fa417f2 id:string short_name:string send_message:Input botInlineMessageMediaAuto#764cf810 flags:# message:string entities:flags.1?Vector reply_markup:flags.2?ReplyMarkup = BotInlineMessage; botInlineMessageText#8c7f65e2 flags:# no_webpage:flags.0?true message:string entities:flags.1?Vector reply_markup:flags.2?ReplyMarkup = BotInlineMessage; -botInlineMessageMediaGeo#b722de65 flags:# geo:GeoPoint period:int reply_markup:flags.2?ReplyMarkup = BotInlineMessage; +botInlineMessageMediaGeo#51846fd flags:# geo:GeoPoint heading:flags.0?int period:flags.1?int proximity_notification_radius:flags.3?int reply_markup:flags.2?ReplyMarkup = BotInlineMessage; botInlineMessageMediaVenue#8a86659c flags:# geo:GeoPoint title:string address:string provider:string venue_id:string venue_type:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage; botInlineMessageMediaContact#18d1cdc2 flags:# phone_number:string first_name:string last_name:string vcard:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage; +botInlineMessageMediaInvoice#354a9b09 flags:# shipping_address_requested:flags.1?true test:flags.3?true title:string description:string photo:flags.0?WebDocument currency:string total_amount:long reply_markup:flags.2?ReplyMarkup = BotInlineMessage; botInlineResult#11965f3a flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb:flags.4?WebDocument content:flags.5?WebDocument send_message:BotInlineMessage = BotInlineResult; botInlineMediaResult#17db940b flags:# id:string type:string photo:flags.0?Photo document:flags.1?Document title:flags.2?string description:flags.3?string send_message:BotInlineMessage = BotInlineResult; -messages.botResults#947ca848 flags:# gallery:flags.0?true query_id:long next_offset:flags.1?string switch_pm:flags.2?InlineBotSwitchPM results:Vector cache_time:int users:Vector = messages.BotResults; +messages.botResults#e021f2f6 flags:# gallery:flags.0?true query_id:long next_offset:flags.1?string switch_pm:flags.2?InlineBotSwitchPM switch_webview:flags.3?InlineBotWebView results:Vector cache_time:int users:Vector = messages.BotResults; exportedMessageLink#5dab1af4 link:string html:string = ExportedMessageLink; -messageFwdHeader#ec338270 flags:# from_id:flags.0?int from_name:flags.5?string date:int channel_id:flags.1?int channel_post:flags.2?int post_author:flags.3?string saved_from_peer:flags.4?Peer saved_from_msg_id:flags.4?int = MessageFwdHeader; +messageFwdHeader#5f777dce flags:# imported:flags.7?true from_id:flags.0?Peer from_name:flags.5?string date:int channel_post:flags.2?int post_author:flags.3?string saved_from_peer:flags.4?Peer saved_from_msg_id:flags.4?int psa_type:flags.6?string = MessageFwdHeader; auth.codeTypeSms#72a3158c = auth.CodeType; auth.codeTypeCall#741cd3e3 = auth.CodeType; auth.codeTypeFlashCall#226ccefb = auth.CodeType; +auth.codeTypeMissedCall#d61ad6ee = auth.CodeType; +auth.codeTypeFragmentSms#6ed998c = auth.CodeType; auth.sentCodeTypeApp#3dbb5986 length:int = auth.SentCodeType; auth.sentCodeTypeSms#c000bba2 length:int = auth.SentCodeType; auth.sentCodeTypeCall#5353e5a7 length:int = auth.SentCodeType; auth.sentCodeTypeFlashCall#ab03c6d9 pattern:string = auth.SentCodeType; +auth.sentCodeTypeMissedCall#82006484 prefix:string length:int = auth.SentCodeType; +auth.sentCodeTypeEmailCode#f450f59b flags:# apple_signin_allowed:flags.0?true google_signin_allowed:flags.1?true email_pattern:string length:int reset_available_period:flags.3?int reset_pending_date:flags.4?int = auth.SentCodeType; +auth.sentCodeTypeSetUpEmailRequired#a5491dea flags:# apple_signin_allowed:flags.0?true google_signin_allowed:flags.1?true = auth.SentCodeType; +auth.sentCodeTypeFragmentSms#d9565c39 url:string length:int = auth.SentCodeType; +auth.sentCodeTypeFirebaseSms#e57b1432 flags:# nonce:flags.0?bytes receipt:flags.1?string push_timeout:flags.1?int length:int = auth.SentCodeType; messages.botCallbackAnswer#36585ea4 flags:# alert:flags.1?true has_url:flags.3?true native_ui:flags.4?true message:flags.0?string url:flags.2?string cache_time:int = messages.BotCallbackAnswer; messages.messageEditData#26b5dde6 flags:# caption:flags.0?true = messages.MessageEditData; inputBotInlineMessageID#890c3d89 dc_id:int id:long access_hash:long = InputBotInlineMessageID; +inputBotInlineMessageID64#b6d915d7 dc_id:int owner_id:long id:int access_hash:long = InputBotInlineMessageID; inlineBotSwitchPM#3c20629f text:string start_param:string = InlineBotSwitchPM; @@ -628,6 +741,8 @@ topPeerCategoryCorrespondents#637b7ed = TopPeerCategory; topPeerCategoryGroups#bd17a14a = TopPeerCategory; topPeerCategoryChannels#161d9628 = TopPeerCategory; topPeerCategoryPhoneCalls#1e76a78c = TopPeerCategory; +topPeerCategoryForwardUsers#a8406ca9 = TopPeerCategory; +topPeerCategoryForwardChats#fbeec0f0 = TopPeerCategory; topPeerCategoryPeers#fb834291 category:TopPeerCategory count:int peers:Vector = TopPeerCategoryPeers; @@ -638,11 +753,11 @@ contacts.topPeersDisabled#b52c939d = contacts.TopPeers; draftMessageEmpty#1b0c841a flags:# date:flags.0?int = DraftMessage; draftMessage#fd8e711f flags:# no_webpage:flags.1?true reply_to_msg_id:flags.0?int message:string entities:flags.3?Vector date:int = DraftMessage; -messages.featuredStickersNotModified#4ede3cf = messages.FeaturedStickers; -messages.featuredStickers#f89d88e5 hash:int sets:Vector unread:Vector = messages.FeaturedStickers; +messages.featuredStickersNotModified#c6dc0c66 count:int = messages.FeaturedStickers; +messages.featuredStickers#be382906 flags:# premium:flags.0?true hash:long count:int sets:Vector unread:Vector = messages.FeaturedStickers; messages.recentStickersNotModified#b17f890 = messages.RecentStickers; -messages.recentStickers#22f3afb3 hash:int packs:Vector stickers:Vector dates:Vector = messages.RecentStickers; +messages.recentStickers#88d37c56 hash:long packs:Vector stickers:Vector dates:Vector = messages.RecentStickers; messages.archivedStickers#4fcba9c8 count:int sets:Vector = messages.ArchivedStickers; @@ -651,6 +766,8 @@ messages.stickerSetInstallResultArchive#35e410a8 sets:Vector stickerSetCovered#6410a5d2 set:StickerSet cover:Document = StickerSetCovered; stickerSetMultiCovered#3407e51b set:StickerSet covers:Vector = StickerSetCovered; +stickerSetFullCovered#40d13c0e set:StickerSet packs:Vector keywords:Vector documents:Vector = StickerSetCovered; +stickerSetNoCovered#77b15d1c set:StickerSet = StickerSetCovered; maskCoords#aed6dbb2 n:int x:double y:double zoom:double = MaskCoords; @@ -662,7 +779,7 @@ game#bdf9653b flags:# id:long access_hash:long short_name:string title:string de inputGameID#32c3e77 id:long access_hash:long = InputGame; inputGameShortName#c331e80a bot_id:InputUser short_name:string = InputGame; -highScore#58fffcd0 pos:int user_id:int score:int = HighScore; +highScore#73a379eb pos:int user_id:long score:int = HighScore; messages.highScores#9a3bfd99 scores:Vector users:Vector = messages.HighScores; @@ -722,7 +839,7 @@ dataJSON#7d748d04 data:string = DataJSON; labeledPrice#cb296bf8 label:string amount:long = LabeledPrice; -invoice#c30aa358 flags:# test:flags.0?true name_requested:flags.1?true phone_requested:flags.2?true email_requested:flags.3?true shipping_address_requested:flags.4?true flexible:flags.5?true phone_to_provider:flags.6?true email_to_provider:flags.7?true currency:string prices:Vector = Invoice; +invoice#3e85a91b flags:# test:flags.0?true name_requested:flags.1?true phone_requested:flags.2?true email_requested:flags.3?true shipping_address_requested:flags.4?true flexible:flags.5?true phone_to_provider:flags.6?true email_to_provider:flags.7?true recurring:flags.9?true currency:string prices:Vector max_tip_amount:flags.8?long suggested_tip_amounts:flags.8?Vector recurring_terms_url:flags.9?string = Invoice; paymentCharge#ea02c27e id:string provider_charge_id:string = PaymentCharge; @@ -739,43 +856,45 @@ inputWebDocument#9bed434d url:string size:int mime_type:string attributes:Vector inputWebFileLocation#c239d686 url:string access_hash:long = InputWebFileLocation; inputWebFileGeoPointLocation#9f2221c9 geo_point:InputGeoPoint access_hash:long w:int h:int zoom:int scale:int = InputWebFileLocation; +inputWebFileAudioAlbumThumbLocation#f46fe924 flags:# small:flags.2?true document:flags.0?InputDocument title:flags.1?string performer:flags.1?string = InputWebFileLocation; upload.webFile#21e753bc size:int mime_type:string file_type:storage.FileType mtime:int bytes:bytes = upload.WebFile; -payments.paymentForm#3f56aea3 flags:# can_save_credentials:flags.2?true password_missing:flags.3?true bot_id:int invoice:Invoice provider_id:int url:string native_provider:flags.4?string native_params:flags.4?DataJSON saved_info:flags.0?PaymentRequestedInfo saved_credentials:flags.1?PaymentSavedCredentials users:Vector = payments.PaymentForm; +payments.paymentForm#a0058751 flags:# can_save_credentials:flags.2?true password_missing:flags.3?true form_id:long bot_id:long title:string description:string photo:flags.5?WebDocument invoice:Invoice provider_id:long url:string native_provider:flags.4?string native_params:flags.4?DataJSON additional_methods:flags.6?Vector saved_info:flags.0?PaymentRequestedInfo saved_credentials:flags.1?Vector users:Vector = payments.PaymentForm; payments.validatedRequestedInfo#d1451883 flags:# id:flags.0?string shipping_options:flags.1?Vector = payments.ValidatedRequestedInfo; payments.paymentResult#4e5f810d updates:Updates = payments.PaymentResult; -payments.paymentVerficationNeeded#6b56b921 url:string = payments.PaymentResult; +payments.paymentVerificationNeeded#d8411139 url:string = payments.PaymentResult; -payments.paymentReceipt#500911e1 flags:# date:int bot_id:int invoice:Invoice provider_id:int info:flags.0?PaymentRequestedInfo shipping:flags.1?ShippingOption currency:string total_amount:long credentials_title:string users:Vector = payments.PaymentReceipt; +payments.paymentReceipt#70c4fe03 flags:# date:int bot_id:long provider_id:long title:string description:string photo:flags.2?WebDocument invoice:Invoice info:flags.0?PaymentRequestedInfo shipping:flags.1?ShippingOption tip_amount:flags.3?long currency:string total_amount:long credentials_title:string users:Vector = payments.PaymentReceipt; payments.savedInfo#fb8fe43c flags:# has_saved_credentials:flags.1?true saved_info:flags.0?PaymentRequestedInfo = payments.SavedInfo; inputPaymentCredentialsSaved#c10eb2cf id:string tmp_password:bytes = InputPaymentCredentials; inputPaymentCredentials#3417d728 flags:# save:flags.0?true data:DataJSON = InputPaymentCredentials; inputPaymentCredentialsApplePay#aa1c39f payment_data:DataJSON = InputPaymentCredentials; -inputPaymentCredentialsAndroidPay#ca05d50e payment_token:DataJSON google_transaction_id:string = InputPaymentCredentials; +inputPaymentCredentialsGooglePay#8ac32801 payment_token:DataJSON = InputPaymentCredentials; account.tmpPassword#db64fd34 tmp_password:bytes valid_until:int = account.TmpPassword; shippingOption#b6213cdf id:string title:string prices:Vector = ShippingOption; -inputStickerSetItem#ffa0a496 flags:# document:InputDocument emoji:string mask_coords:flags.0?MaskCoords = InputStickerSetItem; +inputStickerSetItem#32da9e9c flags:# document:InputDocument emoji:string mask_coords:flags.0?MaskCoords keywords:flags.1?string = InputStickerSetItem; inputPhoneCall#1e36fded id:long access_hash:long = InputPhoneCall; phoneCallEmpty#5366c915 id:long = PhoneCall; -phoneCallWaiting#1b8f4ad1 flags:# id:long access_hash:long date:int admin_id:int participant_id:int protocol:PhoneCallProtocol receive_date:flags.0?int = PhoneCall; -phoneCallRequested#83761ce4 id:long access_hash:long date:int admin_id:int participant_id:int g_a_hash:bytes protocol:PhoneCallProtocol = PhoneCall; -phoneCallAccepted#6d003d3f id:long access_hash:long date:int admin_id:int participant_id:int g_b:bytes protocol:PhoneCallProtocol = PhoneCall; -phoneCall#e6f9ddf3 flags:# p2p_allowed:flags.5?true id:long access_hash:long date:int admin_id:int participant_id:int g_a_or_b:bytes key_fingerprint:long protocol:PhoneCallProtocol connection:PhoneConnection alternative_connections:Vector start_date:int = PhoneCall; -phoneCallDiscarded#50ca4de1 flags:# need_rating:flags.2?true need_debug:flags.3?true id:long reason:flags.0?PhoneCallDiscardReason duration:flags.1?int = PhoneCall; +phoneCallWaiting#c5226f17 flags:# video:flags.6?true id:long access_hash:long date:int admin_id:long participant_id:long protocol:PhoneCallProtocol receive_date:flags.0?int = PhoneCall; +phoneCallRequested#14b0ed0c flags:# video:flags.6?true id:long access_hash:long date:int admin_id:long participant_id:long g_a_hash:bytes protocol:PhoneCallProtocol = PhoneCall; +phoneCallAccepted#3660c311 flags:# video:flags.6?true id:long access_hash:long date:int admin_id:long participant_id:long g_b:bytes protocol:PhoneCallProtocol = PhoneCall; +phoneCall#967f7c67 flags:# p2p_allowed:flags.5?true video:flags.6?true id:long access_hash:long date:int admin_id:long participant_id:long g_a_or_b:bytes key_fingerprint:long protocol:PhoneCallProtocol connections:Vector start_date:int = PhoneCall; +phoneCallDiscarded#50ca4de1 flags:# need_rating:flags.2?true need_debug:flags.3?true video:flags.6?true id:long reason:flags.0?PhoneCallDiscardReason duration:flags.1?int = PhoneCall; -phoneConnection#9d4c17c0 id:long ip:string ipv6:string port:int peer_tag:bytes = PhoneConnection; +phoneConnection#9cc123c7 flags:# tcp:flags.0?true id:long ip:string ipv6:string port:int peer_tag:bytes = PhoneConnection; +phoneConnectionWebrtc#635fe375 flags:# turn:flags.0?true stun:flags.1?true id:long ip:string ipv6:string port:int username:string password:string = PhoneConnection; -phoneCallProtocol#a2bb35cb flags:# udp_p2p:flags.0?true udp_reflector:flags.1?true min_layer:int max_layer:int = PhoneCallProtocol; +phoneCallProtocol#fc878fc8 flags:# udp_p2p:flags.0?true udp_reflector:flags.1?true min_layer:int max_layer:int library_versions:Vector = PhoneCallProtocol; phone.phoneCall#ec82e140 phone_call:PhoneCall users:Vector = phone.PhoneCall; @@ -797,7 +916,7 @@ langPackLanguage#eeca5ce3 flags:# official:flags.0?true rtl:flags.2?true beta:fl channelAdminLogEventActionChangeTitle#e6dfb825 prev_value:string new_value:string = ChannelAdminLogEventAction; channelAdminLogEventActionChangeAbout#55188a2e prev_value:string new_value:string = ChannelAdminLogEventAction; channelAdminLogEventActionChangeUsername#6a4afc38 prev_value:string new_value:string = ChannelAdminLogEventAction; -channelAdminLogEventActionChangePhoto#b82f55c3 prev_photo:ChatPhoto new_photo:ChatPhoto = ChannelAdminLogEventAction; +channelAdminLogEventActionChangePhoto#434bd2af prev_photo:Photo new_photo:Photo = ChannelAdminLogEventAction; channelAdminLogEventActionToggleInvites#1b7907ae new_value:Bool = ChannelAdminLogEventAction; channelAdminLogEventActionToggleSignatures#26ae0971 new_value:Bool = ChannelAdminLogEventAction; channelAdminLogEventActionUpdatePinned#e9e82c18 message:Message = ChannelAdminLogEventAction; @@ -812,21 +931,46 @@ channelAdminLogEventActionChangeStickerSet#b1c3caa7 prev_stickerset:InputSticker channelAdminLogEventActionTogglePreHistoryHidden#5f5c95f1 new_value:Bool = ChannelAdminLogEventAction; channelAdminLogEventActionDefaultBannedRights#2df5fc0a prev_banned_rights:ChatBannedRights new_banned_rights:ChatBannedRights = ChannelAdminLogEventAction; channelAdminLogEventActionStopPoll#8f079643 message:Message = ChannelAdminLogEventAction; - -channelAdminLogEvent#3b5a3e40 id:long date:int user_id:int action:ChannelAdminLogEventAction = ChannelAdminLogEvent; +channelAdminLogEventActionChangeLinkedChat#50c7ac8 prev_value:long new_value:long = ChannelAdminLogEventAction; +channelAdminLogEventActionChangeLocation#e6b76ae prev_value:ChannelLocation new_value:ChannelLocation = ChannelAdminLogEventAction; +channelAdminLogEventActionToggleSlowMode#53909779 prev_value:int new_value:int = ChannelAdminLogEventAction; +channelAdminLogEventActionStartGroupCall#23209745 call:InputGroupCall = ChannelAdminLogEventAction; +channelAdminLogEventActionDiscardGroupCall#db9f9140 call:InputGroupCall = ChannelAdminLogEventAction; +channelAdminLogEventActionParticipantMute#f92424d2 participant:GroupCallParticipant = ChannelAdminLogEventAction; +channelAdminLogEventActionParticipantUnmute#e64429c0 participant:GroupCallParticipant = ChannelAdminLogEventAction; +channelAdminLogEventActionToggleGroupCallSetting#56d6a247 join_muted:Bool = ChannelAdminLogEventAction; +channelAdminLogEventActionParticipantJoinByInvite#fe9fc158 flags:# via_chatlist:flags.0?true invite:ExportedChatInvite = ChannelAdminLogEventAction; +channelAdminLogEventActionExportedInviteDelete#5a50fca4 invite:ExportedChatInvite = ChannelAdminLogEventAction; +channelAdminLogEventActionExportedInviteRevoke#410a134e invite:ExportedChatInvite = ChannelAdminLogEventAction; +channelAdminLogEventActionExportedInviteEdit#e90ebb59 prev_invite:ExportedChatInvite new_invite:ExportedChatInvite = ChannelAdminLogEventAction; +channelAdminLogEventActionParticipantVolume#3e7f6847 participant:GroupCallParticipant = ChannelAdminLogEventAction; +channelAdminLogEventActionChangeHistoryTTL#6e941a38 prev_value:int new_value:int = ChannelAdminLogEventAction; +channelAdminLogEventActionParticipantJoinByRequest#afb6144a invite:ExportedChatInvite approved_by:long = ChannelAdminLogEventAction; +channelAdminLogEventActionToggleNoForwards#cb2ac766 new_value:Bool = ChannelAdminLogEventAction; +channelAdminLogEventActionSendMessage#278f2868 message:Message = ChannelAdminLogEventAction; +channelAdminLogEventActionChangeAvailableReactions#be4e0ef8 prev_value:ChatReactions new_value:ChatReactions = ChannelAdminLogEventAction; +channelAdminLogEventActionChangeUsernames#f04fb3a9 prev_value:Vector new_value:Vector = ChannelAdminLogEventAction; +channelAdminLogEventActionToggleForum#2cc6383 new_value:Bool = ChannelAdminLogEventAction; +channelAdminLogEventActionCreateTopic#58707d28 topic:ForumTopic = ChannelAdminLogEventAction; +channelAdminLogEventActionEditTopic#f06fe208 prev_topic:ForumTopic new_topic:ForumTopic = ChannelAdminLogEventAction; +channelAdminLogEventActionDeleteTopic#ae168909 topic:ForumTopic = ChannelAdminLogEventAction; +channelAdminLogEventActionPinTopic#5d8d353b flags:# prev_topic:flags.0?ForumTopic new_topic:flags.1?ForumTopic = ChannelAdminLogEventAction; +channelAdminLogEventActionToggleAntiSpam#64f36dfc new_value:Bool = ChannelAdminLogEventAction; + +channelAdminLogEvent#1fad68cd id:long date:int user_id:long action:ChannelAdminLogEventAction = ChannelAdminLogEvent; channels.adminLogResults#ed8af74d events:Vector chats:Vector users:Vector = channels.AdminLogResults; -channelAdminLogEventsFilter#ea107ae4 flags:# join:flags.0?true leave:flags.1?true invite:flags.2?true ban:flags.3?true unban:flags.4?true kick:flags.5?true unkick:flags.6?true promote:flags.7?true demote:flags.8?true info:flags.9?true settings:flags.10?true pinned:flags.11?true edit:flags.12?true delete:flags.13?true = ChannelAdminLogEventsFilter; +channelAdminLogEventsFilter#ea107ae4 flags:# join:flags.0?true leave:flags.1?true invite:flags.2?true ban:flags.3?true unban:flags.4?true kick:flags.5?true unkick:flags.6?true promote:flags.7?true demote:flags.8?true info:flags.9?true settings:flags.10?true pinned:flags.11?true edit:flags.12?true delete:flags.13?true group_call:flags.14?true invites:flags.15?true send:flags.16?true forums:flags.17?true = ChannelAdminLogEventsFilter; popularContact#5ce14175 client_id:long importers:int = PopularContact; messages.favedStickersNotModified#9e8fa6d3 = messages.FavedStickers; -messages.favedStickers#f37f2f16 hash:int packs:Vector stickers:Vector = messages.FavedStickers; +messages.favedStickers#2cb51097 hash:long packs:Vector stickers:Vector = messages.FavedStickers; recentMeUrlUnknown#46e1d13d url:string = RecentMeUrl; -recentMeUrlUser#8dbc3336 url:string user_id:int = RecentMeUrl; -recentMeUrlChat#a01b22f9 url:string chat_id:int = RecentMeUrl; +recentMeUrlUser#b92c09e2 url:string user_id:long = RecentMeUrl; +recentMeUrlChat#b2da71d2 url:string chat_id:long = RecentMeUrl; recentMeUrlChatInvite#eb49081d url:string chat_invite:ChatInvite = RecentMeUrl; recentMeUrlStickerSet#bc0a57dc url:string set:StickerSetCovered = RecentMeUrl; @@ -834,28 +978,28 @@ help.recentMeUrls#e0310d7 urls:Vector chats:Vector users:Vect inputSingleMedia#1cc6e91f flags:# media:InputMedia random_id:long message:string entities:flags.0?Vector = InputSingleMedia; -webAuthorization#cac943f2 hash:long bot_id:int domain:string browser:string platform:string date_created:int date_active:int ip:string region:string = WebAuthorization; +webAuthorization#a6f8f452 hash:long bot_id:long domain:string browser:string platform:string date_created:int date_active:int ip:string region:string = WebAuthorization; account.webAuthorizations#ed56c9fc authorizations:Vector users:Vector = account.WebAuthorizations; inputMessageID#a676a322 id:int = InputMessage; inputMessageReplyTo#bad88395 id:int = InputMessage; inputMessagePinned#86872538 = InputMessage; +inputMessageCallbackQuery#acfa1a7e id:int query_id:long = InputMessage; inputDialogPeer#fcaafeb7 peer:InputPeer = InputDialogPeer; +inputDialogPeerFolder#64600527 folder_id:int = InputDialogPeer; dialogPeer#e56dbf05 peer:Peer = DialogPeer; +dialogPeerFolder#514519e2 folder_id:int = DialogPeer; messages.foundStickerSetsNotModified#d54b65d = messages.FoundStickerSets; -messages.foundStickerSets#5108d648 hash:int sets:Vector = messages.FoundStickerSets; +messages.foundStickerSets#8af09dd2 hash:long sets:Vector = messages.FoundStickerSets; -fileHash#6242c773 offset:int limit:int hash:bytes = FileHash; +fileHash#f39b035c offset:long limit:int hash:bytes = FileHash; inputClientProxy#75588b3f address:string port:int = InputClientProxy; -help.proxyDataEmpty#e09e1fb8 expires:int = help.ProxyData; -help.proxyDataPromo#2bf7ee23 expires:int peer:Peer chats:Vector users:Vector = help.ProxyData; - help.termsOfServiceUpdateEmpty#e3309f7f expires:int = help.TermsOfServiceUpdate; help.termsOfServiceUpdate#28ecf961 expires:int terms_of_service:help.TermsOfService = help.TermsOfServiceUpdate; @@ -863,7 +1007,7 @@ inputSecureFileUploaded#3334b0f0 id:long parts:int md5_checksum:string file_hash inputSecureFile#5367e5be id:long access_hash:long = InputSecureFile; secureFileEmpty#64199744 = SecureFile; -secureFile#e0277a62 id:long access_hash:long size:int dc_id:int date:int file_hash:bytes secret:bytes = SecureFile; +secureFile#7d09c27e id:long access_hash:long size:long dc_id:int date:int file_hash:bytes secret:bytes = SecureFile; secureData#8aeabec3 data:bytes data_hash:bytes secret:bytes = SecureData; @@ -956,7 +1100,7 @@ pageListOrderedItemBlocks#98dd8936 num:string blocks:Vector = PageLis pageRelatedArticle#b390dc08 flags:# url:string webpage_id:long title:flags.0?string description:flags.1?string photo_id:flags.2?long author:flags.3?string published_date:flags.4?int = PageRelatedArticle; -page#ae891bec flags:# part:flags.0?true rtl:flags.1?true v2:flags.2?true url:string blocks:Vector photos:Vector documents:Vector = Page; +page#98657f0d flags:# part:flags.0?true rtl:flags.1?true v2:flags.2?true url:string blocks:Vector photos:Vector documents:Vector views:flags.3?int = Page; help.supportName#8c05f1c9 name:string = help.SupportName; @@ -965,31 +1109,32 @@ help.userInfo#1eb3758 message:string entities:Vector author:strin pollAnswer#6ca9c2e9 text:string option:bytes = PollAnswer; -poll#d5529d06 id:long flags:# closed:flags.0?true question:string answers:Vector = Poll; +poll#86e18161 id:long flags:# closed:flags.0?true public_voters:flags.1?true multiple_choice:flags.2?true quiz:flags.3?true question:string answers:Vector close_period:flags.4?int close_date:flags.5?int = Poll; -pollAnswerVoters#3b6ddad2 flags:# chosen:flags.0?true option:bytes voters:int = PollAnswerVoters; +pollAnswerVoters#3b6ddad2 flags:# chosen:flags.0?true correct:flags.1?true option:bytes voters:int = PollAnswerVoters; -pollResults#5755785a flags:# min:flags.0?true results:flags.1?Vector total_voters:flags.2?int = PollResults; +pollResults#dcb82ea3 flags:# min:flags.0?true results:flags.1?Vector total_voters:flags.2?int recent_voters:flags.3?Vector solution:flags.4?string solution_entities:flags.4?Vector = PollResults; chatOnlines#f041e250 onlines:int = ChatOnlines; statsURL#47a971e0 url:string = StatsURL; -chatAdminRights#5fb224d5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true pin_messages:flags.7?true add_admins:flags.9?true = ChatAdminRights; +chatAdminRights#5fb224d5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true pin_messages:flags.7?true add_admins:flags.9?true anonymous:flags.10?true manage_call:flags.11?true other:flags.12?true manage_topics:flags.13?true = ChatAdminRights; -chatBannedRights#9f120418 flags:# view_messages:flags.0?true send_messages:flags.1?true send_media:flags.2?true send_stickers:flags.3?true send_gifs:flags.4?true send_games:flags.5?true send_inline:flags.6?true embed_links:flags.7?true send_polls:flags.8?true change_info:flags.10?true invite_users:flags.15?true pin_messages:flags.17?true until_date:int = ChatBannedRights; +chatBannedRights#9f120418 flags:# view_messages:flags.0?true send_messages:flags.1?true send_media:flags.2?true send_stickers:flags.3?true send_gifs:flags.4?true send_games:flags.5?true send_inline:flags.6?true embed_links:flags.7?true send_polls:flags.8?true change_info:flags.10?true invite_users:flags.15?true pin_messages:flags.17?true manage_topics:flags.18?true send_photos:flags.19?true send_videos:flags.20?true send_roundvideos:flags.21?true send_audios:flags.22?true send_voices:flags.23?true send_docs:flags.24?true send_plain:flags.25?true until_date:int = ChatBannedRights; inputWallPaper#e630b979 id:long access_hash:long = InputWallPaper; inputWallPaperSlug#72091c80 slug:string = InputWallPaper; +inputWallPaperNoFile#967a462e id:long = InputWallPaper; account.wallPapersNotModified#1c199183 = account.WallPapers; -account.wallPapers#702b65a9 hash:int wallpapers:Vector = account.WallPapers; +account.wallPapers#cdc3858c hash:long wallpapers:Vector = account.WallPapers; -codeSettings#302f59f3 flags:# allow_flashcall:flags.0?true current_number:flags.1?true app_hash_persistent:flags.2?true app_hash:flags.3?string = CodeSettings; +codeSettings#ad253d78 flags:# allow_flashcall:flags.0?true current_number:flags.1?true allow_app_hash:flags.4?true allow_missed_call:flags.5?true allow_firebase:flags.7?true logout_tokens:flags.6?Vector token:flags.8?string app_sandbox:flags.8?Bool = CodeSettings; -wallPaperSettings#a12f40b8 flags:# blur:flags.1?true motion:flags.2?true background_color:flags.0?int intensity:flags.3?int = WallPaperSettings; +wallPaperSettings#1dc1bca4 flags:# blur:flags.1?true motion:flags.2?true background_color:flags.0?int second_background_color:flags.4?int third_background_color:flags.5?int fourth_background_color:flags.6?int intensity:flags.3?int rotation:flags.4?int = WallPaperSettings; -autoDownloadSettings#d246fd47 flags:# disabled:flags.0?true video_preload_large:flags.1?true audio_preload_next:flags.2?true phonecalls_less_data:flags.3?true photo_size_max:int video_size_max:int file_size_max:int = AutoDownloadSettings; +autoDownloadSettings#8efab953 flags:# disabled:flags.0?true video_preload_large:flags.1?true audio_preload_next:flags.2?true phonecalls_less_data:flags.3?true photo_size_max:int video_size_max:long file_size_max:long video_upload_maxbitrate:int = AutoDownloadSettings; account.autoDownloadSettings#63cacf26 low:AutoDownloadSettings medium:AutoDownloadSettings high:AutoDownloadSettings = account.AutoDownloadSettings; @@ -1000,46 +1145,429 @@ emojiKeywordsDifference#5cc761bd lang_code:string from_version:int version:int k emojiURL#a575739d url:string = EmojiURL; +emojiLanguage#b3fb5361 lang_code:string = EmojiLanguage; + +folder#ff544e65 flags:# autofill_new_broadcasts:flags.0?true autofill_public_groups:flags.1?true autofill_new_correspondents:flags.2?true id:int title:string photo:flags.3?ChatPhoto = Folder; + +inputFolderPeer#fbd2c296 peer:InputPeer folder_id:int = InputFolderPeer; + +folderPeer#e9baa668 peer:Peer folder_id:int = FolderPeer; + +messages.searchCounter#e844ebff flags:# inexact:flags.1?true filter:MessagesFilter count:int = messages.SearchCounter; + +urlAuthResultRequest#92d33a0e flags:# request_write_access:flags.0?true bot:User domain:string = UrlAuthResult; +urlAuthResultAccepted#8f8c0e4e url:string = UrlAuthResult; +urlAuthResultDefault#a9d6db1f = UrlAuthResult; + +channelLocationEmpty#bfb5ad8b = ChannelLocation; +channelLocation#209b82db geo_point:GeoPoint address:string = ChannelLocation; + +peerLocated#ca461b5d peer:Peer expires:int distance:int = PeerLocated; +peerSelfLocated#f8ec284b expires:int = PeerLocated; + +restrictionReason#d072acb4 platform:string reason:string text:string = RestrictionReason; + +inputTheme#3c5693e9 id:long access_hash:long = InputTheme; +inputThemeSlug#f5890df1 slug:string = InputTheme; + +theme#a00e67d6 flags:# creator:flags.0?true default:flags.1?true for_chat:flags.5?true id:long access_hash:long slug:string title:string document:flags.2?Document settings:flags.3?Vector emoticon:flags.6?string installs_count:flags.4?int = Theme; + +account.themesNotModified#f41eb622 = account.Themes; +account.themes#9a3d8c6d hash:long themes:Vector = account.Themes; + +auth.loginToken#629f1980 expires:int token:bytes = auth.LoginToken; +auth.loginTokenMigrateTo#68e9916 dc_id:int token:bytes = auth.LoginToken; +auth.loginTokenSuccess#390d5c5e authorization:auth.Authorization = auth.LoginToken; + +account.contentSettings#57e28221 flags:# sensitive_enabled:flags.0?true sensitive_can_change:flags.1?true = account.ContentSettings; + +messages.inactiveChats#a927fec5 dates:Vector chats:Vector users:Vector = messages.InactiveChats; + +baseThemeClassic#c3a12462 = BaseTheme; +baseThemeDay#fbd81688 = BaseTheme; +baseThemeNight#b7b31ea8 = BaseTheme; +baseThemeTinted#6d5f77ee = BaseTheme; +baseThemeArctic#5b11125a = BaseTheme; + +inputThemeSettings#8fde504f flags:# message_colors_animated:flags.2?true base_theme:BaseTheme accent_color:int outbox_accent_color:flags.3?int message_colors:flags.0?Vector wallpaper:flags.1?InputWallPaper wallpaper_settings:flags.1?WallPaperSettings = InputThemeSettings; + +themeSettings#fa58b6d4 flags:# message_colors_animated:flags.2?true base_theme:BaseTheme accent_color:int outbox_accent_color:flags.3?int message_colors:flags.0?Vector wallpaper:flags.1?WallPaper = ThemeSettings; + +webPageAttributeTheme#54b56617 flags:# documents:flags.0?Vector settings:flags.1?ThemeSettings = WebPageAttribute; + +messageUserVote#34d247b4 user_id:long option:bytes date:int = MessageUserVote; +messageUserVoteInputOption#3ca5b0ec user_id:long date:int = MessageUserVote; +messageUserVoteMultiple#8a65e557 user_id:long options:Vector date:int = MessageUserVote; + +messages.votesList#823f649 flags:# count:int votes:Vector users:Vector next_offset:flags.0?string = messages.VotesList; + +bankCardOpenUrl#f568028a url:string name:string = BankCardOpenUrl; + +payments.bankCardData#3e24e573 title:string open_urls:Vector = payments.BankCardData; + +dialogFilter#7438f7e8 flags:# contacts:flags.0?true non_contacts:flags.1?true groups:flags.2?true broadcasts:flags.3?true bots:flags.4?true exclude_muted:flags.11?true exclude_read:flags.12?true exclude_archived:flags.13?true id:int title:string emoticon:flags.25?string pinned_peers:Vector include_peers:Vector exclude_peers:Vector = DialogFilter; +dialogFilterDefault#363293ae = DialogFilter; +dialogFilterChatlist#d64a04a8 flags:# has_my_invites:flags.26?true id:int title:string emoticon:flags.25?string pinned_peers:Vector include_peers:Vector = DialogFilter; + +dialogFilterSuggested#77744d4a filter:DialogFilter description:string = DialogFilterSuggested; + +statsDateRangeDays#b637edaf min_date:int max_date:int = StatsDateRangeDays; + +statsAbsValueAndPrev#cb43acde current:double previous:double = StatsAbsValueAndPrev; + +statsPercentValue#cbce2fe0 part:double total:double = StatsPercentValue; + +statsGraphAsync#4a27eb2d token:string = StatsGraph; +statsGraphError#bedc9822 error:string = StatsGraph; +statsGraph#8ea464b6 flags:# json:DataJSON zoom_token:flags.0?string = StatsGraph; + +messageInteractionCounters#ad4fc9bd msg_id:int views:int forwards:int = MessageInteractionCounters; + +stats.broadcastStats#bdf78394 period:StatsDateRangeDays followers:StatsAbsValueAndPrev views_per_post:StatsAbsValueAndPrev shares_per_post:StatsAbsValueAndPrev enabled_notifications:StatsPercentValue growth_graph:StatsGraph followers_graph:StatsGraph mute_graph:StatsGraph top_hours_graph:StatsGraph interactions_graph:StatsGraph iv_interactions_graph:StatsGraph views_by_source_graph:StatsGraph new_followers_by_source_graph:StatsGraph languages_graph:StatsGraph recent_message_interactions:Vector = stats.BroadcastStats; + +help.promoDataEmpty#98f6ac75 expires:int = help.PromoData; +help.promoData#8c39793f flags:# proxy:flags.0?true expires:int peer:Peer chats:Vector users:Vector psa_type:flags.1?string psa_message:flags.2?string = help.PromoData; + +videoSize#de33b094 flags:# type:string w:int h:int size:int video_start_ts:flags.0?double = VideoSize; +videoSizeEmojiMarkup#f85c413c emoji_id:long background_colors:Vector = VideoSize; +videoSizeStickerMarkup#da082fe stickerset:InputStickerSet sticker_id:long background_colors:Vector = VideoSize; + +statsGroupTopPoster#9d04af9b user_id:long messages:int avg_chars:int = StatsGroupTopPoster; + +statsGroupTopAdmin#d7584c87 user_id:long deleted:int kicked:int banned:int = StatsGroupTopAdmin; + +statsGroupTopInviter#535f779d user_id:long invitations:int = StatsGroupTopInviter; + +stats.megagroupStats#ef7ff916 period:StatsDateRangeDays members:StatsAbsValueAndPrev messages:StatsAbsValueAndPrev viewers:StatsAbsValueAndPrev posters:StatsAbsValueAndPrev growth_graph:StatsGraph members_graph:StatsGraph new_members_by_source_graph:StatsGraph languages_graph:StatsGraph messages_graph:StatsGraph actions_graph:StatsGraph top_hours_graph:StatsGraph weekdays_graph:StatsGraph top_posters:Vector top_admins:Vector top_inviters:Vector users:Vector = stats.MegagroupStats; + +globalPrivacySettings#bea2f424 flags:# archive_and_mute_new_noncontact_peers:flags.0?Bool = GlobalPrivacySettings; + +help.countryCode#4203c5ef flags:# country_code:string prefixes:flags.0?Vector patterns:flags.1?Vector = help.CountryCode; + +help.country#c3878e23 flags:# hidden:flags.0?true iso2:string default_name:string name:flags.1?string country_codes:Vector = help.Country; + +help.countriesListNotModified#93cc1f32 = help.CountriesList; +help.countriesList#87d0759e countries:Vector hash:int = help.CountriesList; + +messageViews#455b853d flags:# views:flags.0?int forwards:flags.1?int replies:flags.2?MessageReplies = MessageViews; + +messages.messageViews#b6c4f543 views:Vector chats:Vector users:Vector = messages.MessageViews; + +messages.discussionMessage#a6341782 flags:# messages:Vector max_id:flags.0?int read_inbox_max_id:flags.1?int read_outbox_max_id:flags.2?int unread_count:int chats:Vector users:Vector = messages.DiscussionMessage; + +messageReplyHeader#a6d57763 flags:# reply_to_scheduled:flags.2?true forum_topic:flags.3?true reply_to_msg_id:int reply_to_peer_id:flags.0?Peer reply_to_top_id:flags.1?int = MessageReplyHeader; + +messageReplies#83d60fc2 flags:# comments:flags.0?true replies:int replies_pts:int recent_repliers:flags.1?Vector channel_id:flags.0?long max_id:flags.2?int read_max_id:flags.3?int = MessageReplies; + +peerBlocked#e8fd8014 peer_id:Peer date:int = PeerBlocked; + +stats.messageStats#8999f295 views_graph:StatsGraph = stats.MessageStats; + +groupCallDiscarded#7780bcb4 id:long access_hash:long duration:int = GroupCall; +groupCall#d597650c flags:# join_muted:flags.1?true can_change_join_muted:flags.2?true join_date_asc:flags.6?true schedule_start_subscribed:flags.8?true can_start_video:flags.9?true record_video_active:flags.11?true rtmp_stream:flags.12?true listeners_hidden:flags.13?true id:long access_hash:long participants_count:int title:flags.3?string stream_dc_id:flags.4?int record_start_date:flags.5?int schedule_date:flags.7?int unmuted_video_count:flags.10?int unmuted_video_limit:int version:int = GroupCall; + +inputGroupCall#d8aa840f id:long access_hash:long = InputGroupCall; + +groupCallParticipant#eba636fe flags:# muted:flags.0?true left:flags.1?true can_self_unmute:flags.2?true just_joined:flags.4?true versioned:flags.5?true min:flags.8?true muted_by_you:flags.9?true volume_by_admin:flags.10?true self:flags.12?true video_joined:flags.15?true peer:Peer date:int active_date:flags.3?int source:int volume:flags.7?int about:flags.11?string raise_hand_rating:flags.13?long video:flags.6?GroupCallParticipantVideo presentation:flags.14?GroupCallParticipantVideo = GroupCallParticipant; + +phone.groupCall#9e727aad call:GroupCall participants:Vector participants_next_offset:string chats:Vector users:Vector = phone.GroupCall; + +phone.groupParticipants#f47751b6 count:int participants:Vector next_offset:string chats:Vector users:Vector version:int = phone.GroupParticipants; + +inlineQueryPeerTypeSameBotPM#3081ed9d = InlineQueryPeerType; +inlineQueryPeerTypePM#833c0fac = InlineQueryPeerType; +inlineQueryPeerTypeChat#d766c50a = InlineQueryPeerType; +inlineQueryPeerTypeMegagroup#5ec4be43 = InlineQueryPeerType; +inlineQueryPeerTypeBroadcast#6334ee9a = InlineQueryPeerType; +inlineQueryPeerTypeBotPM#e3b2d0c = InlineQueryPeerType; + +messages.historyImport#1662af0b id:long = messages.HistoryImport; + +messages.historyImportParsed#5e0fb7b9 flags:# pm:flags.0?true group:flags.1?true title:flags.2?string = messages.HistoryImportParsed; + +messages.affectedFoundMessages#ef8d3e6c pts:int pts_count:int offset:int messages:Vector = messages.AffectedFoundMessages; + +chatInviteImporter#8c5adfd9 flags:# requested:flags.0?true via_chatlist:flags.3?true user_id:long date:int about:flags.2?string approved_by:flags.1?long = ChatInviteImporter; + +messages.exportedChatInvites#bdc62dcc count:int invites:Vector users:Vector = messages.ExportedChatInvites; + +messages.exportedChatInvite#1871be50 invite:ExportedChatInvite users:Vector = messages.ExportedChatInvite; +messages.exportedChatInviteReplaced#222600ef invite:ExportedChatInvite new_invite:ExportedChatInvite users:Vector = messages.ExportedChatInvite; + +messages.chatInviteImporters#81b6b00a count:int importers:Vector users:Vector = messages.ChatInviteImporters; + +chatAdminWithInvites#f2ecef23 admin_id:long invites_count:int revoked_invites_count:int = ChatAdminWithInvites; + +messages.chatAdminsWithInvites#b69b72d7 admins:Vector users:Vector = messages.ChatAdminsWithInvites; + +messages.checkedHistoryImportPeer#a24de717 confirm_text:string = messages.CheckedHistoryImportPeer; + +phone.joinAsPeers#afe5623f peers:Vector chats:Vector users:Vector = phone.JoinAsPeers; + +phone.exportedGroupCallInvite#204bd158 link:string = phone.ExportedGroupCallInvite; + +groupCallParticipantVideoSourceGroup#dcb118b7 semantics:string sources:Vector = GroupCallParticipantVideoSourceGroup; + +groupCallParticipantVideo#67753ac8 flags:# paused:flags.0?true endpoint:string source_groups:Vector audio_source:flags.1?int = GroupCallParticipantVideo; + +stickers.suggestedShortName#85fea03f short_name:string = stickers.SuggestedShortName; + +botCommandScopeDefault#2f6cb2ab = BotCommandScope; +botCommandScopeUsers#3c4f04d8 = BotCommandScope; +botCommandScopeChats#6fe1a881 = BotCommandScope; +botCommandScopeChatAdmins#b9aa606a = BotCommandScope; +botCommandScopePeer#db9d897d peer:InputPeer = BotCommandScope; +botCommandScopePeerAdmins#3fd863d1 peer:InputPeer = BotCommandScope; +botCommandScopePeerUser#a1321f3 peer:InputPeer user_id:InputUser = BotCommandScope; + +account.resetPasswordFailedWait#e3779861 retry_date:int = account.ResetPasswordResult; +account.resetPasswordRequestedWait#e9effc7d until_date:int = account.ResetPasswordResult; +account.resetPasswordOk#e926d63e = account.ResetPasswordResult; + +sponsoredMessage#fc25b828 flags:# recommended:flags.5?true show_peer_photo:flags.6?true random_id:bytes from_id:flags.3?Peer chat_invite:flags.4?ChatInvite chat_invite_hash:flags.4?string channel_post:flags.2?int start_param:flags.0?string message:string entities:flags.1?Vector sponsor_info:flags.7?string additional_info:flags.8?string = SponsoredMessage; + +messages.sponsoredMessages#c9ee1d87 flags:# posts_between:flags.0?int messages:Vector chats:Vector users:Vector = messages.SponsoredMessages; +messages.sponsoredMessagesEmpty#1839490f = messages.SponsoredMessages; + +searchResultsCalendarPeriod#c9b0539f date:int min_msg_id:int max_msg_id:int count:int = SearchResultsCalendarPeriod; + +messages.searchResultsCalendar#147ee23c flags:# inexact:flags.0?true count:int min_date:int min_msg_id:int offset_id_offset:flags.1?int periods:Vector messages:Vector chats:Vector users:Vector = messages.SearchResultsCalendar; + +searchResultPosition#7f648b67 msg_id:int date:int offset:int = SearchResultsPosition; + +messages.searchResultsPositions#53b22baf count:int positions:Vector = messages.SearchResultsPositions; + +channels.sendAsPeers#f496b0c6 peers:Vector chats:Vector users:Vector = channels.SendAsPeers; + +users.userFull#3b6d152e full_user:UserFull chats:Vector users:Vector = users.UserFull; + +messages.peerSettings#6880b94d settings:PeerSettings chats:Vector users:Vector = messages.PeerSettings; + +auth.loggedOut#c3a2835f flags:# future_auth_token:flags.0?bytes = auth.LoggedOut; + +reactionCount#a3d1cb80 flags:# chosen_order:flags.0?int reaction:Reaction count:int = ReactionCount; + +messageReactions#4f2b9479 flags:# min:flags.0?true can_see_list:flags.2?true results:Vector recent_reactions:flags.1?Vector = MessageReactions; + +messages.messageReactionsList#31bd492d flags:# count:int reactions:Vector chats:Vector users:Vector next_offset:flags.0?string = messages.MessageReactionsList; + +availableReaction#c077ec01 flags:# inactive:flags.0?true premium:flags.2?true reaction:string title:string static_icon:Document appear_animation:Document select_animation:Document activate_animation:Document effect_animation:Document around_animation:flags.1?Document center_icon:flags.1?Document = AvailableReaction; + +messages.availableReactionsNotModified#9f071957 = messages.AvailableReactions; +messages.availableReactions#768e3aad hash:int reactions:Vector = messages.AvailableReactions; + +messagePeerReaction#8c79b63c flags:# big:flags.0?true unread:flags.1?true peer_id:Peer date:int reaction:Reaction = MessagePeerReaction; + +groupCallStreamChannel#80eb48af channel:int scale:int last_timestamp_ms:long = GroupCallStreamChannel; + +phone.groupCallStreamChannels#d0e482b2 channels:Vector = phone.GroupCallStreamChannels; + +phone.groupCallStreamRtmpUrl#2dbf3432 url:string key:string = phone.GroupCallStreamRtmpUrl; + +attachMenuBotIconColor#4576f3f0 name:string color:int = AttachMenuBotIconColor; + +attachMenuBotIcon#b2a7386b flags:# name:string icon:Document colors:flags.0?Vector = AttachMenuBotIcon; + +attachMenuBot#c8aa2cd2 flags:# inactive:flags.0?true has_settings:flags.1?true request_write_access:flags.2?true bot_id:long short_name:string peer_types:Vector icons:Vector = AttachMenuBot; + +attachMenuBotsNotModified#f1d88a5c = AttachMenuBots; +attachMenuBots#3c4301c0 hash:long bots:Vector users:Vector = AttachMenuBots; + +attachMenuBotsBot#93bf667f bot:AttachMenuBot users:Vector = AttachMenuBotsBot; + +webViewResultUrl#c14557c query_id:long url:string = WebViewResult; + +simpleWebViewResultUrl#882f76bb url:string = SimpleWebViewResult; + +webViewMessageSent#c94511c flags:# msg_id:flags.0?InputBotInlineMessageID = WebViewMessageSent; + +botMenuButtonDefault#7533a588 = BotMenuButton; +botMenuButtonCommands#4258c205 = BotMenuButton; +botMenuButton#c7b57ce6 text:string url:string = BotMenuButton; + +account.savedRingtonesNotModified#fbf6e8b1 = account.SavedRingtones; +account.savedRingtones#c1e92cc5 hash:long ringtones:Vector = account.SavedRingtones; + +notificationSoundDefault#97e8bebe = NotificationSound; +notificationSoundNone#6f0c34df = NotificationSound; +notificationSoundLocal#830b9ae4 title:string data:string = NotificationSound; +notificationSoundRingtone#ff6c8049 id:long = NotificationSound; + +account.savedRingtone#b7263f6d = account.SavedRingtone; +account.savedRingtoneConverted#1f307eb7 document:Document = account.SavedRingtone; + +attachMenuPeerTypeSameBotPM#7d6be90e = AttachMenuPeerType; +attachMenuPeerTypeBotPM#c32bfa1a = AttachMenuPeerType; +attachMenuPeerTypePM#f146d31f = AttachMenuPeerType; +attachMenuPeerTypeChat#509113f = AttachMenuPeerType; +attachMenuPeerTypeBroadcast#7bfbdefc = AttachMenuPeerType; + +inputInvoiceMessage#c5b56859 peer:InputPeer msg_id:int = InputInvoice; +inputInvoiceSlug#c326caef slug:string = InputInvoice; + +payments.exportedInvoice#aed0cbd9 url:string = payments.ExportedInvoice; + +messages.transcribedAudio#93752c52 flags:# pending:flags.0?true transcription_id:long text:string = messages.TranscribedAudio; + +help.premiumPromo#5334759c status_text:string status_entities:Vector video_sections:Vector videos:Vector period_options:Vector users:Vector = help.PremiumPromo; + +inputStorePaymentPremiumSubscription#a6751e66 flags:# restore:flags.0?true upgrade:flags.1?true = InputStorePaymentPurpose; +inputStorePaymentGiftPremium#616f7fe8 user_id:InputUser currency:string amount:long = InputStorePaymentPurpose; + +premiumGiftOption#74c34319 flags:# months:int currency:string amount:long bot_url:string store_product:flags.0?string = PremiumGiftOption; + +paymentFormMethod#88f8f21b url:string title:string = PaymentFormMethod; + +emojiStatusEmpty#2de11aae = EmojiStatus; +emojiStatus#929b619d document_id:long = EmojiStatus; +emojiStatusUntil#fa30a8c7 document_id:long until:int = EmojiStatus; + +account.emojiStatusesNotModified#d08ce645 = account.EmojiStatuses; +account.emojiStatuses#90c467d1 hash:long statuses:Vector = account.EmojiStatuses; + +reactionEmpty#79f5d419 = Reaction; +reactionEmoji#1b2286b8 emoticon:string = Reaction; +reactionCustomEmoji#8935fc73 document_id:long = Reaction; + +chatReactionsNone#eafc32bc = ChatReactions; +chatReactionsAll#52928bca flags:# allow_custom:flags.0?true = ChatReactions; +chatReactionsSome#661d4037 reactions:Vector = ChatReactions; + +messages.reactionsNotModified#b06fdbdf = messages.Reactions; +messages.reactions#eafdf716 hash:long reactions:Vector = messages.Reactions; + +emailVerifyPurposeLoginSetup#4345be73 phone_number:string phone_code_hash:string = EmailVerifyPurpose; +emailVerifyPurposeLoginChange#527d22eb = EmailVerifyPurpose; +emailVerifyPurposePassport#bbf51685 = EmailVerifyPurpose; + +emailVerificationCode#922e55a9 code:string = EmailVerification; +emailVerificationGoogle#db909ec2 token:string = EmailVerification; +emailVerificationApple#96d074fd token:string = EmailVerification; + +account.emailVerified#2b96cd1b email:string = account.EmailVerified; +account.emailVerifiedLogin#e1bb0d61 email:string sent_code:auth.SentCode = account.EmailVerified; + +premiumSubscriptionOption#5f2d1df2 flags:# current:flags.1?true can_purchase_upgrade:flags.2?true transaction:flags.3?string months:int currency:string amount:long bot_url:string store_product:flags.0?string = PremiumSubscriptionOption; + +sendAsPeer#b81c7034 flags:# premium_required:flags.0?true peer:Peer = SendAsPeer; + +messageExtendedMediaPreview#ad628cc8 flags:# w:flags.0?int h:flags.0?int thumb:flags.1?PhotoSize video_duration:flags.2?int = MessageExtendedMedia; +messageExtendedMedia#ee479c64 media:MessageMedia = MessageExtendedMedia; + +stickerKeyword#fcfeb29c document_id:long keyword:Vector = StickerKeyword; + +username#b4073647 flags:# editable:flags.0?true active:flags.1?true username:string = Username; + +forumTopicDeleted#23f109b id:int = ForumTopic; +forumTopic#71701da9 flags:# my:flags.1?true closed:flags.2?true pinned:flags.3?true short:flags.5?true hidden:flags.6?true id:int date:int title:string icon_color:int icon_emoji_id:flags.0?long top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int from_id:Peer notify_settings:PeerNotifySettings draft:flags.4?DraftMessage = ForumTopic; + +messages.forumTopics#367617d3 flags:# order_by_create_date:flags.0?true count:int topics:Vector messages:Vector chats:Vector users:Vector pts:int = messages.ForumTopics; + +defaultHistoryTTL#43b46b20 period:int = DefaultHistoryTTL; + +exportedContactToken#41bf109b url:string expires:int = ExportedContactToken; + +requestPeerTypeUser#5f3b8a00 flags:# bot:flags.0?Bool premium:flags.1?Bool = RequestPeerType; +requestPeerTypeChat#c9f06e1b flags:# creator:flags.0?true bot_participant:flags.5?true has_username:flags.3?Bool forum:flags.4?Bool user_admin_rights:flags.1?ChatAdminRights bot_admin_rights:flags.2?ChatAdminRights = RequestPeerType; +requestPeerTypeBroadcast#339bef6c flags:# creator:flags.0?true has_username:flags.3?Bool user_admin_rights:flags.1?ChatAdminRights bot_admin_rights:flags.2?ChatAdminRights = RequestPeerType; + +emojiListNotModified#481eadfa = EmojiList; +emojiList#7a1e11d1 hash:long document_id:Vector = EmojiList; + +emojiGroup#7a9abda9 title:string icon_emoji_id:long emoticons:Vector = EmojiGroup; + +messages.emojiGroupsNotModified#6fb4ad87 = messages.EmojiGroups; +messages.emojiGroups#881fb94b hash:int groups:Vector = messages.EmojiGroups; + +textWithEntities#751f3146 text:string entities:Vector = TextWithEntities; + +messages.translateResult#33db32f8 result:Vector = messages.TranslatedText; + +autoSaveSettings#c84834ce flags:# photos:flags.0?true videos:flags.1?true video_max_size:flags.2?long = AutoSaveSettings; + +autoSaveException#81602d47 peer:Peer settings:AutoSaveSettings = AutoSaveException; + +account.autoSaveSettings#4c3e069d users_settings:AutoSaveSettings chats_settings:AutoSaveSettings broadcasts_settings:AutoSaveSettings exceptions:Vector chats:Vector users:Vector = account.AutoSaveSettings; + +help.appConfigNotModified#7cde641d = help.AppConfig; +help.appConfig#dd18782e hash:int config:JSONValue = help.AppConfig; + +inputBotAppID#a920bd7a id:long access_hash:long = InputBotApp; +inputBotAppShortName#908c0407 bot_id:InputUser short_name:string = InputBotApp; + +botAppNotModified#5da674b7 = BotApp; +botApp#95fcd1d6 flags:# id:long access_hash:long short_name:string title:string description:string photo:Photo document:flags.0?Document hash:long = BotApp; + +messages.botApp#eb50adf5 flags:# inactive:flags.0?true request_write_access:flags.1?true app:BotApp = messages.BotApp; + +appWebViewResultUrl#3c1b4f0d url:string = AppWebViewResult; + +inlineBotWebView#b57295d5 text:string url:string = InlineBotWebView; + +readParticipantDate#4a4ff172 user_id:long date:int = ReadParticipantDate; + +inputChatlistDialogFilter#f3e0da33 filter_id:int = InputChatlist; + +exportedChatlistInvite#c5181ac flags:# title:string url:string peers:Vector = ExportedChatlistInvite; + +chatlists.exportedChatlistInvite#10e6e3a6 filter:DialogFilter invite:ExportedChatlistInvite = chatlists.ExportedChatlistInvite; + +chatlists.exportedInvites#10ab6dc7 invites:Vector chats:Vector users:Vector = chatlists.ExportedInvites; + +chatlists.chatlistInviteAlready#fa87f659 filter_id:int missing_peers:Vector already_peers:Vector chats:Vector users:Vector = chatlists.ChatlistInvite; +chatlists.chatlistInvite#1dcd839d flags:# title:string emoticon:flags.0?string peers:Vector chats:Vector users:Vector = chatlists.ChatlistInvite; + +chatlists.chatlistUpdates#93bd878d missing_peers:Vector chats:Vector users:Vector = chatlists.ChatlistUpdates; + +bots.botInfo#e8a775b0 name:string about:string description:string = bots.BotInfo; + ---functions--- invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; invokeAfterMsgs#3dc4b4f0 {X:Type} msg_ids:Vector query:!X = X; -initConnection#785188b8 {X:Type} flags:# api_id:int device_model:string system_version:string app_version:string system_lang_code:string lang_pack:string lang_code:string proxy:flags.0?InputClientProxy query:!X = X; +initConnection#c1cd5ea9 {X:Type} flags:# api_id:int device_model:string system_version:string app_version:string system_lang_code:string lang_pack:string lang_code:string proxy:flags.0?InputClientProxy params:flags.1?JSONValue query:!X = X; invokeWithLayer#da9b0d0d {X:Type} layer:int query:!X = X; invokeWithoutUpdates#bf9459b7 {X:Type} query:!X = X; invokeWithMessagesRange#365275f2 {X:Type} range:MessageRange query:!X = X; invokeWithTakeout#aca9fd2e {X:Type} takeout_id:long query:!X = X; auth.sendCode#a677244f phone_number:string api_id:int api_hash:string settings:CodeSettings = auth.SentCode; -auth.signUp#1b067634 phone_number:string phone_code_hash:string phone_code:string first_name:string last_name:string = auth.Authorization; -auth.signIn#bcd51581 phone_number:string phone_code_hash:string phone_code:string = auth.Authorization; -auth.logOut#5717da40 = Bool; +auth.signUp#80eee427 phone_number:string phone_code_hash:string first_name:string last_name:string = auth.Authorization; +auth.signIn#8d52a951 flags:# phone_number:string phone_code_hash:string phone_code:flags.0?string email_verification:flags.1?EmailVerification = auth.Authorization; +auth.logOut#3e72ba19 = auth.LoggedOut; auth.resetAuthorizations#9fab0d1a = Bool; auth.exportAuthorization#e5bfffcd dc_id:int = auth.ExportedAuthorization; -auth.importAuthorization#e3ef9613 id:int bytes:bytes = auth.Authorization; +auth.importAuthorization#a57a7dad id:long bytes:bytes = auth.Authorization; auth.bindTempAuthKey#cdd42a05 perm_auth_key_id:long nonce:long expires_at:int encrypted_message:bytes = Bool; auth.importBotAuthorization#67a3ff2c flags:int api_id:int api_hash:string bot_auth_token:string = auth.Authorization; auth.checkPassword#d18b4d16 password:InputCheckPasswordSRP = auth.Authorization; auth.requestPasswordRecovery#d897bc66 = auth.PasswordRecovery; -auth.recoverPassword#4ea56e92 code:string = auth.Authorization; +auth.recoverPassword#37096c70 flags:# code:string new_settings:flags.0?account.PasswordInputSettings = auth.Authorization; auth.resendCode#3ef1a9bf phone_number:string phone_code_hash:string = auth.SentCode; auth.cancelCode#1f040578 phone_number:string phone_code_hash:string = Bool; auth.dropTempAuthKeys#8e48a188 except_auth_keys:Vector = Bool; - -account.registerDevice#5cbea590 token_type:int token:string app_sandbox:Bool secret:bytes other_uids:Vector = Bool; -account.unregisterDevice#3076c4bf token_type:int token:string other_uids:Vector = Bool; +auth.exportLoginToken#b7e085fe api_id:int api_hash:string except_ids:Vector = auth.LoginToken; +auth.importLoginToken#95ac5ce4 token:bytes = auth.LoginToken; +auth.acceptLoginToken#e894ad4d token:bytes = Authorization; +auth.checkRecoveryPassword#d36bf79 code:string = Bool; +auth.importWebTokenAuthorization#2db873a9 api_id:int api_hash:string web_auth_token:string = auth.Authorization; +auth.requestFirebaseSms#89464b50 flags:# phone_number:string phone_code_hash:string safety_net_token:flags.0?string ios_push_secret:flags.1?string = Bool; +auth.resetLoginEmail#7e960193 phone_number:string phone_code_hash:string = auth.SentCode; + +account.registerDevice#ec86017a flags:# no_muted:flags.0?true token_type:int token:string app_sandbox:Bool secret:bytes other_uids:Vector = Bool; +account.unregisterDevice#6a0d3206 token_type:int token:string other_uids:Vector = Bool; account.updateNotifySettings#84be5b93 peer:InputNotifyPeer settings:InputPeerNotifySettings = Bool; account.getNotifySettings#12b3ad31 peer:InputNotifyPeer = PeerNotifySettings; account.resetNotifySettings#db7e1747 = Bool; account.updateProfile#78515775 flags:# first_name:flags.0?string last_name:flags.1?string about:flags.2?string = User; account.updateStatus#6628562c offline:Bool = Bool; -account.getWallPapers#aabb1763 hash:int = account.WallPapers; -account.reportPeer#ae189d5f peer:InputPeer reason:ReportReason = Bool; +account.getWallPapers#7967d36 hash:long = account.WallPapers; +account.reportPeer#c5ba3d86 peer:InputPeer reason:ReportReason message:string = Bool; account.checkUsername#2714d86c username:string = Bool; account.updateUsername#3e0bdd7c username:string = User; account.getPrivacy#dadbc950 key:InputPrivacyKey = account.PrivacyRules; account.setPrivacy#c9f81ce8 key:InputPrivacyKey rules:Vector = account.PrivacyRules; -account.deleteAccount#418d4e0b reason:string = Bool; +account.deleteAccount#a2c0cf74 flags:# reason:string password:flags.0?InputCheckPasswordSRP = Bool; account.getAccountTTL#8fc711d = AccountDaysTTL; account.setAccountTTL#2442485e ttl:AccountDaysTTL = Bool; account.sendChangePhoneCode#82574ae5 phone_number:string settings:CodeSettings = auth.SentCode; @@ -1060,13 +1588,13 @@ account.getAllSecureValues#b288bc7d = Vector; account.getSecureValue#73665bc2 types:Vector = Vector; account.saveSecureValue#899fe31d value:InputSecureValue secure_secret_id:long = SecureValue; account.deleteSecureValue#b880bc4b types:Vector = Bool; -account.getAuthorizationForm#b86ba8e1 bot_id:int scope:string public_key:string = account.AuthorizationForm; -account.acceptAuthorization#e7027c94 bot_id:int scope:string public_key:string value_hashes:Vector credentials:SecureCredentialsEncrypted = Bool; +account.getAuthorizationForm#a929597a bot_id:long scope:string public_key:string = account.AuthorizationForm; +account.acceptAuthorization#f3ed4c73 bot_id:long scope:string public_key:string value_hashes:Vector credentials:SecureCredentialsEncrypted = Bool; account.sendVerifyPhoneCode#a5a356f9 phone_number:string settings:CodeSettings = auth.SentCode; account.verifyPhone#4dd3a7f6 phone_number:string phone_code_hash:string phone_code:string = Bool; -account.sendVerifyEmailCode#7011509f email:string = account.SentEmailCode; -account.verifyEmail#ecba39db email:string code:string = Bool; -account.initTakeoutSession#f05b4804 flags:# contacts:flags.0?true message_users:flags.1?true message_chats:flags.2?true message_megagroups:flags.3?true message_channels:flags.4?true files:flags.5?true file_max_size:flags.5?int = account.Takeout; +account.sendVerifyEmailCode#98e037bb purpose:EmailVerifyPurpose email:string = account.SentEmailCode; +account.verifyEmail#32da4cf purpose:EmailVerifyPurpose verification:EmailVerification = account.EmailVerified; +account.initTakeoutSession#8ef3eab0 flags:# contacts:flags.0?true message_users:flags.1?true message_chats:flags.2?true message_megagroups:flags.3?true message_channels:flags.4?true files:flags.5?true file_max_size:flags.5?long = account.Takeout; account.finishTakeoutSession#1d2652ee flags:# success:flags.0?true = Bool; account.confirmPasswordEmail#8fdf1920 code:string = Bool; account.resendPasswordEmail#7a7f2a15 = Bool; @@ -1075,162 +1603,280 @@ account.getContactSignUpNotification#9f07c728 = Bool; account.setContactSignUpNotification#cff43f61 silent:Bool = Bool; account.getNotifyExceptions#53577479 flags:# compare_sound:flags.1?true peer:flags.0?InputNotifyPeer = Updates; account.getWallPaper#fc8ddbea wallpaper:InputWallPaper = WallPaper; -account.uploadWallPaper#dd853661 file:InputFile mime_type:string settings:WallPaperSettings = WallPaper; +account.uploadWallPaper#e39a8f03 flags:# for_chat:flags.0?true file:InputFile mime_type:string settings:WallPaperSettings = WallPaper; account.saveWallPaper#6c5a5b37 wallpaper:InputWallPaper unsave:Bool settings:WallPaperSettings = Bool; account.installWallPaper#feed5769 wallpaper:InputWallPaper settings:WallPaperSettings = Bool; account.resetWallPapers#bb3b9804 = Bool; account.getAutoDownloadSettings#56da0b3f = account.AutoDownloadSettings; account.saveAutoDownloadSettings#76f36233 flags:# low:flags.0?true high:flags.1?true settings:AutoDownloadSettings = Bool; +account.uploadTheme#1c3db333 flags:# file:InputFile thumb:flags.0?InputFile file_name:string mime_type:string = Document; +account.createTheme#652e4400 flags:# slug:string title:string document:flags.2?InputDocument settings:flags.3?Vector = Theme; +account.updateTheme#2bf40ccc flags:# format:string theme:InputTheme slug:flags.0?string title:flags.1?string document:flags.2?InputDocument settings:flags.3?Vector = Theme; +account.saveTheme#f257106c theme:InputTheme unsave:Bool = Bool; +account.installTheme#c727bb3b flags:# dark:flags.0?true theme:flags.1?InputTheme format:flags.2?string base_theme:flags.3?BaseTheme = Bool; +account.getTheme#3a5869ec format:string theme:InputTheme = Theme; +account.getThemes#7206e458 format:string hash:long = account.Themes; +account.setContentSettings#b574b16b flags:# sensitive_enabled:flags.0?true = Bool; +account.getContentSettings#8b9b4dae = account.ContentSettings; +account.getMultiWallPapers#65ad71dc wallpapers:Vector = Vector; +account.getGlobalPrivacySettings#eb2b4cf6 = GlobalPrivacySettings; +account.setGlobalPrivacySettings#1edaaac2 settings:GlobalPrivacySettings = GlobalPrivacySettings; +account.reportProfilePhoto#fa8cc6f5 peer:InputPeer photo_id:InputPhoto reason:ReportReason message:string = Bool; +account.resetPassword#9308ce1b = account.ResetPasswordResult; +account.declinePasswordReset#4c9409f6 = Bool; +account.getChatThemes#d638de89 hash:long = account.Themes; +account.setAuthorizationTTL#bf899aa0 authorization_ttl_days:int = Bool; +account.changeAuthorizationSettings#40f48462 flags:# hash:long encrypted_requests_disabled:flags.0?Bool call_requests_disabled:flags.1?Bool = Bool; +account.getSavedRingtones#e1902288 hash:long = account.SavedRingtones; +account.saveRingtone#3dea5b03 id:InputDocument unsave:Bool = account.SavedRingtone; +account.uploadRingtone#831a83a2 file:InputFile file_name:string mime_type:string = Document; +account.updateEmojiStatus#fbd3de6b emoji_status:EmojiStatus = Bool; +account.getDefaultEmojiStatuses#d6753386 hash:long = account.EmojiStatuses; +account.getRecentEmojiStatuses#f578105 hash:long = account.EmojiStatuses; +account.clearRecentEmojiStatuses#18201aae = Bool; +account.reorderUsernames#ef500eab order:Vector = Bool; +account.toggleUsername#58d6b376 username:string active:Bool = Bool; +account.getDefaultProfilePhotoEmojis#e2750328 hash:long = EmojiList; +account.getDefaultGroupPhotoEmojis#915860ae hash:long = EmojiList; +account.getAutoSaveSettings#adcbbcda = account.AutoSaveSettings; +account.saveAutoSaveSettings#d69b8361 flags:# users:flags.0?true chats:flags.1?true broadcasts:flags.2?true peer:flags.3?InputPeer settings:AutoSaveSettings = Bool; +account.deleteAutoSaveExceptions#53bc0020 = Bool; users.getUsers#d91a548 id:Vector = Vector; -users.getFullUser#ca30a5b1 id:InputUser = UserFull; +users.getFullUser#b60f5918 id:InputUser = users.UserFull; users.setSecureValueErrors#90c894b5 id:InputUser errors:Vector = Bool; -contacts.getContactIDs#2caa4a42 hash:int = Vector; +contacts.getContactIDs#7adc669d hash:long = Vector; contacts.getStatuses#c4a353ee = Vector; -contacts.getContacts#c023849f hash:int = contacts.Contacts; +contacts.getContacts#5dd69e12 hash:long = contacts.Contacts; contacts.importContacts#2c800be5 contacts:Vector = contacts.ImportedContacts; -contacts.deleteContact#8e953744 id:InputUser = contacts.Link; -contacts.deleteContacts#59ab389e id:Vector = Bool; +contacts.deleteContacts#96a0e00 id:Vector = Updates; contacts.deleteByPhones#1013fd9e phones:Vector = Bool; -contacts.block#332b49fc id:InputUser = Bool; -contacts.unblock#e54100bd id:InputUser = Bool; +contacts.block#68cc1411 id:InputPeer = Bool; +contacts.unblock#bea65d50 id:InputPeer = Bool; contacts.getBlocked#f57c350f offset:int limit:int = contacts.Blocked; contacts.search#11f812d8 q:string limit:int = contacts.Found; contacts.resolveUsername#f93ccba3 username:string = contacts.ResolvedPeer; -contacts.getTopPeers#d4982db5 flags:# correspondents:flags.0?true bots_pm:flags.1?true bots_inline:flags.2?true phone_calls:flags.3?true groups:flags.10?true channels:flags.15?true offset:int limit:int hash:int = contacts.TopPeers; +contacts.getTopPeers#973478b6 flags:# correspondents:flags.0?true bots_pm:flags.1?true bots_inline:flags.2?true phone_calls:flags.3?true forward_users:flags.4?true forward_chats:flags.5?true groups:flags.10?true channels:flags.15?true offset:int limit:int hash:long = contacts.TopPeers; contacts.resetTopPeerRating#1ae373ac category:TopPeerCategory peer:InputPeer = Bool; contacts.resetSaved#879537f1 = Bool; contacts.getSaved#82f1e39f = Vector; contacts.toggleTopPeers#8514bdda enabled:Bool = Bool; +contacts.addContact#e8f463d0 flags:# add_phone_privacy_exception:flags.0?true id:InputUser first_name:string last_name:string phone:string = Updates; +contacts.acceptContact#f831a20f id:InputUser = Updates; +contacts.getLocated#d348bc44 flags:# background:flags.1?true geo_point:InputGeoPoint self_expires:flags.0?int = Updates; +contacts.blockFromReplies#29a8962c flags:# delete_message:flags.0?true delete_history:flags.1?true report_spam:flags.2?true msg_id:int = Updates; +contacts.resolvePhone#8af94344 phone:string = contacts.ResolvedPeer; +contacts.exportContactToken#f8654027 = ExportedContactToken; +contacts.importContactToken#13005788 token:string = User; messages.getMessages#63c66506 id:Vector = messages.Messages; -messages.getDialogs#b098aee6 flags:# exclude_pinned:flags.0?true offset_date:int offset_id:int offset_peer:InputPeer limit:int hash:int = messages.Dialogs; -messages.getHistory#dcbb8260 peer:InputPeer offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:int = messages.Messages; -messages.search#8614ef68 flags:# peer:InputPeer q:string from_id:flags.0?InputUser filter:MessagesFilter min_date:int max_date:int offset_id:int add_offset:int limit:int max_id:int min_id:int hash:int = messages.Messages; +messages.getDialogs#a0f4cb4f flags:# exclude_pinned:flags.0?true folder_id:flags.1?int offset_date:int offset_id:int offset_peer:InputPeer limit:int hash:long = messages.Dialogs; +messages.getHistory#4423e6c5 peer:InputPeer offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:long = messages.Messages; +messages.search#a0fda762 flags:# peer:InputPeer q:string from_id:flags.0?InputPeer top_msg_id:flags.1?int filter:MessagesFilter min_date:int max_date:int offset_id:int add_offset:int limit:int max_id:int min_id:int hash:long = messages.Messages; messages.readHistory#e306d3a peer:InputPeer max_id:int = messages.AffectedMessages; -messages.deleteHistory#1c015b09 flags:# just_clear:flags.0?true revoke:flags.1?true peer:InputPeer max_id:int = messages.AffectedHistory; +messages.deleteHistory#b08f922a flags:# just_clear:flags.0?true revoke:flags.1?true peer:InputPeer max_id:int min_date:flags.2?int max_date:flags.3?int = messages.AffectedHistory; messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector = messages.AffectedMessages; messages.receivedMessages#5a954c0 max_id:int = Vector; -messages.setTyping#a3825e50 peer:InputPeer action:SendMessageAction = Bool; -messages.sendMessage#fa88427a flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector = Updates; -messages.sendMedia#b8d1262b flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector = Updates; -messages.forwardMessages#708e0195 flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true grouped:flags.9?true from_peer:InputPeer id:Vector random_id:Vector to_peer:InputPeer = Updates; +messages.setTyping#58943ee2 flags:# peer:InputPeer top_msg_id:flags.0?int action:SendMessageAction = Bool; +messages.sendMessage#1cc20387 flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true peer:InputPeer reply_to_msg_id:flags.0?int top_msg_id:flags.9?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates; +messages.sendMedia#7547c966 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true peer:InputPeer reply_to_msg_id:flags.0?int top_msg_id:flags.9?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates; +messages.forwardMessages#c661bbc4 flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true drop_author:flags.11?true drop_media_captions:flags.12?true noforwards:flags.14?true from_peer:InputPeer id:Vector random_id:Vector to_peer:InputPeer top_msg_id:flags.9?int schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates; messages.reportSpam#cf1592db peer:InputPeer = Bool; -messages.hideReportSpam#a8f1709b peer:InputPeer = Bool; -messages.getPeerSettings#3672e09c peer:InputPeer = PeerSettings; -messages.report#bd82b658 peer:InputPeer id:Vector reason:ReportReason = Bool; -messages.getChats#3c6aa187 id:Vector = messages.Chats; -messages.getFullChat#3b831c66 chat_id:int = messages.ChatFull; -messages.editChatTitle#dc452855 chat_id:int title:string = Updates; -messages.editChatPhoto#ca4c79d8 chat_id:int photo:InputChatPhoto = Updates; -messages.addChatUser#f9a0aa09 chat_id:int user_id:InputUser fwd_limit:int = Updates; -messages.deleteChatUser#e0611f16 chat_id:int user_id:InputUser = Updates; -messages.createChat#9cb126e users:Vector title:string = Updates; +messages.getPeerSettings#efd9a6a2 peer:InputPeer = messages.PeerSettings; +messages.report#8953ab4e peer:InputPeer id:Vector reason:ReportReason message:string = Bool; +messages.getChats#49e9528f id:Vector = messages.Chats; +messages.getFullChat#aeb00b34 chat_id:long = messages.ChatFull; +messages.editChatTitle#73783ffd chat_id:long title:string = Updates; +messages.editChatPhoto#35ddd674 chat_id:long photo:InputChatPhoto = Updates; +messages.addChatUser#f24753e3 chat_id:long user_id:InputUser fwd_limit:int = Updates; +messages.deleteChatUser#a2185cab flags:# revoke_history:flags.0?true chat_id:long user_id:InputUser = Updates; +messages.createChat#34a818 flags:# users:Vector title:string ttl_period:flags.0?int = Updates; messages.getDhConfig#26cf8950 version:int random_length:int = messages.DhConfig; messages.requestEncryption#f64daf43 user_id:InputUser random_id:int g_a:bytes = EncryptedChat; messages.acceptEncryption#3dbc0415 peer:InputEncryptedChat g_b:bytes key_fingerprint:long = EncryptedChat; -messages.discardEncryption#edd923c5 chat_id:int = Bool; +messages.discardEncryption#f393aea0 flags:# delete_history:flags.0?true chat_id:int = Bool; messages.setEncryptedTyping#791451ed peer:InputEncryptedChat typing:Bool = Bool; messages.readEncryptedHistory#7f4b690a peer:InputEncryptedChat max_date:int = Bool; -messages.sendEncrypted#a9776773 peer:InputEncryptedChat random_id:long data:bytes = messages.SentEncryptedMessage; -messages.sendEncryptedFile#9a901b66 peer:InputEncryptedChat random_id:long data:bytes file:InputEncryptedFile = messages.SentEncryptedMessage; +messages.sendEncrypted#44fa7a15 flags:# silent:flags.0?true peer:InputEncryptedChat random_id:long data:bytes = messages.SentEncryptedMessage; +messages.sendEncryptedFile#5559481d flags:# silent:flags.0?true peer:InputEncryptedChat random_id:long data:bytes file:InputEncryptedFile = messages.SentEncryptedMessage; messages.sendEncryptedService#32d439a4 peer:InputEncryptedChat random_id:long data:bytes = messages.SentEncryptedMessage; messages.receivedQueue#55a5bb66 max_qts:int = Vector; messages.reportEncryptedSpam#4b0c8c0f peer:InputEncryptedChat = Bool; messages.readMessageContents#36a73f77 id:Vector = messages.AffectedMessages; -messages.getStickers#43d4f2c emoticon:string hash:int = messages.Stickers; -messages.getAllStickers#1c9618b1 hash:int = messages.AllStickers; +messages.getStickers#d5a5d3a1 emoticon:string hash:long = messages.Stickers; +messages.getAllStickers#b8a0a1a8 hash:long = messages.AllStickers; messages.getWebPagePreview#8b68b0cc flags:# message:string entities:flags.3?Vector = MessageMedia; -messages.exportChatInvite#df7534c peer:InputPeer = ExportedChatInvite; +messages.exportChatInvite#a02ce5d5 flags:# legacy_revoke_permanent:flags.2?true request_needed:flags.3?true peer:InputPeer expire_date:flags.0?int usage_limit:flags.1?int title:flags.4?string = ExportedChatInvite; messages.checkChatInvite#3eadb1bb hash:string = ChatInvite; messages.importChatInvite#6c50051c hash:string = Updates; -messages.getStickerSet#2619a90e stickerset:InputStickerSet = messages.StickerSet; +messages.getStickerSet#c8a0ec74 stickerset:InputStickerSet hash:int = messages.StickerSet; messages.installStickerSet#c78fe460 stickerset:InputStickerSet archived:Bool = messages.StickerSetInstallResult; messages.uninstallStickerSet#f96e55de stickerset:InputStickerSet = Bool; messages.startBot#e6df7378 bot:InputUser peer:InputPeer random_id:long start_param:string = Updates; -messages.getMessagesViews#c4c8a55d peer:InputPeer id:Vector increment:Bool = Vector; -messages.editChatAdmin#a9e69f2e chat_id:int user_id:InputUser is_admin:Bool = Bool; -messages.migrateChat#15a3b8e3 chat_id:int = Updates; -messages.searchGlobal#9e3cacb0 q:string offset_date:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages; -messages.reorderStickerSets#78337739 flags:# masks:flags.0?true order:Vector = Bool; -messages.getDocumentByHash#338e2464 sha256:bytes size:int mime_type:string = Document; -messages.searchGifs#bf9a776b q:string offset:int = messages.FoundGifs; -messages.getSavedGifs#83bf3d52 hash:int = messages.SavedGifs; +messages.getMessagesViews#5784d3e1 peer:InputPeer id:Vector increment:Bool = messages.MessageViews; +messages.editChatAdmin#a85bd1c2 chat_id:long user_id:InputUser is_admin:Bool = Bool; +messages.migrateChat#a2875319 chat_id:long = Updates; +messages.searchGlobal#4bc6589a flags:# folder_id:flags.0?int q:string filter:MessagesFilter min_date:int max_date:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages; +messages.reorderStickerSets#78337739 flags:# masks:flags.0?true emojis:flags.1?true order:Vector = Bool; +messages.getDocumentByHash#b1f2061f sha256:bytes size:long mime_type:string = Document; +messages.getSavedGifs#5cf09635 hash:long = messages.SavedGifs; messages.saveGif#327a30cb id:InputDocument unsave:Bool = Bool; messages.getInlineBotResults#514e999d flags:# bot:InputUser peer:InputPeer geo_point:flags.0?InputGeoPoint query:string offset:string = messages.BotResults; -messages.setInlineBotResults#eb5ea206 flags:# gallery:flags.0?true private:flags.1?true query_id:long results:Vector cache_time:int next_offset:flags.2?string switch_pm:flags.3?InlineBotSwitchPM = Bool; -messages.sendInlineBotResult#b16e06fe flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to_msg_id:flags.0?int random_id:long query_id:long id:string = Updates; +messages.setInlineBotResults#bb12a419 flags:# gallery:flags.0?true private:flags.1?true query_id:long results:Vector cache_time:int next_offset:flags.2?string switch_pm:flags.3?InlineBotSwitchPM switch_webview:flags.4?InlineBotWebView = Bool; +messages.sendInlineBotResult#d3fbdccb flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to_msg_id:flags.0?int top_msg_id:flags.9?int random_id:long query_id:long id:string schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates; messages.getMessageEditData#fda68d36 peer:InputPeer id:int = messages.MessageEditData; -messages.editMessage#d116f31e flags:# no_webpage:flags.1?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector = Updates; +messages.editMessage#48f71778 flags:# no_webpage:flags.1?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector schedule_date:flags.15?int = Updates; messages.editInlineBotMessage#83557dba flags:# no_webpage:flags.1?true id:InputBotInlineMessageID message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector = Bool; -messages.getBotCallbackAnswer#810a9fec flags:# game:flags.1?true peer:InputPeer msg_id:int data:flags.0?bytes = messages.BotCallbackAnswer; +messages.getBotCallbackAnswer#9342ca07 flags:# game:flags.1?true peer:InputPeer msg_id:int data:flags.0?bytes password:flags.2?InputCheckPasswordSRP = messages.BotCallbackAnswer; messages.setBotCallbackAnswer#d58f130a flags:# alert:flags.1?true query_id:long message:flags.0?string url:flags.2?string cache_time:int = Bool; messages.getPeerDialogs#e470bcfd peers:Vector = messages.PeerDialogs; -messages.saveDraft#bc39e14b flags:# no_webpage:flags.1?true reply_to_msg_id:flags.0?int peer:InputPeer message:string entities:flags.3?Vector = Bool; +messages.saveDraft#b4331e3f flags:# no_webpage:flags.1?true reply_to_msg_id:flags.0?int top_msg_id:flags.2?int peer:InputPeer message:string entities:flags.3?Vector = Bool; messages.getAllDrafts#6a3f8d65 = Updates; -messages.getFeaturedStickers#2dacca4f hash:int = messages.FeaturedStickers; +messages.getFeaturedStickers#64780b14 hash:long = messages.FeaturedStickers; messages.readFeaturedStickers#5b118126 id:Vector = Bool; -messages.getRecentStickers#5ea192c9 flags:# attached:flags.0?true hash:int = messages.RecentStickers; +messages.getRecentStickers#9da9403b flags:# attached:flags.0?true hash:long = messages.RecentStickers; messages.saveRecentSticker#392718f8 flags:# attached:flags.0?true id:InputDocument unsave:Bool = Bool; messages.clearRecentStickers#8999602d flags:# attached:flags.0?true = Bool; -messages.getArchivedStickers#57f17692 flags:# masks:flags.0?true offset_id:long limit:int = messages.ArchivedStickers; -messages.getMaskStickers#65b8c79f hash:int = messages.AllStickers; +messages.getArchivedStickers#57f17692 flags:# masks:flags.0?true emojis:flags.1?true offset_id:long limit:int = messages.ArchivedStickers; +messages.getMaskStickers#640f82b8 hash:long = messages.AllStickers; messages.getAttachedStickers#cc5b67cc media:InputStickeredMedia = Vector; messages.setGameScore#8ef8ecc0 flags:# edit_message:flags.0?true force:flags.1?true peer:InputPeer id:int user_id:InputUser score:int = Updates; messages.setInlineGameScore#15ad9f64 flags:# edit_message:flags.0?true force:flags.1?true id:InputBotInlineMessageID user_id:InputUser score:int = Bool; messages.getGameHighScores#e822649d peer:InputPeer id:int user_id:InputUser = messages.HighScores; messages.getInlineGameHighScores#f635e1b id:InputBotInlineMessageID user_id:InputUser = messages.HighScores; -messages.getCommonChats#d0a48c4 user_id:InputUser max_id:int limit:int = messages.Chats; -messages.getAllChats#eba80ff0 except_ids:Vector = messages.Chats; +messages.getCommonChats#e40ca104 user_id:InputUser max_id:long limit:int = messages.Chats; +messages.getAllChats#875f74be except_ids:Vector = messages.Chats; messages.getWebPage#32ca8f91 url:string hash:int = WebPage; messages.toggleDialogPin#a731e257 flags:# pinned:flags.0?true peer:InputDialogPeer = Bool; -messages.reorderPinnedDialogs#5b51d63f flags:# force:flags.0?true order:Vector = Bool; -messages.getPinnedDialogs#e254d64e = messages.PeerDialogs; +messages.reorderPinnedDialogs#3b1adf37 flags:# force:flags.0?true folder_id:int order:Vector = Bool; +messages.getPinnedDialogs#d6b94df2 folder_id:int = messages.PeerDialogs; messages.setBotShippingResults#e5f672fa flags:# query_id:long error:flags.0?string shipping_options:flags.1?Vector = Bool; messages.setBotPrecheckoutResults#9c2dd95 flags:# success:flags.1?true query_id:long error:flags.0?string = Bool; messages.uploadMedia#519bc2b1 peer:InputPeer media:InputMedia = MessageMedia; messages.sendScreenshotNotification#c97df020 peer:InputPeer reply_to_msg_id:int random_id:long = Updates; -messages.getFavedStickers#21ce0b0e hash:int = messages.FavedStickers; +messages.getFavedStickers#4f1aaa9 hash:long = messages.FavedStickers; messages.faveSticker#b9ffc55b id:InputDocument unfave:Bool = Bool; -messages.getUnreadMentions#46578472 peer:InputPeer offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages; -messages.readMentions#f0189d3 peer:InputPeer = messages.AffectedHistory; -messages.getRecentLocations#bbc45b09 peer:InputPeer limit:int hash:int = messages.Messages; -messages.sendMultiMedia#2095512f flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector = Updates; +messages.getUnreadMentions#f107e790 flags:# peer:InputPeer top_msg_id:flags.0?int offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages; +messages.readMentions#36e5bf4d flags:# peer:InputPeer top_msg_id:flags.0?int = messages.AffectedHistory; +messages.getRecentLocations#702a40e0 peer:InputPeer limit:int hash:long = messages.Messages; +messages.sendMultiMedia#b6f11a1c flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true peer:InputPeer reply_to_msg_id:flags.0?int top_msg_id:flags.9?int multi_media:Vector schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates; messages.uploadEncryptedFile#5057c497 peer:InputEncryptedChat file:InputEncryptedFile = EncryptedFile; -messages.searchStickerSets#c2b7d08b flags:# exclude_featured:flags.0?true q:string hash:int = messages.FoundStickerSets; +messages.searchStickerSets#35705b8a flags:# exclude_featured:flags.0?true q:string hash:long = messages.FoundStickerSets; messages.getSplitRanges#1cff7e08 = Vector; messages.markDialogUnread#c286d98f flags:# unread:flags.0?true peer:InputDialogPeer = Bool; messages.getDialogUnreadMarks#22e24e22 = Vector; messages.clearAllDrafts#7e58ee9c = Bool; -messages.updatePinnedMessage#d2aaf7ec flags:# silent:flags.0?true peer:InputPeer id:int = Updates; +messages.updatePinnedMessage#d2aaf7ec flags:# silent:flags.0?true unpin:flags.1?true pm_oneside:flags.2?true peer:InputPeer id:int = Updates; messages.sendVote#10ea6184 peer:InputPeer msg_id:int options:Vector = Updates; messages.getPollResults#73bb643b peer:InputPeer msg_id:int = Updates; messages.getOnlines#6e2be050 peer:InputPeer = ChatOnlines; -messages.getStatsURL#812c2ae6 flags:# dark:flags.0?true peer:InputPeer params:string = StatsURL; messages.editChatAbout#def60797 peer:InputPeer about:string = Bool; messages.editChatDefaultBannedRights#a5866b41 peer:InputPeer banned_rights:ChatBannedRights = Updates; messages.getEmojiKeywords#35a0e062 lang_code:string = EmojiKeywordsDifference; messages.getEmojiKeywordsDifference#1508b6af lang_code:string from_version:int = EmojiKeywordsDifference; +messages.getEmojiKeywordsLanguages#4e9963b2 lang_codes:Vector = Vector; messages.getEmojiURL#d5b10c26 lang_code:string = EmojiURL; +messages.getSearchCounters#ae7cc1 flags:# peer:InputPeer top_msg_id:flags.0?int filters:Vector = Vector; +messages.requestUrlAuth#198fb446 flags:# peer:flags.1?InputPeer msg_id:flags.1?int button_id:flags.1?int url:flags.2?string = UrlAuthResult; +messages.acceptUrlAuth#b12c7125 flags:# write_allowed:flags.0?true peer:flags.1?InputPeer msg_id:flags.1?int button_id:flags.1?int url:flags.2?string = UrlAuthResult; +messages.hidePeerSettingsBar#4facb138 peer:InputPeer = Bool; +messages.getScheduledHistory#f516760b peer:InputPeer hash:long = messages.Messages; +messages.getScheduledMessages#bdbb0464 peer:InputPeer id:Vector = messages.Messages; +messages.sendScheduledMessages#bd38850a peer:InputPeer id:Vector = Updates; +messages.deleteScheduledMessages#59ae2b16 peer:InputPeer id:Vector = Updates; +messages.getPollVotes#b86e380e flags:# peer:InputPeer id:int option:flags.0?bytes offset:flags.1?string limit:int = messages.VotesList; +messages.toggleStickerSets#b5052fea flags:# uninstall:flags.0?true archive:flags.1?true unarchive:flags.2?true stickersets:Vector = Bool; +messages.getDialogFilters#f19ed96d = Vector; +messages.getSuggestedDialogFilters#a29cd42c = Vector; +messages.updateDialogFilter#1ad4a04a flags:# id:int filter:flags.0?DialogFilter = Bool; +messages.updateDialogFiltersOrder#c563c1e4 order:Vector = Bool; +messages.getOldFeaturedStickers#7ed094a1 offset:int limit:int hash:long = messages.FeaturedStickers; +messages.getReplies#22ddd30c peer:InputPeer msg_id:int offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:long = messages.Messages; +messages.getDiscussionMessage#446972fd peer:InputPeer msg_id:int = messages.DiscussionMessage; +messages.readDiscussion#f731a9f4 peer:InputPeer msg_id:int read_max_id:int = Bool; +messages.unpinAllMessages#ee22b9a8 flags:# peer:InputPeer top_msg_id:flags.0?int = messages.AffectedHistory; +messages.deleteChat#5bd0ee50 chat_id:long = Bool; +messages.deletePhoneCallHistory#f9cbe409 flags:# revoke:flags.0?true = messages.AffectedFoundMessages; +messages.checkHistoryImport#43fe19f3 import_head:string = messages.HistoryImportParsed; +messages.initHistoryImport#34090c3b peer:InputPeer file:InputFile media_count:int = messages.HistoryImport; +messages.uploadImportedMedia#2a862092 peer:InputPeer import_id:long file_name:string media:InputMedia = MessageMedia; +messages.startHistoryImport#b43df344 peer:InputPeer import_id:long = Bool; +messages.getExportedChatInvites#a2b5a3f6 flags:# revoked:flags.3?true peer:InputPeer admin_id:InputUser offset_date:flags.2?int offset_link:flags.2?string limit:int = messages.ExportedChatInvites; +messages.getExportedChatInvite#73746f5c peer:InputPeer link:string = messages.ExportedChatInvite; +messages.editExportedChatInvite#bdca2f75 flags:# revoked:flags.2?true peer:InputPeer link:string expire_date:flags.0?int usage_limit:flags.1?int request_needed:flags.3?Bool title:flags.4?string = messages.ExportedChatInvite; +messages.deleteRevokedExportedChatInvites#56987bd5 peer:InputPeer admin_id:InputUser = Bool; +messages.deleteExportedChatInvite#d464a42b peer:InputPeer link:string = Bool; +messages.getAdminsWithInvites#3920e6ef peer:InputPeer = messages.ChatAdminsWithInvites; +messages.getChatInviteImporters#df04dd4e flags:# requested:flags.0?true peer:InputPeer link:flags.1?string q:flags.2?string offset_date:int offset_user:InputUser limit:int = messages.ChatInviteImporters; +messages.setHistoryTTL#b80e5fe4 peer:InputPeer period:int = Updates; +messages.checkHistoryImportPeer#5dc60f03 peer:InputPeer = messages.CheckedHistoryImportPeer; +messages.setChatTheme#e63be13f peer:InputPeer emoticon:string = Updates; +messages.getMessageReadParticipants#31c1c44f peer:InputPeer msg_id:int = Vector; +messages.getSearchResultsCalendar#49f0bde9 peer:InputPeer filter:MessagesFilter offset_id:int offset_date:int = messages.SearchResultsCalendar; +messages.getSearchResultsPositions#6e9583a3 peer:InputPeer filter:MessagesFilter offset_id:int limit:int = messages.SearchResultsPositions; +messages.hideChatJoinRequest#7fe7e815 flags:# approved:flags.0?true peer:InputPeer user_id:InputUser = Updates; +messages.hideAllChatJoinRequests#e085f4ea flags:# approved:flags.0?true peer:InputPeer link:flags.1?string = Updates; +messages.toggleNoForwards#b11eafa2 peer:InputPeer enabled:Bool = Updates; +messages.saveDefaultSendAs#ccfddf96 peer:InputPeer send_as:InputPeer = Bool; +messages.sendReaction#d30d78d4 flags:# big:flags.1?true add_to_recent:flags.2?true peer:InputPeer msg_id:int reaction:flags.0?Vector = Updates; +messages.getMessagesReactions#8bba90e6 peer:InputPeer id:Vector = Updates; +messages.getMessageReactionsList#461b3f48 flags:# peer:InputPeer id:int reaction:flags.0?Reaction offset:flags.1?string limit:int = messages.MessageReactionsList; +messages.setChatAvailableReactions#feb16771 peer:InputPeer available_reactions:ChatReactions = Updates; +messages.getAvailableReactions#18dea0ac hash:int = messages.AvailableReactions; +messages.setDefaultReaction#4f47a016 reaction:Reaction = Bool; +messages.translateText#63183030 flags:# peer:flags.0?InputPeer id:flags.0?Vector text:flags.1?Vector to_lang:string = messages.TranslatedText; +messages.getUnreadReactions#3223495b flags:# peer:InputPeer top_msg_id:flags.0?int offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages; +messages.readReactions#54aa7f8e flags:# peer:InputPeer top_msg_id:flags.0?int = messages.AffectedHistory; +messages.searchSentMedia#107e31a0 q:string filter:MessagesFilter limit:int = messages.Messages; +messages.getAttachMenuBots#16fcc2cb hash:long = AttachMenuBots; +messages.getAttachMenuBot#77216192 bot:InputUser = AttachMenuBotsBot; +messages.toggleBotInAttachMenu#69f59d69 flags:# write_allowed:flags.0?true bot:InputUser enabled:Bool = Bool; +messages.requestWebView#178b480b flags:# from_bot_menu:flags.4?true silent:flags.5?true peer:InputPeer bot:InputUser url:flags.1?string start_param:flags.3?string theme_params:flags.2?DataJSON platform:string reply_to_msg_id:flags.0?int top_msg_id:flags.9?int send_as:flags.13?InputPeer = WebViewResult; +messages.prolongWebView#7ff34309 flags:# silent:flags.5?true peer:InputPeer bot:InputUser query_id:long reply_to_msg_id:flags.0?int top_msg_id:flags.9?int send_as:flags.13?InputPeer = Bool; +messages.requestSimpleWebView#299bec8e flags:# from_switch_webview:flags.1?true bot:InputUser url:string theme_params:flags.0?DataJSON platform:string = SimpleWebViewResult; +messages.sendWebViewResultMessage#a4314f5 bot_query_id:string result:InputBotInlineResult = WebViewMessageSent; +messages.sendWebViewData#dc0242c8 bot:InputUser random_id:long button_text:string data:string = Updates; +messages.transcribeAudio#269e9a49 peer:InputPeer msg_id:int = messages.TranscribedAudio; +messages.rateTranscribedAudio#7f1d072f peer:InputPeer msg_id:int transcription_id:long good:Bool = Bool; +messages.getCustomEmojiDocuments#d9ab0f54 document_id:Vector = Vector; +messages.getEmojiStickers#fbfca18f hash:long = messages.AllStickers; +messages.getFeaturedEmojiStickers#ecf6736 hash:long = messages.FeaturedStickers; +messages.reportReaction#3f64c076 peer:InputPeer id:int reaction_peer:InputPeer = Bool; +messages.getTopReactions#bb8125ba limit:int hash:long = messages.Reactions; +messages.getRecentReactions#39461db2 limit:int hash:long = messages.Reactions; +messages.clearRecentReactions#9dfeefb4 = Bool; +messages.getExtendedMedia#84f80814 peer:InputPeer id:Vector = Updates; +messages.setDefaultHistoryTTL#9eb51445 period:int = Bool; +messages.getDefaultHistoryTTL#658b7188 = DefaultHistoryTTL; +messages.sendBotRequestedPeer#fe38d01b peer:InputPeer msg_id:int button_id:int requested_peer:InputPeer = Updates; +messages.getEmojiGroups#7488ce5b hash:int = messages.EmojiGroups; +messages.getEmojiStatusGroups#2ecd56cd hash:int = messages.EmojiGroups; +messages.getEmojiProfilePhotoGroups#21a548f3 hash:int = messages.EmojiGroups; +messages.searchCustomEmoji#2c11c0d7 emoticon:string hash:long = EmojiList; +messages.togglePeerTranslations#e47cb579 flags:# disabled:flags.0?true peer:InputPeer = Bool; +messages.getBotApp#34fdc5c3 app:InputBotApp hash:long = messages.BotApp; +messages.requestAppWebView#8c5a3b3c flags:# write_allowed:flags.0?true peer:InputPeer app:InputBotApp start_param:flags.1?string theme_params:flags.2?DataJSON platform:string = AppWebViewResult; +messages.setChatWallPaper#8ffacae1 flags:# peer:InputPeer wallpaper:flags.0?InputWallPaper settings:flags.2?WallPaperSettings id:flags.1?int = Updates; updates.getState#edd4882a = updates.State; updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference; updates.getChannelDifference#3173d78 flags:# force:flags.0?true channel:InputChannel filter:ChannelMessagesFilter pts:int limit:int = updates.ChannelDifference; -photos.updateProfilePhoto#f0bb5152 id:InputPhoto = UserProfilePhoto; -photos.uploadProfilePhoto#4f32c098 file:InputFile = photos.Photo; +photos.updateProfilePhoto#9e82039 flags:# fallback:flags.0?true bot:flags.1?InputUser id:InputPhoto = photos.Photo; +photos.uploadProfilePhoto#388a3b5 flags:# fallback:flags.3?true bot:flags.5?InputUser file:flags.0?InputFile video:flags.1?InputFile video_start_ts:flags.2?double video_emoji_markup:flags.4?VideoSize = photos.Photo; photos.deletePhotos#87cf7f2f id:Vector = Vector; photos.getUserPhotos#91cd32a8 user_id:InputUser offset:int max_id:long limit:int = photos.Photos; +photos.uploadContactProfilePhoto#e14c4a71 flags:# suggest:flags.3?true save:flags.4?true user_id:InputUser file:flags.0?InputFile video:flags.1?InputFile video_start_ts:flags.2?double video_emoji_markup:flags.5?VideoSize = photos.Photo; upload.saveFilePart#b304a621 file_id:long file_part:int bytes:bytes = Bool; -upload.getFile#e3a6cfb5 location:InputFileLocation offset:int limit:int = upload.File; +upload.getFile#be5335be flags:# precise:flags.0?true cdn_supported:flags.1?true location:InputFileLocation offset:long limit:int = upload.File; upload.saveBigFilePart#de7b673d file_id:long file_part:int file_total_parts:int bytes:bytes = Bool; upload.getWebFile#24e6818d location:InputWebFileLocation offset:int limit:int = upload.WebFile; -upload.getCdnFile#2000bcc3 file_token:bytes offset:int limit:int = upload.CdnFile; +upload.getCdnFile#395f69da file_token:bytes offset:long limit:int = upload.CdnFile; upload.reuploadCdnFile#9b2754a8 file_token:bytes request_token:bytes = Vector; -upload.getCdnFileHashes#4da54231 file_token:bytes offset:int = Vector; -upload.getFileHashes#c7025931 location:InputFileLocation offset:int = Vector; +upload.getCdnFileHashes#91dc3f31 file_token:bytes offset:long = Vector; +upload.getFileHashes#9156982a location:InputFileLocation offset:long = Vector; help.getConfig#c4f9186b = Config; help.getNearestDc#1fb33026 = NearestDc; @@ -1241,28 +1887,31 @@ help.getAppChangelog#9010ef6f prev_app_version:string = Updates; help.setBotUpdatesStatus#ec22cfcd pending_updates_count:int message:string = Bool; help.getCdnConfig#52029342 = CdnConfig; help.getRecentMeUrls#3dc0f114 referer:string = help.RecentMeUrls; -help.getProxyData#3d7758e1 = help.ProxyData; help.getTermsOfServiceUpdate#2ca51fd1 = help.TermsOfServiceUpdate; help.acceptTermsOfService#ee72f79a id:DataJSON = Bool; help.getDeepLinkInfo#3fedc75f path:string = help.DeepLinkInfo; -help.getAppConfig#98914110 = JSONValue; +help.getAppConfig#61e3f854 hash:int = help.AppConfig; help.saveAppLog#6f02f748 events:Vector = Bool; help.getPassportConfig#c661ad08 hash:int = help.PassportConfig; help.getSupportName#d360e72c = help.SupportName; help.getUserInfo#38a08d3 user_id:InputUser = help.UserInfo; help.editUserInfo#66b91b70 user_id:InputUser message:string entities:Vector = help.UserInfo; +help.getPromoData#c0977421 = help.PromoData; +help.hidePromoData#1e251c95 peer:InputPeer = Bool; +help.dismissSuggestion#f50dbaa1 peer:InputPeer suggestion:string = Bool; +help.getCountriesList#735787a8 lang_code:string hash:int = help.CountriesList; +help.getPremiumPromo#b81b93d4 = help.PremiumPromo; channels.readHistory#cc104937 channel:InputChannel max_id:int = Bool; channels.deleteMessages#84c1fd4e channel:InputChannel id:Vector = messages.AffectedMessages; -channels.deleteUserHistory#d10dd71b channel:InputChannel user_id:InputUser = messages.AffectedHistory; -channels.reportSpam#fe087810 channel:InputChannel user_id:InputUser id:Vector = Bool; +channels.reportSpam#f44a8315 channel:InputChannel participant:InputPeer id:Vector = Bool; channels.getMessages#ad8c9a23 channel:InputChannel id:Vector = messages.Messages; -channels.getParticipants#123e05e9 channel:InputChannel filter:ChannelParticipantsFilter offset:int limit:int hash:int = channels.ChannelParticipants; -channels.getParticipant#546dd7a6 channel:InputChannel user_id:InputUser = channels.ChannelParticipant; +channels.getParticipants#77ced9d0 channel:InputChannel filter:ChannelParticipantsFilter offset:int limit:int hash:long = channels.ChannelParticipants; +channels.getParticipant#a0ab6cc6 channel:InputChannel participant:InputPeer = channels.ChannelParticipant; channels.getChannels#a7f6bbb id:Vector = messages.Chats; channels.getFullChannel#8736a09 channel:InputChannel = messages.ChatFull; -channels.createChannel#f4893d7f flags:# broadcast:flags.0?true megagroup:flags.1?true title:string about:string = Updates; -channels.editAdmin#70f893ba channel:InputChannel user_id:InputUser admin_rights:ChatAdminRights = Updates; +channels.createChannel#91006707 flags:# broadcast:flags.0?true megagroup:flags.1?true for_import:flags.3?true forum:flags.5?true title:string about:string geo_point:flags.2?InputGeoPoint address:flags.2?string ttl_period:flags.4?int = Updates; +channels.editAdmin#d33c8902 channel:InputChannel user_id:InputUser admin_rights:ChatAdminRights rank:string = Updates; channels.editTitle#566decd0 channel:InputChannel title:string = Updates; channels.editPhoto#f12e57c9 channel:InputChannel photo:InputChatPhoto = Updates; channels.checkUsername#10e6bd2c channel:InputChannel username:string = Bool; @@ -1271,40 +1920,112 @@ channels.joinChannel#24b524c5 channel:InputChannel = Updates; channels.leaveChannel#f836aa95 channel:InputChannel = Updates; channels.inviteToChannel#199f3a6c channel:InputChannel users:Vector = Updates; channels.deleteChannel#c0111fe3 channel:InputChannel = Updates; -channels.exportMessageLink#ceb77163 channel:InputChannel id:int grouped:Bool = ExportedMessageLink; +channels.exportMessageLink#e63fadeb flags:# grouped:flags.0?true thread:flags.1?true channel:InputChannel id:int = ExportedMessageLink; channels.toggleSignatures#1f69b606 channel:InputChannel enabled:Bool = Updates; -channels.getAdminedPublicChannels#8d8d82d7 = messages.Chats; -channels.editBanned#72796912 channel:InputChannel user_id:InputUser banned_rights:ChatBannedRights = Updates; +channels.getAdminedPublicChannels#f8b036af flags:# by_location:flags.0?true check_limit:flags.1?true = messages.Chats; +channels.editBanned#96e6cd81 channel:InputChannel participant:InputPeer banned_rights:ChatBannedRights = Updates; channels.getAdminLog#33ddf480 flags:# channel:InputChannel q:string events_filter:flags.0?ChannelAdminLogEventsFilter admins:flags.1?Vector max_id:long min_id:long limit:int = channels.AdminLogResults; channels.setStickers#ea8ca4f9 channel:InputChannel stickerset:InputStickerSet = Bool; channels.readMessageContents#eab5dc38 channel:InputChannel id:Vector = Bool; -channels.deleteHistory#af369d42 channel:InputChannel max_id:int = Bool; +channels.deleteHistory#9baa9647 flags:# for_everyone:flags.0?true channel:InputChannel max_id:int = Updates; channels.togglePreHistoryHidden#eabbb94c channel:InputChannel enabled:Bool = Updates; channels.getLeftChannels#8341ecc0 offset:int = messages.Chats; +channels.getGroupsForDiscussion#f5dad378 = messages.Chats; +channels.setDiscussionGroup#40582bb2 broadcast:InputChannel group:InputChannel = Bool; +channels.editCreator#8f38cd1f channel:InputChannel user_id:InputUser password:InputCheckPasswordSRP = Updates; +channels.editLocation#58e63f6d channel:InputChannel geo_point:InputGeoPoint address:string = Bool; +channels.toggleSlowMode#edd49ef0 channel:InputChannel seconds:int = Updates; +channels.getInactiveChannels#11e831ee = messages.InactiveChats; +channels.convertToGigagroup#b290c69 channel:InputChannel = Updates; +channels.viewSponsoredMessage#beaedb94 channel:InputChannel random_id:bytes = Bool; +channels.getSponsoredMessages#ec210fbf channel:InputChannel = messages.SponsoredMessages; +channels.getSendAs#dc770ee peer:InputPeer = channels.SendAsPeers; +channels.deleteParticipantHistory#367544db channel:InputChannel participant:InputPeer = messages.AffectedHistory; +channels.toggleJoinToSend#e4cb9580 channel:InputChannel enabled:Bool = Updates; +channels.toggleJoinRequest#4c2985b6 channel:InputChannel enabled:Bool = Updates; +channels.reorderUsernames#b45ced1d channel:InputChannel order:Vector = Bool; +channels.toggleUsername#50f24105 channel:InputChannel username:string active:Bool = Bool; +channels.deactivateAllUsernames#a245dd3 channel:InputChannel = Bool; +channels.toggleForum#a4298b29 channel:InputChannel enabled:Bool = Updates; +channels.createForumTopic#f40c0224 flags:# channel:InputChannel title:string icon_color:flags.0?int icon_emoji_id:flags.3?long random_id:long send_as:flags.2?InputPeer = Updates; +channels.getForumTopics#de560d1 flags:# channel:InputChannel q:flags.0?string offset_date:int offset_id:int offset_topic:int limit:int = messages.ForumTopics; +channels.getForumTopicsByID#b0831eb9 channel:InputChannel topics:Vector = messages.ForumTopics; +channels.editForumTopic#f4dfa185 flags:# channel:InputChannel topic_id:int title:flags.0?string icon_emoji_id:flags.1?long closed:flags.2?Bool hidden:flags.3?Bool = Updates; +channels.updatePinnedForumTopic#6c2d9026 channel:InputChannel topic_id:int pinned:Bool = Updates; +channels.deleteTopicHistory#34435f2d channel:InputChannel top_msg_id:int = messages.AffectedHistory; +channels.reorderPinnedForumTopics#2950a18f flags:# force:flags.0?true channel:InputChannel order:Vector = Updates; +channels.toggleAntiSpam#68f3e4eb channel:InputChannel enabled:Bool = Updates; +channels.reportAntiSpamFalsePositive#a850a693 channel:InputChannel msg_id:int = Bool; +channels.toggleParticipantsHidden#6a6e7854 channel:InputChannel enabled:Bool = Updates; bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON; bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool; - -payments.getPaymentForm#99f09745 msg_id:int = payments.PaymentForm; -payments.getPaymentReceipt#a092a980 msg_id:int = payments.PaymentReceipt; -payments.validateRequestedInfo#770a8e74 flags:# save:flags.0?true msg_id:int info:PaymentRequestedInfo = payments.ValidatedRequestedInfo; -payments.sendPaymentForm#2b8879b3 flags:# msg_id:int requested_info_id:flags.0?string shipping_option_id:flags.1?string credentials:InputPaymentCredentials = payments.PaymentResult; +bots.setBotCommands#517165a scope:BotCommandScope lang_code:string commands:Vector = Bool; +bots.resetBotCommands#3d8de0f9 scope:BotCommandScope lang_code:string = Bool; +bots.getBotCommands#e34c0dd6 scope:BotCommandScope lang_code:string = Vector; +bots.setBotMenuButton#4504d54f user_id:InputUser button:BotMenuButton = Bool; +bots.getBotMenuButton#9c60eb28 user_id:InputUser = BotMenuButton; +bots.setBotBroadcastDefaultAdminRights#788464e1 admin_rights:ChatAdminRights = Bool; +bots.setBotGroupDefaultAdminRights#925ec9ea admin_rights:ChatAdminRights = Bool; +bots.setBotInfo#10cf3123 flags:# bot:flags.2?InputUser lang_code:string name:flags.3?string about:flags.0?string description:flags.1?string = Bool; +bots.getBotInfo#dcd914fd flags:# bot:flags.0?InputUser lang_code:string = bots.BotInfo; +bots.reorderUsernames#9709b1c2 bot:InputUser order:Vector = Bool; +bots.toggleUsername#53ca973 bot:InputUser username:string active:Bool = Bool; + +payments.getPaymentForm#37148dbb flags:# invoice:InputInvoice theme_params:flags.0?DataJSON = payments.PaymentForm; +payments.getPaymentReceipt#2478d1cc peer:InputPeer msg_id:int = payments.PaymentReceipt; +payments.validateRequestedInfo#b6c8f12b flags:# save:flags.0?true invoice:InputInvoice info:PaymentRequestedInfo = payments.ValidatedRequestedInfo; +payments.sendPaymentForm#2d03522f flags:# form_id:long invoice:InputInvoice requested_info_id:flags.0?string shipping_option_id:flags.1?string credentials:InputPaymentCredentials tip_amount:flags.2?long = payments.PaymentResult; payments.getSavedInfo#227d824b = payments.SavedInfo; payments.clearSavedInfo#d83d70c1 flags:# credentials:flags.0?true info:flags.1?true = Bool; +payments.getBankCardData#2e79d779 number:string = payments.BankCardData; +payments.exportInvoice#f91b065 invoice_media:InputMedia = payments.ExportedInvoice; +payments.assignAppStoreTransaction#80ed747d receipt:bytes purpose:InputStorePaymentPurpose = Updates; +payments.assignPlayMarketTransaction#dffd50d3 receipt:DataJSON purpose:InputStorePaymentPurpose = Updates; +payments.canPurchasePremium#9fc19eb6 purpose:InputStorePaymentPurpose = Bool; -stickers.createStickerSet#9bd86e6a flags:# masks:flags.0?true user_id:InputUser title:string short_name:string stickers:Vector = messages.StickerSet; +stickers.createStickerSet#9021ab67 flags:# masks:flags.0?true animated:flags.1?true videos:flags.4?true emojis:flags.5?true text_color:flags.6?true user_id:InputUser title:string short_name:string thumb:flags.2?InputDocument stickers:Vector software:flags.3?string = messages.StickerSet; stickers.removeStickerFromSet#f7760f51 sticker:InputDocument = messages.StickerSet; stickers.changeStickerPosition#ffb6d4ca sticker:InputDocument position:int = messages.StickerSet; stickers.addStickerToSet#8653febe stickerset:InputStickerSet sticker:InputStickerSetItem = messages.StickerSet; +stickers.setStickerSetThumb#a76a5392 flags:# stickerset:InputStickerSet thumb:flags.0?InputDocument thumb_document_id:flags.1?long = messages.StickerSet; +stickers.checkShortName#284b3639 short_name:string = Bool; +stickers.suggestShortName#4dafc503 title:string = stickers.SuggestedShortName; +stickers.changeSticker#f5537ebc flags:# sticker:InputDocument emoji:flags.0?string mask_coords:flags.1?MaskCoords keywords:flags.2?string = messages.StickerSet; +stickers.renameStickerSet#124b1c00 stickerset:InputStickerSet title:string = messages.StickerSet; +stickers.deleteStickerSet#87704394 stickerset:InputStickerSet = Bool; phone.getCallConfig#55451fa9 = DataJSON; -phone.requestCall#5b95b3d4 user_id:InputUser random_id:int g_a_hash:bytes protocol:PhoneCallProtocol = phone.PhoneCall; +phone.requestCall#42ff96ed flags:# video:flags.0?true user_id:InputUser random_id:int g_a_hash:bytes protocol:PhoneCallProtocol = phone.PhoneCall; phone.acceptCall#3bd2b4a0 peer:InputPhoneCall g_b:bytes protocol:PhoneCallProtocol = phone.PhoneCall; phone.confirmCall#2efe1722 peer:InputPhoneCall g_a:bytes key_fingerprint:long protocol:PhoneCallProtocol = phone.PhoneCall; phone.receivedCall#17d54f61 peer:InputPhoneCall = Bool; -phone.discardCall#78d413a6 peer:InputPhoneCall duration:int reason:PhoneCallDiscardReason connection_id:long = Updates; +phone.discardCall#b2cbc1c0 flags:# video:flags.0?true peer:InputPhoneCall duration:int reason:PhoneCallDiscardReason connection_id:long = Updates; phone.setCallRating#59ead627 flags:# user_initiative:flags.0?true peer:InputPhoneCall rating:int comment:string = Updates; phone.saveCallDebug#277add7e peer:InputPhoneCall debug:DataJSON = Bool; +phone.sendSignalingData#ff7a9383 peer:InputPhoneCall data:bytes = Bool; +phone.createGroupCall#48cdc6d8 flags:# rtmp_stream:flags.2?true peer:InputPeer random_id:int title:flags.0?string schedule_date:flags.1?int = Updates; +phone.joinGroupCall#b132ff7b flags:# muted:flags.0?true video_stopped:flags.2?true call:InputGroupCall join_as:InputPeer invite_hash:flags.1?string params:DataJSON = Updates; +phone.leaveGroupCall#500377f9 call:InputGroupCall source:int = Updates; +phone.inviteToGroupCall#7b393160 call:InputGroupCall users:Vector = Updates; +phone.discardGroupCall#7a777135 call:InputGroupCall = Updates; +phone.toggleGroupCallSettings#74bbb43d flags:# reset_invite_hash:flags.1?true call:InputGroupCall join_muted:flags.0?Bool = Updates; +phone.getGroupCall#41845db call:InputGroupCall limit:int = phone.GroupCall; +phone.getGroupParticipants#c558d8ab call:InputGroupCall ids:Vector sources:Vector offset:string limit:int = phone.GroupParticipants; +phone.checkGroupCall#b59cf977 call:InputGroupCall sources:Vector = Vector; +phone.toggleGroupCallRecord#f128c708 flags:# start:flags.0?true video:flags.2?true call:InputGroupCall title:flags.1?string video_portrait:flags.2?Bool = Updates; +phone.editGroupCallParticipant#a5273abf flags:# call:InputGroupCall participant:InputPeer muted:flags.0?Bool volume:flags.1?int raise_hand:flags.2?Bool video_stopped:flags.3?Bool video_paused:flags.4?Bool presentation_paused:flags.5?Bool = Updates; +phone.editGroupCallTitle#1ca6ac0a call:InputGroupCall title:string = Updates; +phone.getGroupCallJoinAs#ef7c213a peer:InputPeer = phone.JoinAsPeers; +phone.exportGroupCallInvite#e6aa647f flags:# can_self_unmute:flags.0?true call:InputGroupCall = phone.ExportedGroupCallInvite; +phone.toggleGroupCallStartSubscription#219c34e6 call:InputGroupCall subscribed:Bool = Updates; +phone.startScheduledGroupCall#5680e342 call:InputGroupCall = Updates; +phone.saveDefaultGroupCallJoinAs#575e1f8c peer:InputPeer join_as:InputPeer = Bool; +phone.joinGroupCallPresentation#cbea6bc4 call:InputGroupCall params:DataJSON = Updates; +phone.leaveGroupCallPresentation#1c50d144 call:InputGroupCall = Updates; +phone.getGroupCallStreamChannels#1ab21940 call:InputGroupCall = phone.GroupCallStreamChannels; +phone.getGroupCallStreamRtmpUrl#deb3abbf peer:InputPeer revoke:Bool = phone.GroupCallStreamRtmpUrl; +phone.saveCallLog#41248786 peer:InputPhoneCall file:InputFile = Bool; langpack.getLangPack#f2f2330a lang_pack:string lang_code:string = LangPackDifference; langpack.getStrings#efea3803 lang_pack:string lang_code:string keys:Vector = Vector; @@ -1312,7 +2033,24 @@ langpack.getDifference#cd984aa5 lang_pack:string lang_code:string from_version:i langpack.getLanguages#42c6978f lang_pack:string = Vector; langpack.getLanguage#6a596502 lang_pack:string lang_code:string = LangPackLanguage; -// LAYER 97 - -// Ports -channels.exportInvite#c7560885 channel:InputChannel = ExportedChatInvite; \ No newline at end of file +folders.editPeerFolders#6847d0ab folder_peers:Vector = Updates; + +stats.getBroadcastStats#ab42441a flags:# dark:flags.0?true channel:InputChannel = stats.BroadcastStats; +stats.loadAsyncGraph#621d5fa0 flags:# token:string x:flags.0?long = StatsGraph; +stats.getMegagroupStats#dcdf8607 flags:# dark:flags.0?true channel:InputChannel = stats.MegagroupStats; +stats.getMessagePublicForwards#5630281b channel:InputChannel msg_id:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages; +stats.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats; + +chatlists.exportChatlistInvite#8472478e chatlist:InputChatlist title:string peers:Vector = chatlists.ExportedChatlistInvite; +chatlists.deleteExportedInvite#719c5c5e chatlist:InputChatlist slug:string = Bool; +chatlists.editExportedInvite#653db63d flags:# chatlist:InputChatlist slug:string title:flags.1?string peers:flags.2?Vector = ExportedChatlistInvite; +chatlists.getExportedInvites#ce03da83 chatlist:InputChatlist = chatlists.ExportedInvites; +chatlists.checkChatlistInvite#41c10fff slug:string = chatlists.ChatlistInvite; +chatlists.joinChatlistInvite#a6b1e39a slug:string peers:Vector = Updates; +chatlists.getChatlistUpdates#89419521 chatlist:InputChatlist = chatlists.ChatlistUpdates; +chatlists.joinChatlistUpdates#e089f8f5 chatlist:InputChatlist peers:Vector = Updates; +chatlists.hideChatlistUpdates#66e486fb chatlist:InputChatlist = Bool; +chatlists.getLeaveChatlistSuggestions#fdbcd714 chatlist:InputChatlist = Vector; +chatlists.leaveChatlist#74fae13a chatlist:InputChatlist peers:Vector = Updates; + +// LAYER 158 diff --git a/compiler/api/source/sys_msgs.tl b/compiler/api/source/sys_msgs.tl index 067ab91ef12..6a3f6325dc3 100644 --- a/compiler/api/source/sys_msgs.tl +++ b/compiler/api/source/sys_msgs.tl @@ -53,6 +53,15 @@ ipPortSecret#37982646 ipv4:int port:int secret:bytes = IpPort; accessPointRule#4679b65f phone_prefix_rules:string dc_id:int ips:vector = AccessPointRule; help.configSimple#5a592a6c date:int expires:int rules:vector = help.ConfigSimple; +// tlsClientHello blocks:vector = TlsClientHello; +// +// tlsBlockString data:string = TlsBlock; +// tlsBlockRandom length:int = TlsBlock; +// tlsBlockZero length:int = TlsBlock; +// tlsBlockDomain = TlsBlock; +// tlsBlockGrease seed:int = TlsBlock; +// tlsBlockScope entries:Vector = TlsBlock; + ---functions--- rpc_drop_answer#58e4a740 req_msg_id:long = RpcDropAnswer; diff --git a/compiler/api/template/combinator.txt b/compiler/api/template/combinator.txt new file mode 100644 index 00000000000..fa7a76976b3 --- /dev/null +++ b/compiler/api/template/combinator.txt @@ -0,0 +1,35 @@ +{notice} + +from io import BytesIO + +from pyrogram.raw.core.primitives import Int, Long, Int128, Int256, Bool, Bytes, String, Double, Vector +from pyrogram.raw.core import TLObject +from pyrogram import raw +from typing import List, Optional, Any + +{warning} + + +class {name}(TLObject): # type: ignore + """{docstring} + """ + + __slots__: List[str] = [{slots}] + + ID = {id} + QUALNAME = "{qualname}" + + def __init__(self{arguments}) -> None: + {fields} + + @staticmethod + def read(b: BytesIO, *args: Any) -> "{name}": + {read_types} + return {name}({return_arguments}) + + def write(self, *args) -> bytes: + b = BytesIO() + b.write(Int(self.ID, False)) + + {write_types} + return b.getvalue() diff --git a/compiler/api/template/mtproto.txt b/compiler/api/template/mtproto.txt deleted file mode 100644 index c63525d6f01..00000000000 --- a/compiler/api/template/mtproto.txt +++ /dev/null @@ -1,30 +0,0 @@ -{notice} - -from io import BytesIO - -from pyrogram.api.core import * - - -class {class_name}(Object): - """{docstring_args} - """ - - __slots__ = [{slots}] - - ID = {object_id} - QUALNAME = "{qualname}" - - def __init__(self{arguments}): - {fields} - - @staticmethod - def read(b: BytesIO, *args) -> "{class_name}": - {read_types} - return {class_name}({return_arguments}) - - def write(self) -> bytes: - b = BytesIO() - b.write(Int(self.ID, False)) - - {write_types} - return b.getvalue() diff --git a/compiler/api/template/pyrogram.txt b/compiler/api/template/pyrogram.txt deleted file mode 100644 index 00ad8e33170..00000000000 --- a/compiler/api/template/pyrogram.txt +++ /dev/null @@ -1,12 +0,0 @@ -{notice} - -from pyrogram.api.core import Object - - -class {class_name}(Object): - """{docstring_args} - """ - ID = {object_id} - - def __init__(self{arguments}): - {fields} diff --git a/compiler/api/template/type.txt b/compiler/api/template/type.txt new file mode 100644 index 00000000000..99310359d51 --- /dev/null +++ b/compiler/api/template/type.txt @@ -0,0 +1,23 @@ +{notice} + +{warning} + +from typing import Union +from pyrogram import raw +from pyrogram.raw.core import TLObject + +{name} = Union[{types}] + + +# noinspection PyRedeclaration +class {name}: # type: ignore + """{docstring} + """ + + QUALNAME = "pyrogram.raw.base.{qualname}" + + def __init__(self): + raise TypeError("Base types can only be used for type checking purposes: " + "you tried to use a base type instance as argument, " + "but you need to instantiate one of its constructors instead. " + "More info: https://docs.pyrogram.org/telegram/base/{doc_name}") diff --git a/compiler/docs/__init__.py b/compiler/docs/__init__.py index f3769dd4d0d..46887cb7a57 100644 --- a/compiler/docs/__init__.py +++ b/compiler/docs/__init__.py @@ -1,17 +1,17 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2019 Dan Tès +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan # -# This file is part of Pyrogram. +# 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 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. +# 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 . +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . diff --git a/compiler/docs/compiler.py b/compiler/docs/compiler.py index 6ea2240d9ea..12a4d972212 100644 --- a/compiler/docs/compiler.py +++ b/compiler/docs/compiler.py @@ -1,36 +1,42 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2019 Dan Tès +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan # -# This file is part of Pyrogram. +# 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 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. +# 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 . +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . import ast import os +import re import shutil HOME = "compiler/docs" -DESTINATION = "docs/source" +DESTINATION = "docs/source/telegram" +PYROGRAM_API_DEST = "docs/source/api" -FUNCTIONS_PATH = "pyrogram/api/functions" -TYPES_PATH = "pyrogram/api/types" +FUNCTIONS_PATH = "pyrogram/raw/functions" +TYPES_PATH = "pyrogram/raw/types" +BASE_PATH = "pyrogram/raw/base" FUNCTIONS_BASE = "functions" TYPES_BASE = "types" +BASE_BASE = "base" -shutil.rmtree(TYPES_BASE, ignore_errors=True) -shutil.rmtree(FUNCTIONS_BASE, ignore_errors=True) + +def snek(s: str): + s = re.sub(r"(.)([A-Z][a-z]+)", r"\1_\2", s) + return re.sub(r"([a-z0-9])([A-Z])", r"\1_\2", s).lower() def generate(source_path, base): @@ -50,22 +56,30 @@ def build(path, level=0): for node in ast.walk(p): if isinstance(node, ast.ClassDef): name = node.name + break + else: + continue - # name = "".join([str(j.title()) for j in os.path.splitext(i)[0].split("_")]) - full_path = os.path.basename(path) + "/" + name + ".rst" + full_path = os.path.basename(path) + "/" + snek(name).replace("_", "-") + ".rst" if level: full_path = base + "/" + full_path + namespace = path.split("/")[-1] + if namespace in ["base", "types", "functions"]: + namespace = "" + + full_name = f"{(namespace + '.') if namespace else ''}{name}" + os.makedirs(os.path.dirname(DESTINATION + "/" + full_path), exist_ok=True) with open(DESTINATION + "/" + full_path, "w", encoding="utf-8") as f: f.write( page_template.format( - title=name, - title_markup="=" * len(name), - full_class_path="pyrogram.api.{}".format( - os.path.splitext(full_path)[0].replace("/", ".") + title=full_name, + title_markup="=" * len(full_name), + full_class_path="pyrogram.raw.{}".format( + ".".join(full_path.split("/")[:-1]) + "." + name ) ) ) @@ -82,22 +96,23 @@ def build(path, level=0): entities = [] for i in v: - entities.append(i) + entities.append(f'{i} <{snek(i).replace("_", "-")}>') if k != base: inner_path = base + "/" + k + "/index" + ".rst" - module = "pyrogram.api.{}.{}".format(base, k) + module = "pyrogram.raw.{}.{}".format(base, k) else: for i in sorted(list(all_entities), reverse=True): if i != base: entities.insert(0, "{0}/index".format(i)) inner_path = base + "/index" + ".rst" - module = "pyrogram.api.{}".format(base) + module = "pyrogram.raw.{}".format(base) with open(DESTINATION + "/" + inner_path, "w", encoding="utf-8") as f: if k == base: f.write(":tocdepth: 1\n\n") + k = "Raw " + k f.write( toctree.format( @@ -111,10 +126,522 @@ def build(path, level=0): f.write("\n") +def pyrogram_api(): + def get_title_list(s: str) -> list: + return [i.strip() for i in [j.strip() for j in s.split("\n") if j] if i] + + # Methods + + categories = dict( + utilities=""" + Utilities + start + stop + run + restart + add_handler + remove_handler + stop_transmission + export_session_string + set_parse_mode + """, + messages=""" + Messages + send_message + forward_messages + copy_message + copy_media_group + send_photo + send_audio + send_document + send_sticker + send_video + send_animation + send_voice + send_video_note + send_media_group + send_location + send_venue + send_contact + send_cached_media + send_reaction + edit_message_text + edit_message_caption + edit_message_media + edit_message_reply_markup + edit_inline_text + edit_inline_caption + edit_inline_media + edit_inline_reply_markup + send_chat_action + delete_messages + get_messages + get_media_group + get_chat_history + get_chat_history_count + read_chat_history + send_poll + vote_poll + stop_poll + retract_vote + send_dice + search_messages + search_messages_count + search_global + search_global_count + download_media + stream_media + get_discussion_message + get_discussion_replies + get_discussion_replies_count + get_custom_emoji_stickers + """, + chats=""" + Chats + join_chat + leave_chat + ban_chat_member + unban_chat_member + restrict_chat_member + promote_chat_member + set_administrator_title + set_chat_photo + delete_chat_photo + set_chat_title + set_chat_description + set_chat_permissions + pin_chat_message + unpin_chat_message + unpin_all_chat_messages + get_chat + get_chat_member + get_chat_members + get_chat_members_count + get_dialogs + get_dialogs_count + set_chat_username + get_nearby_chats + archive_chats + unarchive_chats + add_chat_members + create_channel + create_group + create_supergroup + delete_channel + delete_supergroup + delete_user_history + set_slow_mode + mark_chat_unread + get_chat_event_log + get_chat_online_count + get_send_as_chats + set_send_as_chat + set_chat_protected_content + """, + users=""" + Users + get_me + get_users + get_chat_photos + get_chat_photos_count + set_profile_photo + delete_profile_photos + set_username + update_profile + block_user + unblock_user + get_common_chats + get_default_emoji_statuses + set_emoji_status + """, + invite_links=""" + Invite Links + get_chat_invite_link + export_chat_invite_link + create_chat_invite_link + edit_chat_invite_link + revoke_chat_invite_link + delete_chat_invite_link + get_chat_invite_link_joiners + get_chat_invite_link_joiners_count + get_chat_admin_invite_links + get_chat_admin_invite_links_count + get_chat_admins_with_invite_links + get_chat_join_requests + delete_chat_admin_invite_links + approve_chat_join_request + approve_all_chat_join_requests + decline_chat_join_request + decline_all_chat_join_requests + """, + contacts=""" + Contacts + add_contact + delete_contacts + import_contacts + get_contacts + get_contacts_count + """, + password=""" + Password + enable_cloud_password + change_cloud_password + remove_cloud_password + """, + bots=""" + Bots + get_inline_bot_results + send_inline_bot_result + answer_callback_query + answer_inline_query + request_callback_answer + send_game + set_game_score + get_game_high_scores + set_bot_commands + get_bot_commands + delete_bot_commands + set_bot_default_privileges + get_bot_default_privileges + set_chat_menu_button + get_chat_menu_button + answer_web_app_query + """, + authorization=""" + Authorization + connect + disconnect + initialize + terminate + send_code + resend_code + sign_in + sign_in_bot + sign_up + get_password_hint + check_password + send_recovery_code + recover_password + accept_terms_of_service + log_out + """, + advanced=""" + Advanced + invoke + resolve_peer + save_file + """ + ) + + root = PYROGRAM_API_DEST + "/methods" + + shutil.rmtree(root, ignore_errors=True) + os.mkdir(root) + + with open(HOME + "/template/methods.rst") as f: + template = f.read() + + with open(root + "/index.rst", "w") as f: + fmt_keys = {} + + for k, v in categories.items(): + name, *methods = get_title_list(v) + fmt_keys.update({k: "\n ".join("{0} <{0}>".format(m) for m in methods)}) + + for method in methods: + with open(root + "/{}.rst".format(method), "w") as f2: + title = "{}()".format(method) + + f2.write(title + "\n" + "=" * len(title) + "\n\n") + f2.write(".. automethod:: pyrogram.Client.{}()".format(method)) + + functions = ["idle", "compose"] + + for func in functions: + with open(root + "/{}.rst".format(func), "w") as f2: + title = "{}()".format(func) + + f2.write(title + "\n" + "=" * len(title) + "\n\n") + f2.write(".. autofunction:: pyrogram.{}()".format(func)) + + f.write(template.format(**fmt_keys)) + + # Types + + categories = dict( + users_chats=""" + Users & Chats + User + Chat + ChatPreview + ChatPhoto + ChatMember + ChatPermissions + ChatPrivileges + ChatInviteLink + ChatAdminWithInviteLinks + ChatEvent + ChatEventFilter + ChatMemberUpdated + ChatJoinRequest + ChatJoiner + Dialog + Restriction + EmojiStatus + """, + messages_media=""" + Messages & Media + Message + MessageEntity + Photo + Thumbnail + Audio + Document + Animation + Video + Voice + VideoNote + Contact + Location + Venue + Sticker + Game + WebPage + Poll + PollOption + Dice + Reaction + VideoChatScheduled + VideoChatStarted + VideoChatEnded + VideoChatMembersInvited + WebAppData + MessageReactions + ChatReactions + """, + bot_keyboards=""" + Bot keyboards + ReplyKeyboardMarkup + KeyboardButton + ReplyKeyboardRemove + InlineKeyboardMarkup + InlineKeyboardButton + LoginUrl + ForceReply + CallbackQuery + GameHighScore + CallbackGame + WebAppInfo + MenuButton + MenuButtonCommands + MenuButtonWebApp + MenuButtonDefault + SentWebAppMessage + """, + bot_commands=""" + Bot commands + BotCommand + BotCommandScope + BotCommandScopeDefault + BotCommandScopeAllPrivateChats + BotCommandScopeAllGroupChats + BotCommandScopeAllChatAdministrators + BotCommandScopeChat + BotCommandScopeChatAdministrators + BotCommandScopeChatMember + """, + input_media=""" + Input Media + InputMedia + InputMediaPhoto + InputMediaVideo + InputMediaAudio + InputMediaAnimation + InputMediaDocument + InputPhoneContact + """, + inline_mode=""" + Inline Mode + InlineQuery + InlineQueryResult + InlineQueryResultCachedAudio + InlineQueryResultCachedDocument + InlineQueryResultCachedAnimation + InlineQueryResultCachedPhoto + InlineQueryResultCachedSticker + InlineQueryResultCachedVideo + InlineQueryResultCachedVoice + InlineQueryResultArticle + InlineQueryResultAudio + InlineQueryResultContact + InlineQueryResultDocument + InlineQueryResultAnimation + InlineQueryResultLocation + InlineQueryResultPhoto + InlineQueryResultVenue + InlineQueryResultVideo + InlineQueryResultVoice + ChosenInlineResult + """, + input_message_content=""" + InputMessageContent + InputMessageContent + InputTextMessageContent + """, + authorization=""" + Authorization + SentCode + TermsOfService + """ + ) + + root = PYROGRAM_API_DEST + "/types" + + shutil.rmtree(root, ignore_errors=True) + os.mkdir(root) + + with open(HOME + "/template/types.rst") as f: + template = f.read() + + with open(root + "/index.rst", "w") as f: + fmt_keys = {} + + for k, v in categories.items(): + name, *types = get_title_list(v) + + fmt_keys.update({k: "\n ".join(types)}) + + # noinspection PyShadowingBuiltins + for type in types: + with open(root + "/{}.rst".format(type), "w") as f2: + title = "{}".format(type) + + f2.write(title + "\n" + "=" * len(title) + "\n\n") + f2.write(".. autoclass:: pyrogram.types.{}()\n".format(type)) + + f.write(template.format(**fmt_keys)) + + # Bound Methods + + categories = dict( + message=""" + Message + Message.click + Message.delete + Message.download + Message.forward + Message.copy + Message.pin + Message.unpin + Message.edit + Message.edit_text + Message.edit_caption + Message.edit_media + Message.edit_reply_markup + Message.reply + Message.reply_text + Message.reply_animation + Message.reply_audio + Message.reply_cached_media + Message.reply_chat_action + Message.reply_contact + Message.reply_document + Message.reply_game + Message.reply_inline_bot_result + Message.reply_location + Message.reply_media_group + Message.reply_photo + Message.reply_poll + Message.reply_sticker + Message.reply_venue + Message.reply_video + Message.reply_video_note + Message.reply_voice + Message.get_media_group + Message.react + """, + chat=""" + Chat + Chat.archive + Chat.unarchive + Chat.set_title + Chat.set_description + Chat.set_photo + Chat.ban_member + Chat.unban_member + Chat.restrict_member + Chat.promote_member + Chat.get_member + Chat.get_members + Chat.add_members + Chat.join + Chat.leave + Chat.mark_unread + Chat.set_protected_content + Chat.unpin_all_messages + """, + user=""" + User + User.archive + User.unarchive + User.block + User.unblock + """, + callback_query=""" + Callback Query + CallbackQuery.answer + CallbackQuery.edit_message_text + CallbackQuery.edit_message_caption + CallbackQuery.edit_message_media + CallbackQuery.edit_message_reply_markup + """, + inline_query=""" + InlineQuery + InlineQuery.answer + """, + chat_join_request=""" + ChatJoinRequest + ChatJoinRequest.approve + ChatJoinRequest.decline + """ + ) + + root = PYROGRAM_API_DEST + "/bound-methods" + + shutil.rmtree(root, ignore_errors=True) + os.mkdir(root) + + with open(HOME + "/template/bound-methods.rst") as f: + template = f.read() + + with open(root + "/index.rst", "w") as f: + fmt_keys = {} + + for k, v in categories.items(): + name, *bound_methods = get_title_list(v) + + fmt_keys.update({"{}_hlist".format(k): "\n ".join("- :meth:`~{}`".format(bm) for bm in bound_methods)}) + + fmt_keys.update( + {"{}_toctree".format(k): "\n ".join("{} <{}>".format(bm.split(".")[1], bm) for bm in bound_methods)}) + + # noinspection PyShadowingBuiltins + for bm in bound_methods: + with open(root + "/{}.rst".format(bm), "w") as f2: + title = "{}()".format(bm) + + f2.write(title + "\n" + "=" * len(title) + "\n\n") + f2.write(".. automethod:: pyrogram.types.{}()".format(bm)) + + f.write(template.format(**fmt_keys)) + + def start(): global page_template global toctree + shutil.rmtree(DESTINATION, ignore_errors=True) + with open(HOME + "/template/page.txt", encoding="utf-8") as f: page_template = f.read() @@ -123,12 +650,16 @@ def start(): generate(TYPES_PATH, TYPES_BASE) generate(FUNCTIONS_PATH, FUNCTIONS_BASE) + generate(BASE_PATH, BASE_BASE) + pyrogram_api() if "__main__" == __name__: - FUNCTIONS_PATH = "../../pyrogram/api/functions" - TYPES_PATH = "../../pyrogram/api/types" + FUNCTIONS_PATH = "../../pyrogram/raw/functions" + TYPES_PATH = "../../pyrogram/raw/types" + BASE_PATH = "../../pyrogram/raw/base" HOME = "." - DESTINATION = "../../docs/source" + DESTINATION = "../../docs/source/telegram" + PYROGRAM_API_DEST = "../../docs/source/api" start() diff --git a/compiler/docs/template/bound-methods.rst b/compiler/docs/template/bound-methods.rst new file mode 100644 index 00000000000..1e16e32ce9d --- /dev/null +++ b/compiler/docs/template/bound-methods.rst @@ -0,0 +1,103 @@ +Bound Methods +============= + +Some Pyrogram types define what are called bound methods. Bound methods are functions attached to a type which are +accessed via an instance of that type. They make it even easier to call specific methods by automatically inferring +some of the required arguments. + +.. code-block:: python + + from pyrogram import Client + + app = Client("my_account") + + + @app.on_message() + def hello(client, message) + message.reply("hi") + + + app.run() + +----- + +.. currentmodule:: pyrogram.types + +Message +------- + +.. hlist:: + :columns: 3 + + {message_hlist} + +.. toctree:: + :hidden: + + {message_toctree} + +Chat +---- + +.. hlist:: + :columns: 4 + + {chat_hlist} + +.. toctree:: + :hidden: + + {chat_toctree} + +User +---- + +.. hlist:: + :columns: 2 + + {user_hlist} + +.. toctree:: + :hidden: + + {user_toctree} + +CallbackQuery +------------- + +.. hlist:: + :columns: 3 + + {callback_query_hlist} + +.. toctree:: + :hidden: + + {callback_query_toctree} + +InlineQuery +----------- + +.. hlist:: + :columns: 2 + + {inline_query_hlist} + +.. toctree:: + :hidden: + + {inline_query_toctree} + +ChatJoinRequest +--------------- + +.. hlist:: + :columns: 2 + + {chat_join_request_hlist} + +.. toctree:: + :hidden: + + {chat_join_request_toctree} + diff --git a/compiler/docs/template/methods.rst b/compiler/docs/template/methods.rst new file mode 100644 index 00000000000..f76e249defd --- /dev/null +++ b/compiler/docs/template/methods.rst @@ -0,0 +1,168 @@ +Available Methods +================= + +This page is about Pyrogram methods. All the methods listed here are bound to a :class:`~pyrogram.Client` instance, +except for :meth:`~pyrogram.idle()` and :meth:`~pyrogram.compose()`, which are special functions that can be found in +the main package directly. + +.. code-block:: python + + from pyrogram import Client + + app = Client("my_account") + + with app: + app.send_message("me", "hi") + +----- + +.. currentmodule:: pyrogram.Client + +Utilities +--------- + +.. autosummary:: + :nosignatures: + + {utilities} + +.. toctree:: + :hidden: + + {utilities} + +.. currentmodule:: pyrogram + +.. autosummary:: + :nosignatures: + + idle + compose + +.. toctree:: + :hidden: + + idle + compose + +.. currentmodule:: pyrogram.Client + +Messages +-------- + +.. autosummary:: + :nosignatures: + + {messages} + +.. toctree:: + :hidden: + + {messages} + +Chats +----- + +.. autosummary:: + :nosignatures: + + {chats} + +.. toctree:: + :hidden: + + {chats} + +Users +----- + +.. autosummary:: + :nosignatures: + + {users} + +.. toctree:: + :hidden: + + {users} + +Invite Links +------------ + +.. autosummary:: + :nosignatures: + + {invite_links} + +.. toctree:: + :hidden: + + {invite_links} + +Contacts +-------- + +.. autosummary:: + :nosignatures: + + {contacts} + +.. toctree:: + :hidden: + + {contacts} + +Password +-------- + +.. autosummary:: + :nosignatures: + + {password} + +.. toctree:: + :hidden: + + {password} + +Bots +---- + +.. autosummary:: + :nosignatures: + + {bots} + +.. toctree:: + :hidden: + + {bots} + +Authorization +------------- + +.. autosummary:: + :nosignatures: + + {authorization} + +.. toctree:: + :hidden: + + {authorization} + +Advanced +-------- + +Methods used only when dealing with the raw Telegram API. +Learn more about how to use the raw API at :doc:`Advanced Usage <../../topics/advanced-usage>`. + +.. autosummary:: + :nosignatures: + + {advanced} + +.. toctree:: + :hidden: + + {advanced} \ No newline at end of file diff --git a/compiler/docs/template/page.txt b/compiler/docs/template/page.txt index 25a396fae6e..638a10cf118 100644 --- a/compiler/docs/template/page.txt +++ b/compiler/docs/template/page.txt @@ -1,5 +1,5 @@ {title} {title_markup} -.. autoclass:: {full_class_path} +.. autoclass:: {full_class_path}() :members: diff --git a/compiler/docs/template/toctree.txt b/compiler/docs/template/toctree.txt index 717276c40ec..7a36e4a5921 100644 --- a/compiler/docs/template/toctree.txt +++ b/compiler/docs/template/toctree.txt @@ -4,4 +4,6 @@ .. module:: {module} .. toctree:: + :titlesonly: + {entities} \ No newline at end of file diff --git a/compiler/docs/template/types.rst b/compiler/docs/template/types.rst new file mode 100644 index 00000000000..e404ad4cdfa --- /dev/null +++ b/compiler/docs/template/types.rst @@ -0,0 +1,126 @@ +Available Types +=============== + +This page is about Pyrogram Types. All types listed here are available through the ``pyrogram.types`` package. +Unless required as argument to a client method, most of the types don't need to be manually instantiated because they +are only returned by other methods. You also don't need to import them, unless you want to type-hint your variables. + +.. code-block:: python + + from pyrogram.types import User, Message, ... + +.. note:: + + Optional fields always exist inside the object, but they could be empty and contain the value of ``None``. + Empty fields aren't shown when, for example, using ``print(message)`` and this means that + ``hasattr(message, "photo")`` always returns ``True``. + + To tell whether a field is set or not, do a simple boolean check: ``if message.photo: ...``. + +----- + +.. currentmodule:: pyrogram.types + +Users & Chats +------------- + +.. autosummary:: + :nosignatures: + + {users_chats} + +.. toctree:: + :hidden: + + {users_chats} + +Messages & Media +---------------- + +.. autosummary:: + :nosignatures: + + {messages_media} + +.. toctree:: + :hidden: + + {messages_media} + +Bot keyboards +------------- + +.. autosummary:: + :nosignatures: + + {bot_keyboards} + +.. toctree:: + :hidden: + + {bot_keyboards} + +Bot commands +------------- + +.. autosummary:: + :nosignatures: + + {bot_commands} + +.. toctree:: + :hidden: + + {bot_commands} + +Input Media +----------- + +.. autosummary:: + :nosignatures: + + {input_media} + +.. toctree:: + :hidden: + + {input_media} + +Inline Mode +----------- + +.. autosummary:: + :nosignatures: + + {inline_mode} + +.. toctree:: + :hidden: + + {inline_mode} + +InputMessageContent +------------------- + +.. autosummary:: + :nosignatures: + + {input_message_content} + +.. toctree:: + :hidden: + + {input_message_content} + +Authorization +------------- + +.. autosummary:: + :nosignatures: + + {authorization} + +.. toctree:: + :hidden: + + {authorization} \ No newline at end of file diff --git a/compiler/error/__init__.py b/compiler/error/__init__.py deleted file mode 100644 index f3769dd4d0d..00000000000 --- a/compiler/error/__init__.py +++ /dev/null @@ -1,17 +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 . diff --git a/compiler/error/source/303_SEE_OTHER.tsv b/compiler/error/source/303_SEE_OTHER.tsv deleted file mode 100644 index da3ac18d97f..00000000000 --- a/compiler/error/source/303_SEE_OTHER.tsv +++ /dev/null @@ -1,5 +0,0 @@ -id message -FILE_MIGRATE_X The file to be accessed is currently stored in DC{x} -PHONE_MIGRATE_X The phone number a user is trying to use for authorization is associated with DC{x} -NETWORK_MIGRATE_X The source IP address is associated with DC{x} (for registration) -USER_MIGRATE_X The user whose identity is being used to execute queries is associated with DC{x} (for registration) diff --git a/compiler/error/source/400_BAD_REQUEST.tsv b/compiler/error/source/400_BAD_REQUEST.tsv deleted file mode 100644 index 8325040d092..00000000000 --- a/compiler/error/source/400_BAD_REQUEST.tsv +++ /dev/null @@ -1,99 +0,0 @@ -id message -FIRSTNAME_INVALID The first name is invalid -LASTNAME_INVALID The last name is invalid -PHONE_NUMBER_INVALID The phone number is invalid -PHONE_CODE_HASH_EMPTY phone_code_hash is missing -PHONE_CODE_EMPTY phone_code is missing -PHONE_CODE_EXPIRED The confirmation code has expired -PHONE_CODE_INVALID The confirmation code is invalid -API_ID_INVALID The api_id/api_hash combination is invalid -PHONE_NUMBER_OCCUPIED The phone number is already in use -PHONE_NUMBER_UNOCCUPIED The phone number is not yet being used -USERS_TOO_FEW Not enough users (to create a chat, for example) -USERS_TOO_MUCH The maximum number of users has been exceeded (to create a chat, for example) -TYPE_CONSTRUCTOR_INVALID The type constructor is invalid -FILE_PART_INVALID The file part number is invalid -FILE_PARTS_INVALID The number of file parts is invalid -FILE_PART_X_MISSING Part {x} of the file is missing from storage -MD5_CHECKSUM_INVALID The MD5 checksums do not match -PHOTO_INVALID_DIMENSIONS The photo dimensions are invalid -FIELD_NAME_INVALID The field with the name FIELD_NAME is invalid -FIELD_NAME_EMPTY The field with the name FIELD_NAME is missing -MSG_WAIT_FAILED A waiting call returned an error -PEER_ID_INVALID The id/access_hash combination is invalid -MESSAGE_EMPTY The message sent is empty -ENCRYPTED_MESSAGE_INVALID The special binding message (bind_auth_key_inner) contains invalid data -INPUT_METHOD_INVALID The method called is invalid -PASSWORD_HASH_INVALID Two-step verification password is invalid -USERNAME_NOT_OCCUPIED The username is not occupied by anyone -USERNAME_INVALID The username is invalid -MESSAGE_ID_INVALID The message id is invalid -MESSAGE_NOT_MODIFIED The message was not modified -ENTITY_MENTION_USER_INVALID The mentioned entity is not an user -MESSAGE_TOO_LONG The message text is over 4096 characters -ACCESS_TOKEN_EXPIRED The bot token is invalid -BOT_METHOD_INVALID The method can't be used by bots -QUERY_TOO_SHORT The query is too short -SEARCH_QUERY_EMPTY The query is empty -CHAT_ID_INVALID The chat id is invalid -DATE_EMPTY The date argument is empty -PERSISTENT_TIMESTAMP_EMPTY The pts is empty -CDN_METHOD_INVALID The method can't be used on CDN DCs -VOLUME_LOC_NOT_FOUND The volume location can't be found -FILE_ID_INVALID The file id is invalid -LOCATION_INVALID The file location is invalid -CHAT_ADMIN_REQUIRED The method requires chat admin privileges -PHONE_NUMBER_BANNED The phone number is banned -ABOUT_TOO_LONG The about text is too long -MULTI_MEDIA_TOO_LONG The album contains more than 10 items -USERNAME_OCCUPIED The username is already in use -BOT_INLINE_DISABLED The inline feature of the bot is disabled -INLINE_RESULT_EXPIRED The inline bot query expired -INVITE_HASH_INVALID The invite link hash is invalid -USER_ALREADY_PARTICIPANT The user is already a participant of this chat -TTL_MEDIA_INVALID The media does not support self-destruction -MAX_ID_INVALID The max_id parameter is invalid -CHANNEL_INVALID The channel parameter is invalid -DC_ID_INVALID The dc_id parameter is invalid -LIMIT_INVALID The limit parameter is invalid -OFFSET_INVALID The offset parameter is invalid -EMAIL_INVALID The email provided is invalid -USER_IS_BOT A bot cannot send messages to other bots or to itself -WEBPAGE_CURL_FAILED Telegram server could not fetch the provided URL -STICKERSET_INVALID The requested sticker set is invalid -PEER_FLOOD The method can't be used because your account is limited -MEDIA_CAPTION_TOO_LONG The media caption is longer than 1024 characters -USER_NOT_MUTUAL_CONTACT The user is not a mutual contact -USER_CHANNELS_TOO_MUCH The user is already in too many channels or supergroups -API_ID_PUBLISHED_FLOOD You are using an API key that is limited on the server side -USER_NOT_PARTICIPANT The user is not a member of this chat -CHANNEL_PRIVATE The channel/supergroup is not accessible -MESSAGE_IDS_EMPTY The requested message doesn't exist -WEBPAGE_MEDIA_EMPTY The URL doesn't contain any valid media -QUERY_ID_INVALID The callback query id is invalid -MEDIA_EMPTY The media is invalid -USER_IS_BLOCKED The user blocked you -YOU_BLOCKED_USER You blocked this user -ADMINS_TOO_MUCH The chat has too many administrators -BOTS_TOO_MUCH The chat has too many bots -USER_ADMIN_INVALID The action requires admin privileges -INPUT_USER_DEACTIVATED The target user has been deactivated -PASSWORD_RECOVERY_NA The password recovery e-mail is not available -PASSWORD_EMPTY The password entered is empty -PHONE_NUMBER_FLOOD This number has tried to login too many times -TAKEOUT_INVALID The takeout id is invalid -TAKEOUT_REQUIRED The method must be invoked inside a takeout session -MESSAGE_POLL_CLOSED You can't interact with a closed poll -MEDIA_INVALID The media is invalid -BOT_SCORE_NOT_MODIFIED The bot score was not modified -USER_BOT_REQUIRED The method can be used by bots only -IMAGE_PROCESS_FAILED The server failed to process your image -USERNAME_NOT_MODIFIED The username was not modified -CALL_ALREADY_ACCEPTED The call is already accepted -CALL_ALREADY_DECLINED The call is already declined -PHOTO_EXT_INVALID The photo extension is invalid -EXTERNAL_URL_INVALID The external media URL is invalid -CHAT_NOT_MODIFIED The chat settings were not modified -RESULTS_TOO_MUCH The result contains too many items -RESULT_ID_DUPLICATE The result contains items with duplicated identifiers -ACCESS_TOKEN_INVALID The bot access token is invalid \ No newline at end of file diff --git a/compiler/error/source/403_FORBIDDEN.tsv b/compiler/error/source/403_FORBIDDEN.tsv deleted file mode 100644 index 34433da7931..00000000000 --- a/compiler/error/source/403_FORBIDDEN.tsv +++ /dev/null @@ -1,5 +0,0 @@ -id message -CHAT_WRITE_FORBIDDEN You don't have rights to send messages in this chat -RIGHT_FORBIDDEN One or more admin rights can't be applied to this kind of chat (channel/supergroup) -CHAT_ADMIN_INVITE_REQUIRED You don't have rights to invite other users -MESSAGE_DELETE_FORBIDDEN You don't have rights to delete messages in this chat \ No newline at end of file diff --git a/compiler/error/source/406_NOT_ACCEPTABLE.tsv b/compiler/error/source/406_NOT_ACCEPTABLE.tsv deleted file mode 100644 index e94706ed90a..00000000000 --- a/compiler/error/source/406_NOT_ACCEPTABLE.tsv +++ /dev/null @@ -1,3 +0,0 @@ -id message -AUTH_KEY_DUPLICATED Authorization error - you must delete your session file and log in again with your phone number -FILEREF_UPGRADE_NEEDED The file reference has expired - you must obtain the original media message \ No newline at end of file diff --git a/compiler/error/source/420_FLOOD.tsv b/compiler/error/source/420_FLOOD.tsv deleted file mode 100644 index 3d5ceabd2d4..00000000000 --- a/compiler/error/source/420_FLOOD.tsv +++ /dev/null @@ -1,3 +0,0 @@ -id message -FLOOD_WAIT_X A wait of {x} seconds is required -TAKEOUT_INIT_DELAY_X You have to confirm the data export request using one of your mobile devices or wait {x} seconds diff --git a/compiler/error/source/500_INTERNAL_SERVER_ERROR.tsv b/compiler/error/source/500_INTERNAL_SERVER_ERROR.tsv deleted file mode 100644 index d1c666c68c2..00000000000 --- a/compiler/error/source/500_INTERNAL_SERVER_ERROR.tsv +++ /dev/null @@ -1,8 +0,0 @@ -id message -AUTH_RESTART User authorization has restarted -RPC_CALL_FAIL Telegram is having internal problems. Please try again later -RPC_MCGET_FAIL Telegram is having internal problems. Please try again later -PERSISTENT_TIMESTAMP_OUTDATED Telegram is having internal problems. Please try again later -HISTORY_GET_FAILED Telegram is having internal problems. Please try again later -REG_ID_GENERATE_FAILED Telegram is having internal problems. Please try again later -RANDOM_ID_DUPLICATE Telegram is having internal problems. Please try again later \ No newline at end of file diff --git a/compiler/errors/__init__.py b/compiler/errors/__init__.py new file mode 100644 index 00000000000..46887cb7a57 --- /dev/null +++ b/compiler/errors/__init__.py @@ -0,0 +1,17 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . diff --git a/compiler/error/compiler.py b/compiler/errors/compiler.py similarity index 74% rename from compiler/error/compiler.py rename to compiler/errors/compiler.py index 996c4981f50..0c4ef399b67 100644 --- a/compiler/error/compiler.py +++ b/compiler/errors/compiler.py @@ -1,27 +1,27 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2019 Dan Tès +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan # -# This file is part of Pyrogram. +# 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 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. +# 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 . +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . import csv import os import re import shutil -HOME = "compiler/error" +HOME = "compiler/errors" DEST = "pyrogram/errors/exceptions" NOTICE_PATH = "NOTICE" @@ -81,6 +81,8 @@ def start(): sub_classes = [] + f_all.write(" \"_\": \"{}\",\n".format(super_class)) + for j, row in enumerate(reader): if j == 0: continue @@ -90,13 +92,15 @@ def start(): if not row: # Row is empty (blank line) continue - id, message = row + error_id, error_message = row - sub_class = caml(re.sub(r"_X", "_", id)) + sub_class = caml(re.sub(r"_X", "_", error_id)) + sub_class = re.sub(r"^2", "Two", sub_class) + sub_class = re.sub(r" ", "", sub_class) - f_all.write(" \"{}\": \"{}\",\n".format(id, sub_class)) + f_all.write(" \"{}\": \"{}\",\n".format(error_id, sub_class)) - sub_classes.append((sub_class, id, message)) + sub_classes.append((sub_class, error_id, error_message)) with open("{}/template/class.txt".format(HOME), "r", encoding="utf-8") as f_class_template: class_template = f_class_template.read() @@ -129,8 +133,6 @@ def start(): with open("{}/all.py".format(DEST), "w", encoding="utf-8") as f: f.write(re.sub("{count}", str(count), content)) - print("Compiling Errors: [100%]") - if "__main__" == __name__: HOME = "." diff --git a/compiler/errors/sort.py b/compiler/errors/sort.py new file mode 100644 index 00000000000..db94e35162d --- /dev/null +++ b/compiler/errors/sort.py @@ -0,0 +1,35 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import csv +from pathlib import Path + +for p in Path("source").glob("*.tsv"): + with open(p) as f: + reader = csv.reader(f, delimiter="\t") + dct = {k: v for k, v in reader if k != "id"} + keys = sorted(dct) + + with open(p, "w") as f: + f.write("id\tmessage\n") + + for i, item in enumerate(keys, start=1): + f.write(f"{item}\t{dct[item]}") + + if i != len(keys): + f.write("\n") diff --git a/compiler/errors/source/303_SEE_OTHER.tsv b/compiler/errors/source/303_SEE_OTHER.tsv new file mode 100644 index 00000000000..301660cefe0 --- /dev/null +++ b/compiler/errors/source/303_SEE_OTHER.tsv @@ -0,0 +1,6 @@ +id message +FILE_MIGRATE_X The file to be accessed is currently stored in DC{value} +NETWORK_MIGRATE_X The source IP address is associated with DC{value} (for registration) +PHONE_MIGRATE_X The phone number a user is trying to use for authorization is associated with DC{value} +STATS_MIGRATE_X The statistics of the group/channel are stored in DC{value} +USER_MIGRATE_X The user whose identity is being used to execute queries is associated with DC{value} (for registration) \ No newline at end of file diff --git a/compiler/errors/source/400_BAD_REQUEST.tsv b/compiler/errors/source/400_BAD_REQUEST.tsv new file mode 100644 index 00000000000..08ecb7a6762 --- /dev/null +++ b/compiler/errors/source/400_BAD_REQUEST.tsv @@ -0,0 +1,360 @@ +id message +ABOUT_TOO_LONG The provided about/bio text is too long +ACCESS_TOKEN_EXPIRED The bot token has expired +ACCESS_TOKEN_INVALID The bot access token is invalid +ADMINS_TOO_MUCH The chat has too many administrators +ADMIN_RANK_EMOJI_NOT_ALLOWED Emoji are not allowed in custom administrator titles +ADMIN_RANK_INVALID The custom administrator title is invalid or too long +ALBUM_PHOTOS_TOO_MANY Too many photos were included in the album +API_ID_INVALID The api_id/api_hash combination is invalid +API_ID_PUBLISHED_FLOOD You are using an API key that is limited on the server side because it was published somewhere +ARTICLE_TITLE_EMPTY The article title is empty +AUDIO_TITLE_EMPTY The title attribute of the audio is empty +AUTH_BYTES_INVALID The authorization bytes are invalid +AUTH_TOKEN_ALREADY_ACCEPTED The authorization token was already used +AUTH_TOKEN_EXPIRED The provided authorization token has expired and the updated QR-code must be re-scanned +AUTH_TOKEN_INVALID An invalid authorization token was provided +AUTOARCHIVE_NOT_AVAILABLE This feature is not yet enabled for your account due to it not receiving too many private messages from strangers +BANK_CARD_NUMBER_INVALID The credit card number is invalid +BANNED_RIGHTS_INVALID You provided a set of restrictions that is invalid +BASE_PORT_LOC_INVALID The base port location is invalid +BOTS_TOO_MUCH The chat has too many bots +BOT_CHANNELS_NA Bots can't edit admin privileges +BOT_COMMAND_DESCRIPTION_INVALID The command description was empty, too long or had invalid characters +BOT_DOMAIN_INVALID The domain used for the auth button does not match the one configured in @BotFather +BOT_GAMES_DISABLED Bot games cannot be used in this type of chat +BOT_GROUPS_BLOCKED This bot can't be added to groups +BOT_INLINE_DISABLED The inline feature of the bot is disabled +BOT_INVALID This is not a valid bot +BOT_METHOD_INVALID The method can't be used by bots +BOT_MISSING This method can only be run by a bot +BOT_ONESIDE_NOT_AVAIL Bots can't pin messages for one side only in private chats +BOT_PAYMENTS_DISABLED This method can only be run by a bot +BOT_POLLS_DISABLED Sending polls by bots has been disabled +BOT_RESPONSE_TIMEOUT The bot did not answer to the callback query in time +BOT_SCORE_NOT_MODIFIED The bot score was not modified +BROADCAST_ID_INVALID The channel is invalid +BROADCAST_PUBLIC_VOTERS_FORBIDDEN Polls with public voters cannot be sent in channels +BROADCAST_REQUIRED The request can only be used with a channel +BUTTON_DATA_INVALID The button callback data is invalid or too large +BUTTON_TYPE_INVALID The type of one of the buttons you provided is invalid +BUTTON_URL_INVALID The button url is invalid +BUTTON_USER_PRIVACY_RESTRICTED The privacy settings of the user specified in a keyboard button do not allow creating such button +CALL_ALREADY_ACCEPTED The call is already accepted +CALL_ALREADY_DECLINED The call is already declined +CALL_PEER_INVALID The provided call peer object is invalid +CALL_PROTOCOL_FLAGS_INVALID Call protocol flags invalid +CDN_METHOD_INVALID The method can't be used on CDN DCs +CHANNELS_ADMIN_PUBLIC_TOO_MUCH You are an administrator of too many public channels +CHANNELS_TOO_MUCH You have joined too many channels or supergroups, leave some and try again +CHANNEL_ADD_INVALID Internal error. +CHANNEL_BANNED The channel is banned +CHANNEL_INVALID The channel parameter is invalid +CHANNEL_PRIVATE The channel/supergroup is not accessible +CHANNEL_TOO_LARGE The channel is too large +CHAT_ABOUT_NOT_MODIFIED The chat about text was not modified because you tried to edit it using the same content +CHAT_ABOUT_TOO_LONG The chat about text is too long +CHAT_ADMIN_REQUIRED The method requires chat admin privileges +CHAT_FORWARDS_RESTRICTED The chat restricts forwarding content +CHAT_ID_EMPTY The provided chat id is empty +CHAT_ID_INVALID The chat id being used is invalid or not known yet. Make sure you see the chat before interacting with it +CHAT_INVALID The chat is invalid +CHAT_INVITE_PERMANENT The chat invite link is primary +CHAT_LINK_EXISTS The action failed because the supergroup is linked to a channel +CHAT_NOT_MODIFIED The chat settings (title, permissions, photo, etc..) were not modified because you tried to edit them using the same content +CHAT_RESTRICTED The chat is restricted and cannot be used +CHAT_SEND_INLINE_FORBIDDEN You cannot use inline bots to send messages in this chat +CHAT_TITLE_EMPTY The chat title is empty +CHAT_TOO_BIG The chat is too big for this action +CODE_EMPTY The provided code is empty +CODE_HASH_INVALID The provided code hash invalid +CODE_INVALID The provided code is invalid (i.e. from email) +CONNECTION_API_ID_INVALID The provided API id is invalid +CONNECTION_APP_VERSION_EMPTY App version is empty +CONNECTION_DEVICE_MODEL_EMPTY The device model is empty +CONNECTION_LANG_PACK_INVALID The specified language pack is not valid +CONNECTION_LAYER_INVALID The connection layer is invalid. Missing InvokeWithLayer-InitConnection call +CONNECTION_NOT_INITED The connection was not initialized +CONNECTION_SYSTEM_EMPTY The connection to the system is empty +CONNECTION_SYSTEM_LANG_CODE_EMPTY The system language code is empty +CONTACT_ADD_MISSING Contact to add is missing +CONTACT_ID_INVALID The provided contact id is invalid +CONTACT_NAME_EMPTY The provided contact name is empty +CONTACT_REQ_MISSING Missing contact request +DATA_INVALID The encrypted data is invalid +DATA_JSON_INVALID The provided JSON data is invalid +DATA_TOO_LONG Data too long +DATE_EMPTY The date argument is empty +DC_ID_INVALID The dc_id parameter is invalid +DH_G_A_INVALID The g_a parameter invalid +DOCUMENT_INVALID The document is invalid +EMAIL_HASH_EXPIRED The email hash expired and cannot be used to verify it +EMAIL_INVALID The email provided is invalid +EMAIL_UNCONFIRMED Email unconfirmed +EMAIL_UNCONFIRMED_X The provided email isn't confirmed, {value} is the length of the verification code that was just sent to the email +EMAIL_VERIFY_EXPIRED The verification email has expired +EMOTICON_EMPTY The emoticon parameter is empty +EMOTICON_INVALID The emoticon parameter is invalid +EMOTICON_STICKERPACK_MISSING The emoticon sticker pack you are trying to obtain is missing +ENCRYPTED_MESSAGE_INVALID The special binding message (bind_auth_key_inner) contains invalid data +ENCRYPTION_ALREADY_ACCEPTED The secret chat is already accepted +ENCRYPTION_ALREADY_DECLINED The secret chat is already declined +ENCRYPTION_DECLINED The secret chat was declined +ENCRYPTION_ID_INVALID The provided secret chat id is invalid +ENTITIES_TOO_LONG The entity provided contains data that is too long, or you passed too many entities to this message +ENTITY_BOUNDS_INVALID The message entity bounds are invalid +ENTITY_MENTION_USER_INVALID The mentioned entity is not an user +ERROR_TEXT_EMPTY The provided error message is empty +EXPIRE_DATE_INVALID The expiration date is invalid +EXPORT_CARD_INVALID The provided card is invalid +EXTERNAL_URL_INVALID The external media URL is invalid +FIELD_NAME_EMPTY The field with the name FIELD_NAME is missing +FIELD_NAME_INVALID The field with the name FIELD_NAME is invalid +FILE_ID_INVALID The file id is invalid +FILE_MIGRATE_X The file is in Data Center No. {value} +FILE_PARTS_INVALID Invalid number of parts. +FILE_PART_EMPTY The file part sent is empty +FILE_PART_INVALID The file part number is invalid. +FILE_PART_LENGTH_INVALID The length of a file part is invalid +FILE_PART_SIZE_CHANGED The part size is different from the size of one of the previous parts in the same file +FILE_PART_SIZE_INVALID The file part size is invalid +FILE_PART_TOO_BIG The size limit for the content of the file part has been exceeded +FILE_PART_X_MISSING Part {value} of the file is missing from storage +FILE_REFERENCE_EMPTY The file id contains an empty file reference, you must obtain a valid one by fetching the message from the origin context +FILE_REFERENCE_EXPIRED The file id contains an expired file reference, you must obtain a valid one by fetching the message from the origin context +FILE_REFERENCE_INVALID The file id contains an invalid file reference, you must obtain a valid one by fetching the message from the origin context +FILTER_ID_INVALID The specified filter ID is invalid +FIRSTNAME_INVALID The first name is invalid +FOLDER_ID_EMPTY The folder you tried to delete was already empty +FOLDER_ID_INVALID The folder id is invalid +FRESH_CHANGE_ADMINS_FORBIDDEN You can't change administrator settings in this chat because your session was logged-in recently +FROM_MESSAGE_BOT_DISABLED Bots can't use fromMessage min constructors +FROM_PEER_INVALID The from peer value is invalid +GAME_BOT_INVALID You cannot send that game with the current bot +GEO_POINT_INVALID Invalid geo point provided +GIF_CONTENT_TYPE_INVALID GIF content-type invalid +GIF_ID_INVALID The provided gif/animation id is invalid +GRAPH_INVALID_RELOAD Invalid graph token provided, please reload the stats and provide the updated token +GRAPH_OUTDATED_RELOAD The graph data is outdated +GROUPCALL_SSRC_DUPLICATE_MUCH Too many group call synchronization source duplicates +GROUPED_MEDIA_INVALID The album contains invalid media +GROUP_CALL_INVALID The group call is invalid +HASH_INVALID The provided hash is invalid +IMAGE_PROCESS_FAILED The server failed to process your image +IMPORT_FILE_INVALID The imported file is invalid +IMPORT_FORMAT_UNRECOGNIZED The imported format is unrecognized +IMPORT_ID_INVALID The import id is invalid +INLINE_RESULT_EXPIRED The inline bot query expired +INPUT_CONSTRUCTOR_INVALID The provided constructor is invalid +INPUT_FETCH_ERROR An error occurred while deserializing TL parameters +INPUT_FETCH_FAIL Failed deserializing TL payload +INPUT_FILTER_INVALID The filter is invalid for this query +INPUT_LAYER_INVALID The provided layer is invalid +INPUT_METHOD_INVALID The method invoked is invalid in the current schema +INPUT_REQUEST_TOO_LONG The input request is too long +INPUT_USER_DEACTIVATED The target user has been deleted/deactivated +INVITE_HASH_EMPTY The invite hash is empty +INVITE_HASH_EXPIRED The chat invite link is no longer valid +INVITE_HASH_INVALID The invite link hash is invalid +INVITE_REQUEST_SENT The request to join this chat or channel has been successfully sent +INVITE_REVOKED_MISSING The action required a chat invite link to be revoked first +LANG_PACK_INVALID The provided language pack is invalid +LASTNAME_INVALID The last name is invalid +LIMIT_INVALID The limit parameter is invalid +LINK_NOT_MODIFIED The chat link was not modified because you tried to link to the same target +LOCATION_INVALID The file location is invalid +MAX_ID_INVALID The max_id parameter is invalid +MAX_QTS_INVALID The provided QTS is invalid +MD5_CHECKSUM_INVALID The file's checksum did not match the md5_checksum parameter +MEDIA_CAPTION_TOO_LONG The media caption is too long +MEDIA_EMPTY The media you tried to send is invalid +MEDIA_INVALID The media is invalid +MEDIA_NEW_INVALID The new media to edit the message with is invalid +MEDIA_PREV_INVALID The previous media cannot be edited with anything else +MEGAGROUP_ID_INVALID The supergroup is invalid +MEGAGROUP_PREHISTORY_HIDDEN The action failed because the supergroup has the pre-history hidden +MEGAGROUP_REQUIRED The request can only be used with a supergroup +MESSAGE_EDIT_TIME_EXPIRED You can no longer edit this message because too much time has passed +MESSAGE_EMPTY The message sent is empty or contains invalid characters +MESSAGE_IDS_EMPTY The requested message doesn't exist or you provided no message id +MESSAGE_ID_INVALID The message id is invalid +MESSAGE_NOT_MODIFIED The message was not modified because you tried to edit it using the same content +MESSAGE_POLL_CLOSED You can't interact with a closed poll +MESSAGE_TOO_LONG The message text is too long +METHOD_INVALID The API method is invalid and cannot be used +MSG_ID_INVALID The message ID used in the peer was invalid +MSG_WAIT_FAILED A waiting call returned an error +MULTI_MEDIA_TOO_LONG The album/media group contains too many items +NEW_SALT_INVALID The new salt is invalid +NEW_SETTINGS_INVALID The new settings are invalid +NEXT_OFFSET_INVALID The next offset value is invalid +OFFSET_INVALID The offset parameter is invalid +OFFSET_PEER_ID_INVALID The provided offset peer is invalid +OPTIONS_TOO_MUCH The poll options are too many +OPTION_INVALID The option specified is invalid and does not exist in the target poll +PACK_SHORT_NAME_INVALID Invalid sticker pack name. It must begin with a letter, can't contain consecutive underscores and must end in '_by_'. +PACK_SHORT_NAME_OCCUPIED A sticker pack with this name already exists +PACK_TITLE_INVALID The sticker pack title is invalid +PARTICIPANTS_TOO_FEW The chat doesn't have enough participants +PARTICIPANT_VERSION_OUTDATED The other participant is using an outdated Telegram app version +PASSWORD_EMPTY The password provided is empty +PASSWORD_HASH_INVALID The two-step verification password is invalid +PASSWORD_MISSING The account is missing the two-step verification password +PASSWORD_RECOVERY_NA The password recovery e-mail is not available +PASSWORD_REQUIRED The two-step verification password is required for this method +PASSWORD_TOO_FRESH_X The two-step verification password was added recently and you are required to wait {value} seconds +PAYMENT_PROVIDER_INVALID The payment provider was not recognised or its token was invalid +PEER_FLOOD The method can't be used because your account is currently limited +PEER_ID_INVALID The peer id being used is invalid or not known yet. Make sure you meet the peer before interacting with it +PEER_ID_NOT_SUPPORTED The provided peer id is not supported +PERSISTENT_TIMESTAMP_EMPTY The pts argument is empty +PERSISTENT_TIMESTAMP_INVALID The persistent timestamp is invalid +PHONE_CODE_EMPTY The phone code is missing +PHONE_CODE_EXPIRED The confirmation code has expired +PHONE_CODE_HASH_EMPTY The phone code hash is missing +PHONE_CODE_INVALID The confirmation code is invalid +PHONE_NUMBER_APP_SIGNUP_FORBIDDEN You can't sign up using this app +PHONE_NUMBER_BANNED The phone number is banned from Telegram and cannot be used +PHONE_NUMBER_FLOOD This number has tried to login too many times +PHONE_NUMBER_INVALID The phone number is invalid +PHONE_NUMBER_OCCUPIED The phone number is already in use +PHONE_NUMBER_UNOCCUPIED The phone number is not yet being used +PHONE_PASSWORD_PROTECTED The phone is password protected +PHOTO_CONTENT_TYPE_INVALID The photo content type is invalid +PHOTO_CONTENT_URL_EMPTY The photo content URL is empty +PHOTO_CROP_FILE_MISSING Photo crop file missing +PHOTO_CROP_SIZE_SMALL The photo is too small +PHOTO_EXT_INVALID The photo extension is invalid +PHOTO_FILE_MISSING Profile photo file missing +PHOTO_ID_INVALID The photo id is invalid +PHOTO_INVALID The photo is invalid +PHOTO_INVALID_DIMENSIONS The photo dimensions are invalid +PHOTO_SAVE_FILE_INVALID The photo you tried to send cannot be saved by Telegram +PHOTO_THUMB_URL_EMPTY The photo thumb URL is empty +PHOTO_THUMB_URL_INVALID The photo thumb URL is invalid +PINNED_DIALOGS_TOO_MUCH Too many pinned dialogs +PIN_RESTRICTED You can't pin messages in private chats with other people +POLL_ANSWERS_INVALID The poll answers are invalid +POLL_OPTION_DUPLICATE A duplicate option was sent in the same poll +POLL_OPTION_INVALID A poll option used invalid data (the data may be too long) +POLL_QUESTION_INVALID The poll question is invalid +POLL_UNSUPPORTED This layer does not support polls in the invoked method +POLL_VOTE_REQUIRED Cast a vote in the poll before calling this method +PRIVACY_KEY_INVALID The privacy key is invalid +PRIVACY_TOO_LONG Your privacy exception list has exceeded the maximum capacity +PRIVACY_VALUE_INVALID The privacy value is invalid +QUERY_ID_EMPTY The query ID is empty +QUERY_ID_INVALID The callback query id is invalid +QUERY_TOO_SHORT The query is too short +QUIZ_CORRECT_ANSWERS_EMPTY The correct answers of the quiz are empty +QUIZ_CORRECT_ANSWERS_TOO_MUCH The quiz contains too many correct answers +QUIZ_CORRECT_ANSWER_INVALID The correct answers of the quiz are invalid +QUIZ_MULTIPLE_INVALID A quiz can't have multiple answers +RANDOM_ID_EMPTY The random ID is empty +RANDOM_ID_INVALID The provided random ID is invalid +RANDOM_LENGTH_INVALID The random length is invalid +RANGES_INVALID Invalid range provided +REACTION_EMPTY The reaction provided is empty +REACTION_INVALID Invalid reaction provided (only valid emoji are allowed) +REFLECTOR_NOT_AVAILABLE The call reflector is not available +REPLY_MARKUP_BUY_EMPTY Reply markup for buy button empty +REPLY_MARKUP_GAME_EMPTY The provided reply markup for the game is empty +REPLY_MARKUP_INVALID The provided reply markup is invalid +REPLY_MARKUP_TOO_LONG The reply markup is too long +RESULTS_TOO_MUCH The result contains too many items +RESULT_ID_DUPLICATE The result contains items with duplicated identifiers +RESULT_ID_EMPTY Result ID empty +RESULT_ID_INVALID The given result cannot be used to send the selection to the bot +RESULT_TYPE_INVALID The result type is invalid +REVOTE_NOT_ALLOWED You cannot change your vote +RSA_DECRYPT_FAILED Internal RSA decryption failed +SCHEDULE_BOT_NOT_ALLOWED Bots are not allowed to schedule messages +SCHEDULE_DATE_INVALID Invalid schedule date provided +SCHEDULE_DATE_TOO_LATE The date you tried to schedule is too far in the future (more than one year) +SCHEDULE_STATUS_PRIVATE You cannot schedule a message until the person comes online if their privacy does not show this information +SCHEDULE_TOO_MUCH You tried to schedule too many messages in this chat +SEARCH_QUERY_EMPTY The search query is empty +SECONDS_INVALID The seconds interval is invalid +SEND_MESSAGE_MEDIA_INVALID The message media is invalid +SEND_MESSAGE_TYPE_INVALID The message type is invalid +SESSION_TOO_FRESH_X You can't do this action because the current session was logged-in recently +SETTINGS_INVALID Invalid settings were provided +SHA256_HASH_INVALID The provided SHA256 hash is invalid +SHORTNAME_OCCUPY_FAILED An error occurred when trying to register the short-name used for the sticker pack. Try a different name +SLOWMODE_MULTI_MSGS_DISABLED Slowmode is enabled, you cannot forward multiple messages to this group +SMS_CODE_CREATE_FAILED An error occurred while creating the SMS code +SRP_ID_INVALID Invalid SRP ID provided +SRP_PASSWORD_CHANGED The password has changed +START_PARAM_EMPTY The start parameter is empty +START_PARAM_INVALID The start parameter is invalid +START_PARAM_TOO_LONG The start parameter is too long +STICKERSET_INVALID The requested sticker set is invalid +STICKERSET_NOT_MODIFIED The sticker set is not modified +STICKERS_EMPTY The sticker provided is empty +STICKERS_TOO_MUCH Too many stickers in the set +STICKER_DOCUMENT_INVALID The sticker document is invalid +STICKER_EMOJI_INVALID The sticker emoji is invalid +STICKER_FILE_INVALID The sticker file is invalid +STICKER_ID_INVALID The provided sticker id is invalid +STICKER_INVALID The provided sticker is invalid +STICKER_PNG_DIMENSIONS The sticker png dimensions are invalid +STICKER_PNG_NOPNG Stickers must be png files but the provided image was not a png +STICKER_TGS_NOTGS A tgs sticker file was expected, but something else was provided +STICKER_THUMB_PNG_NOPNG A png sticker thumbnail file was expected, but something else was provided +STICKER_VIDEO_NOWEBM A webm video file was expected, but something else was provided +TAKEOUT_INVALID The takeout id is invalid +TAKEOUT_REQUIRED The method must be invoked inside a takeout session +TEMP_AUTH_KEY_EMPTY The temporary auth key provided is empty +THEME_FILE_INVALID Invalid theme file provided +THEME_FORMAT_INVALID Invalid theme format provided +THEME_INVALID Invalid theme provided +THEME_MIME_INVALID You cannot create this theme because the mime-type is invalid +TMP_PASSWORD_DISABLED The temporary password is disabled +TMP_PASSWORD_INVALID The temporary password is invalid +TOKEN_INVALID The provided token is invalid +TTL_DAYS_INVALID The provided TTL days is invalid +TTL_MEDIA_INVALID The media does not support self-destruction +TYPES_EMPTY The types parameter is empty +TYPE_CONSTRUCTOR_INVALID The type constructor is invalid +UNTIL_DATE_INVALID That date parameter is invalid +URL_INVALID The URL provided is invalid +USAGE_LIMIT_INVALID The usage limit is invalid +USERNAME_INVALID The username is invalid +USERNAME_NOT_MODIFIED The username was not modified because you tried to edit it using the same one +USERNAME_NOT_OCCUPIED The username is not occupied by anyone +USERNAME_OCCUPIED The username is already in use by someone else +USERPIC_UPLOAD_REQUIRED You are required to upload a profile picture for this action +USERS_TOO_FEW Not enough users (to create a chat, for example) +USERS_TOO_MUCH The maximum number of users has been exceeded (to create a chat, for example) +USER_ADMIN_INVALID The action requires admin privileges. Probably you tried to edit admin privileges on someone you don't have rights to +USER_ALREADY_PARTICIPANT The user is already a participant of this chat +USER_BANNED_IN_CHANNEL You are limited from sending messages in supergroups/channels, check @SpamBot for details +USER_BLOCKED The user is blocked +USER_BOT Bots in channels can only be administrators, not members. +USER_BOT_INVALID This method can only be used by a bot +USER_BOT_REQUIRED The method can be used by bots only +USER_CHANNELS_TOO_MUCH The user is already in too many channels or supergroups +USER_CREATOR You can't leave this channel because you're its creator +USER_ID_INVALID The user id being used is invalid or not known yet. Make sure you meet the user before interacting with it +USER_INVALID The provided user is invalid +USER_IS_BLOCKED The user blocked you +USER_IS_BOT A bot cannot send messages to other bots or to itself +USER_KICKED This user was kicked from this chat +USER_NOT_MUTUAL_CONTACT The user is not a mutual contact +USER_NOT_PARTICIPANT The user is not a member of this chat +VIDEO_CONTENT_TYPE_INVALID The video content type is invalid (i.e.: not streamable) +VIDEO_FILE_INVALID The video file is invalid +VOICE_MESSAGES_FORBIDDEN Voice messages are restricted +VOLUME_LOC_NOT_FOUND The volume location can't be found +WALLPAPER_FILE_INVALID The provided file cannot be used as a wallpaper +WALLPAPER_INVALID The input wallpaper was not valid +WALLPAPER_MIME_INVALID The wallpaper mime type is invalid +WC_CONVERT_URL_INVALID WC convert URL invalid +WEBDOCUMENT_INVALID The web document is invalid +WEBDOCUMENT_MIME_INVALID The web document mime type is invalid +WEBDOCUMENT_SIZE_TOO_BIG The web document is too big +WEBDOCUMENT_URL_EMPTY The web document URL is empty +WEBDOCUMENT_URL_INVALID The web document URL is invalid +WEBPAGE_CURL_FAILED Telegram server could not fetch the provided URL +WEBPAGE_MEDIA_EMPTY The URL doesn't contain any valid media +YOU_BLOCKED_USER You blocked this user \ No newline at end of file diff --git a/compiler/error/source/401_UNAUTHORIZED.tsv b/compiler/errors/source/401_UNAUTHORIZED.tsv similarity index 63% rename from compiler/error/source/401_UNAUTHORIZED.tsv rename to compiler/errors/source/401_UNAUTHORIZED.tsv index 54b24dd7074..32e0265f53f 100644 --- a/compiler/error/source/401_UNAUTHORIZED.tsv +++ b/compiler/errors/source/401_UNAUTHORIZED.tsv @@ -1,9 +1,10 @@ id message -AUTH_KEY_UNREGISTERED The key is not registered in the system -AUTH_KEY_INVALID The key is invalid -USER_DEACTIVATED The user has been deleted/deactivated -SESSION_REVOKED The authorization has been invalidated, because of the user terminating all sessions -SESSION_EXPIRED The authorization has expired ACTIVE_USER_REQUIRED The method is only available to already activated users +AUTH_KEY_INVALID The key is invalid AUTH_KEY_PERM_EMPTY The method is unavailable for temporary authorization key, not bound to permanent -SESSION_PASSWORD_NEEDED Two-step verification password required +AUTH_KEY_UNREGISTERED The key is not registered in the system. Delete your session file and login again +SESSION_EXPIRED The authorization has expired +SESSION_PASSWORD_NEEDED The two-step verification is enabled and a password is required +SESSION_REVOKED The authorization has been invalidated, because of the user terminating all sessions +USER_DEACTIVATED The user has been deleted/deactivated +USER_DEACTIVATED_BAN The user has been deleted/deactivated \ No newline at end of file diff --git a/compiler/errors/source/403_FORBIDDEN.tsv b/compiler/errors/source/403_FORBIDDEN.tsv new file mode 100644 index 00000000000..027f2e852e8 --- /dev/null +++ b/compiler/errors/source/403_FORBIDDEN.tsv @@ -0,0 +1,28 @@ +id message +BROADCAST_FORBIDDEN The request can't be used in channels +CHANNEL_PUBLIC_GROUP_NA The channel/supergroup is not available +CHAT_ADMIN_INVITE_REQUIRED You don't have rights to invite other users +CHAT_ADMIN_REQUIRED The method requires chat admin privileges +CHAT_FORBIDDEN You cannot write in this chat +CHAT_SEND_GIFS_FORBIDDEN You can't send animations in this chat +CHAT_SEND_INLINE_FORBIDDEN You cannot use inline bots to send messages in this chat +CHAT_SEND_MEDIA_FORBIDDEN You can't send media messages in this chat +CHAT_SEND_POLL_FORBIDDEN You can't send polls in this chat +CHAT_SEND_STICKERS_FORBIDDEN You can't send stickers in this chat +CHAT_WRITE_FORBIDDEN You don't have rights to send messages in this chat +EDIT_BOT_INVITE_FORBIDDEN Bots' chat invite links can't be edited +INLINE_BOT_REQUIRED The action must be performed through an inline bot callback +MESSAGE_AUTHOR_REQUIRED You are not the author of this message +MESSAGE_DELETE_FORBIDDEN You don't have rights to delete messages in this chat, most likely because you are not the author of them +POLL_VOTE_REQUIRED Cast a vote in the poll before calling this method +PREMIUM_ACCOUNT_REQUIRED This action requires a premium account +RIGHT_FORBIDDEN You don't have enough rights for this action, or you tried to set one or more admin rights that can't be applied to this kind of chat (channel or supergroup) +SENSITIVE_CHANGE_FORBIDDEN Your sensitive content settings can't be changed at this time +TAKEOUT_REQUIRED The method must be invoked inside a takeout session +USER_BOT_INVALID This method can only be called by a bot +USER_CHANNELS_TOO_MUCH One of the users you tried to add is already in too many channels/supergroups +USER_INVALID The provided user is invalid +USER_IS_BLOCKED The user is blocked +USER_NOT_MUTUAL_CONTACT The provided user is not a mutual contact +USER_PRIVACY_RESTRICTED The user's privacy settings is preventing you to perform this action +USER_RESTRICTED You are limited/restricted. You can't perform this action \ No newline at end of file diff --git a/compiler/errors/source/406_NOT_ACCEPTABLE.tsv b/compiler/errors/source/406_NOT_ACCEPTABLE.tsv new file mode 100644 index 00000000000..c2df2384dd4 --- /dev/null +++ b/compiler/errors/source/406_NOT_ACCEPTABLE.tsv @@ -0,0 +1,13 @@ +id message +AUTH_KEY_DUPLICATED The same authorization key (session file) was used in more than one place simultaneously. You must delete your session file and log in again with your phone number or bot token +CHANNEL_PRIVATE The channel/supergroup is not accessible +FILEREF_UPGRADE_NEEDED The file reference has expired and you must use a refreshed one by obtaining the original media message +FRESH_CHANGE_ADMINS_FORBIDDEN You were just elected admin, you can't add or modify other admins yet +FRESH_CHANGE_PHONE_FORBIDDEN You can't change your phone number because your session was logged-in recently +FRESH_RESET_AUTHORISATION_FORBIDDEN You can't terminate other authorized sessions because the current was logged-in recently +PHONE_NUMBER_INVALID The phone number is invalid +PHONE_PASSWORD_FLOOD You have tried to log-in too many times +STICKERSET_INVALID The sticker set is invalid +STICKERSET_OWNER_ANONYMOUS This sticker set can't be used as the group's sticker set because it was created by one of its anonymous admins +USERPIC_UPLOAD_REQUIRED You must have a profile picture to publish your geolocation +USER_RESTRICTED You are limited/restricted. You can't perform this action \ No newline at end of file diff --git a/compiler/errors/source/420_FLOOD.tsv b/compiler/errors/source/420_FLOOD.tsv new file mode 100644 index 00000000000..575cc2f5b56 --- /dev/null +++ b/compiler/errors/source/420_FLOOD.tsv @@ -0,0 +1,6 @@ +id message +2FA_CONFIRM_WAIT_X A wait of {value} seconds is required because this account is active and protected by a 2FA password +FLOOD_TEST_PHONE_WAIT_X A wait of {value} seconds is required in the test servers +FLOOD_WAIT_X A wait of {value} seconds is required +SLOWMODE_WAIT_X A wait of {value} seconds is required to send messages in this chat. +TAKEOUT_INIT_DELAY_X You have to confirm the data export request using one of your mobile devices or wait {value} seconds \ No newline at end of file diff --git a/compiler/errors/source/500_INTERNAL_SERVER_ERROR.tsv b/compiler/errors/source/500_INTERNAL_SERVER_ERROR.tsv new file mode 100644 index 00000000000..abfc57a39d4 --- /dev/null +++ b/compiler/errors/source/500_INTERNAL_SERVER_ERROR.tsv @@ -0,0 +1,45 @@ +id message +API_CALL_ERROR API call error due to Telegram having internal problems. Please try again later +AUTH_RESTART User authorization has restarted +CALL_OCCUPY_FAILED The call failed because the user is already making another call +CHAT_ID_GENERATE_FAILED Failure while generating the chat ID due to Telegram having internal problems. Please try again later +CHAT_OCCUPY_LOC_FAILED An internal error occurred while creating the chat +CHAT_OCCUPY_USERNAME_FAILED Failure to occupy chat username due to Telegram having internal problems. Please try again later +CHP_CALL_FAIL Telegram is having internal problems. Please try again later +ENCRYPTION_OCCUPY_ADMIN_FAILED Failed occupying memory for admin info due to Telegram having internal problems. Please try again later +ENCRYPTION_OCCUPY_FAILED Internal server error while accepting secret chat +FOLDER_DEAC_AUTOFIX_ALL Telegram is having internal problems. Please try again later +GROUPCALL_ADD_PARTICIPANTS_FAILED Failure while adding voice chat member due to Telegram having internal problems. Please try again later +GROUPED_ID_OCCUPY_FAILED Telegram is having internal problems. Please try again later +HISTORY_GET_FAILED The chat history couldn't be retrieved due to Telegram having internal problems. Please try again later +IMAGE_ENGINE_DOWN Image engine down due to Telegram having internal problems. Please try again later +INTERDC_X_CALL_ERROR An error occurred while Telegram was intercommunicating with DC{value}. Please try again later +INTERDC_X_CALL_RICH_ERROR A rich error occurred while Telegram was intercommunicating with DC{value}. Please try again later +MEMBER_FETCH_FAILED Telegram is having internal problems. Please try again later +MEMBER_NO_LOCATION Couldn't find the member's location due to Telegram having internal problems. Please try again later +MEMBER_OCCUPY_PRIMARY_LOC_FAILED Telegram is having internal problems. Please try again later +MEMBER_OCCUPY_USERNAME_FAILED Failure to occupy member username due to Telegram having internal problems. Please try again later +MSGID_DECREASE_RETRY Telegram is having internal problems. Please try again later +MSG_RANGE_UNSYNC Message range unsynchronized due to Telegram having internal problems. Please try again later +MT_SEND_QUEUE_TOO_LONG The MTProto send queue has grown too much due to Telegram having internal problems. Please try again later +NEED_CHAT_INVALID The provided chat is invalid +NEED_MEMBER_INVALID The provided member is invalid or does not exist +No workers running The Telegram server is restarting its workers. Try again later. +PARTICIPANT_CALL_FAILED Failure while making call due to Telegram having internal problems. Please try again later +PERSISTENT_TIMESTAMP_OUTDATED The persistent timestamp is outdated due to Telegram having internal problems. Please try again later +PHOTO_CREATE_FAILED The creation of the photo failed due to Telegram having internal problems. Please try again later +POSTPONED_TIMEOUT Telegram is having internal problems. Please try again later +PTS_CHANGE_EMPTY No PTS change +RANDOM_ID_DUPLICATE You provided a random ID that was already used +REG_ID_GENERATE_FAILED The registration id failed to generate due to Telegram having internal problems. Please try again later +RPC_CALL_FAIL Telegram is having internal problems. Please try again later +RPC_CONNECT_FAILED Telegram is having internal problems. Please try again later +RPC_MCGET_FAIL Telegram is having internal problems. Please try again later +SIGN_IN_FAILED Failure while signing in due to Telegram having internal problems. Please try again later +STORAGE_CHECK_FAILED Server storage check failed due to Telegram having internal problems. Please try again later +STORE_INVALID_SCALAR_TYPE Telegram is having internal problems. Please try again later +UNKNOWN_METHOD The method you tried to call cannot be called on non-CDN DCs +UPLOAD_NO_VOLUME Telegram is having internal problems. Please try again later +VOLUME_LOC_NOT_FOUND Telegram is having internal problems. Please try again later +WORKER_BUSY_TOO_LONG_RETRY Server workers are too busy right now due to Telegram having internal problems. Please try again later +WP_ID_GENERATE_FAILED Telegram is having internal problems. Please try again later \ No newline at end of file diff --git a/compiler/errors/source/503_SERVICE_UNAVAILABLE.tsv b/compiler/errors/source/503_SERVICE_UNAVAILABLE.tsv new file mode 100644 index 00000000000..c28edb0aebf --- /dev/null +++ b/compiler/errors/source/503_SERVICE_UNAVAILABLE.tsv @@ -0,0 +1,3 @@ +id message +ApiCallError Telegram is having internal problems. Please try again later. +Timeout Telegram is having internal problems. Please try again later. \ No newline at end of file diff --git a/compiler/error/template/class.txt b/compiler/errors/template/class.txt similarity index 100% rename from compiler/error/template/class.txt rename to compiler/errors/template/class.txt diff --git a/compiler/error/template/sub_class.txt b/compiler/errors/template/sub_class.txt similarity index 100% rename from compiler/error/template/sub_class.txt rename to compiler/errors/template/sub_class.txt diff --git a/dev-requirements.txt b/dev-requirements.txt new file mode 100644 index 00000000000..d8968085b3c --- /dev/null +++ b/dev-requirements.txt @@ -0,0 +1,6 @@ +-r requirements.txt + +pytest +pytest-asyncio +pytest-cov +twine \ No newline at end of file diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index c647eb13e98..00000000000 --- a/docs/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -SPHINXPROJ = Pyrogram -SOURCEDIR = source -BUILDDIR = build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file diff --git a/docs/make.bat b/docs/make.bat deleted file mode 100644 index fea543e2584..00000000000 --- a/docs/make.bat +++ /dev/null @@ -1,36 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=source -set BUILDDIR=build -set SPHINXPROJ=Pyrogram - -if "%1" == "" goto help - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.http://sphinx-doc.org/ - exit /b 1 -) - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% - -:end -popd diff --git a/docs/source/_images/favicon.ico b/docs/source/_images/favicon.ico deleted file mode 100644 index 4165f9edce8..00000000000 Binary files a/docs/source/_images/favicon.ico and /dev/null differ diff --git a/docs/source/_images/logo.png b/docs/source/_images/logo.png deleted file mode 100644 index 1f06fa81657..00000000000 Binary files a/docs/source/_images/logo.png and /dev/null differ diff --git a/docs/source/conf.py b/docs/source/conf.py deleted file mode 100644 index e08298ef5ba..00000000000 --- a/docs/source/conf.py +++ /dev/null @@ -1,200 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# -# Pyrogram documentation build configuration file, created by -# sphinx-quickstart on Fri Dec 29 11:35:55 2017. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -import os -import sys - -sys.path.insert(0, os.path.abspath('../..')) - -# Import after sys.path.insert() to avoid issues -from pyrogram import __version__ - -from pygments.styles.friendly import FriendlyStyle - -FriendlyStyle.background_color = "#f3f2f1" - -# -- General configuration ------------------------------------------------ - -# If your documentation needs a minimal Sphinx version, state it here. -# -# needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.napoleon', - 'sphinx.ext.autosummary' -] - -napoleon_use_rtype = False - -# Don't show source files on docs -html_show_sourcelink = True - -# Order by source, not alphabetically -autodoc_member_order = 'bysource' - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -# -# source_suffix = ['.rst', '.md'] -source_suffix = '.rst' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = 'Pyrogram' -copyright = '2017-2019, Dan Tès' -author = 'Dan Tès' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = "version " + __version__ -# The full version, including alpha/beta/rc tags. -release = version - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. -language = None - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This patterns also effect to html_static_path and html_extra_path -exclude_patterns = [] - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'friendly' - -# If true, `todo` and `todoList` produce output, else they produce nothing. -todo_include_todos = False - -# -- Options for HTML output ---------------------------------------------- - -html_title = "Pyrogram Documentation" - -# Overridden by template -html_show_copyright = False - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = 'sphinx_rtd_theme' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# -html_theme_options = { - 'canonical_url': "https://docs.pyrogram.ml/", - 'collapse_navigation': True, - 'sticky_navigation': False, - 'logo_only': True, - 'display_version': True -} - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -html_logo = '_images/logo.png' - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -html_favicon = '_images/favicon.ico' - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -# html_static_path = ['_static'] - -# Custom sidebar templates, must be a dictionary that maps document names -# to template names. -# -# This is required for the alabaster theme -# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars -html_sidebars = { - '**': [ - 'relations.html', # needs 'show_related': True theme option to display - 'searchbox.html', - ] -} - -# -- Options for HTMLHelp output ------------------------------------------ - -# Output file base name for HTML help builder. -htmlhelp_basename = 'Pyrogramdoc' - -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # - # 'preamble': '', - - # Latex figure (float) alignment - # - # 'figure_align': 'htbp', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - (master_doc, 'Pyrogram.tex', 'Pyrogram Documentation', - 'Dan Tès', 'manual'), -] - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'pyrogram', 'Pyrogram Documentation', - [author], 1) -] - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - (master_doc, 'Pyrogram', 'Pyrogram Documentation', - author, 'Pyrogram', 'One line description of project.', - 'Miscellaneous'), -] diff --git a/docs/source/core/client.rst b/docs/source/core/client.rst deleted file mode 100644 index 43524cb3c8a..00000000000 --- a/docs/source/core/client.rst +++ /dev/null @@ -1,12 +0,0 @@ -Pyrogram -======== - -In this section you can find a detailed description of the Pyrogram package and its API. - -:class:`Client ` is the main class. It exposes easy-to-use methods that are named -after the well established Telegram Bot API methods, thus offering a familiar look to Bot developers. - -Client ------- - -.. autoclass:: pyrogram.Client() diff --git a/docs/source/core/decorators.rst b/docs/source/core/decorators.rst deleted file mode 100644 index a9cd70c7cc5..00000000000 --- a/docs/source/core/decorators.rst +++ /dev/null @@ -1,47 +0,0 @@ -Decorators -========== - -While still being methods bound to the :obj:`Client` class, decorators are of a special kind and thus deserve a -dedicated page. - -Decorators are able to register callback functions for handling updates in a much easier and cleaner way compared to -`Handlers `_; they do so by instantiating the correct handler and calling -:meth:`add_handler() `, automatically. All you need to do is adding the decorators on top -of your functions. - -**Example:** - -.. code-block:: python - - from pyrogram import Client - - app = Client(...) - - - @app.on_message() - def log(client, message): - print(message) - - - app.run() - -.. currentmodule:: pyrogram.Client - -.. autosummary:: - :nosignatures: - - on_message - on_callback_query - on_inline_query - on_deleted_messages - on_user_status - on_disconnect - on_raw_update - -.. automethod:: pyrogram.Client.on_message() -.. automethod:: pyrogram.Client.on_callback_query() -.. automethod:: pyrogram.Client.on_inline_query() -.. automethod:: pyrogram.Client.on_deleted_messages() -.. automethod:: pyrogram.Client.on_user_status() -.. automethod:: pyrogram.Client.on_disconnect() -.. automethod:: pyrogram.Client.on_raw_update() \ No newline at end of file diff --git a/docs/source/core/errors.rst b/docs/source/core/errors.rst deleted file mode 100644 index 68313bddad3..00000000000 --- a/docs/source/core/errors.rst +++ /dev/null @@ -1,28 +0,0 @@ -Errors -====== - -All the Pyrogram errors listed here live inside the ``errors`` sub-package. - -**Example:** - -.. code-block:: python - - from pyrogram.errors import RPCError - - try: - ... - except RPCError: - ... - -.. autoexception:: pyrogram.RPCError() - :members: - -.. toctree:: - ../errors/see-other - ../errors/bad-request - ../errors/unauthorized - ../errors/forbidden - ../errors/not-acceptable - ../errors/flood - ../errors/internal-server-error - ../errors/unknown-error diff --git a/docs/source/core/filters.rst b/docs/source/core/filters.rst deleted file mode 100644 index 091031aef47..00000000000 --- a/docs/source/core/filters.rst +++ /dev/null @@ -1,5 +0,0 @@ -Filters -======= - -.. autoclass:: pyrogram.Filters - :members: diff --git a/docs/source/core/handlers.rst b/docs/source/core/handlers.rst deleted file mode 100644 index 08d40039a95..00000000000 --- a/docs/source/core/handlers.rst +++ /dev/null @@ -1,60 +0,0 @@ -Update Handlers -=============== - -Handlers are used to instruct Pyrogram about which kind of updates you'd like to handle with your callback functions. - -For a much more convenient way of registering callback functions have a look at `Decorators `_ instead. -In case you decided to manually create an handler, use :meth:`add_handler() ` to register -it. - -**Example:** - -.. code-block:: python - - from pyrogram import Client, MessageHandler - - app = Client(...) - - - def dump(client, message): - print(message) - - - app.add_handler(MessageHandler(dump)) - - app.run() - -.. currentmodule:: pyrogram - -.. autosummary:: - :nosignatures: - - MessageHandler - DeletedMessagesHandler - CallbackQueryHandler - InlineQueryHandler - UserStatusHandler - DisconnectHandler - RawUpdateHandler - -.. autoclass:: MessageHandler() - :members: - -.. autoclass:: DeletedMessagesHandler() - :members: - -.. autoclass:: CallbackQueryHandler() - :members: - -.. autoclass:: InlineQueryHandler() - :members: - -.. autoclass:: UserStatusHandler() - :members: - -.. autoclass:: DisconnectHandler() - :members: - -.. autoclass:: RawUpdateHandler() - :members: - diff --git a/docs/source/core/methods.rst b/docs/source/core/methods.rst deleted file mode 100644 index 609a38a8440..00000000000 --- a/docs/source/core/methods.rst +++ /dev/null @@ -1,268 +0,0 @@ -Available Methods -================= - -All Pyrogram methods listed here are bound to a :obj:`Client ` instance. - -**Example:** - -.. code-block:: python - - from pyrogram import Client - - app = Client(...) - - with app: - app.send_message("haskell", "hi") - -.. currentmodule:: pyrogram.Client - -Utilities ---------- - -.. autosummary:: - :nosignatures: - - start - stop - restart - idle - run - add_handler - remove_handler - send - resolve_peer - save_file - stop_transmission - -Messages --------- - -.. autosummary:: - :nosignatures: - - send_message - forward_messages - send_photo - send_audio - send_document - send_sticker - send_video - send_animation - send_voice - send_video_note - send_media_group - send_location - send_venue - send_contact - send_cached_media - send_chat_action - edit_message_text - edit_message_caption - edit_message_reply_markup - edit_message_media - delete_messages - get_messages - get_history - get_history_count - iter_history - send_poll - vote_poll - stop_poll - retract_vote - download_media - -Chats ------ - -.. autosummary:: - :nosignatures: - - join_chat - leave_chat - kick_chat_member - unban_chat_member - restrict_chat_member - promote_chat_member - export_chat_invite_link - set_chat_photo - delete_chat_photo - set_chat_title - set_chat_description - pin_chat_message - unpin_chat_message - get_chat - get_chat_preview - get_chat_member - get_chat_members - get_chat_members_count - iter_chat_members - get_dialogs - iter_dialogs - get_dialogs_count - restrict_chat - update_chat_username - -Users ------ - -.. autosummary:: - :nosignatures: - - get_me - get_users - get_user_profile_photos - get_user_profile_photos_count - set_user_profile_photo - delete_user_profile_photos - update_username - -Contacts --------- - -.. autosummary:: - :nosignatures: - - add_contacts - get_contacts - get_contacts_count - delete_contacts - -Password --------- - -.. autosummary:: - :nosignatures: - - enable_cloud_password - change_cloud_password - remove_cloud_password - -Bots ----- - -.. autosummary:: - :nosignatures: - - get_inline_bot_results - send_inline_bot_result - answer_callback_query - answer_inline_query - request_callback_answer - send_game - set_game_score - get_game_high_scores - -.. Utilities - --------- - -.. automethod:: pyrogram.Client.start() -.. automethod:: pyrogram.Client.stop() -.. automethod:: pyrogram.Client.restart() -.. automethod:: pyrogram.Client.idle() -.. automethod:: pyrogram.Client.run() -.. automethod:: pyrogram.Client.add_handler() -.. automethod:: pyrogram.Client.remove_handler() -.. automethod:: pyrogram.Client.send() -.. automethod:: pyrogram.Client.resolve_peer() -.. automethod:: pyrogram.Client.save_file() -.. automethod:: pyrogram.Client.stop_transmission() - -.. Messages - -------- - -.. automethod:: pyrogram.Client.send_message() -.. automethod:: pyrogram.Client.forward_messages() -.. automethod:: pyrogram.Client.send_photo() -.. automethod:: pyrogram.Client.send_audio() -.. automethod:: pyrogram.Client.send_document() -.. automethod:: pyrogram.Client.send_sticker() -.. automethod:: pyrogram.Client.send_video() -.. automethod:: pyrogram.Client.send_animation() -.. automethod:: pyrogram.Client.send_voice() -.. automethod:: pyrogram.Client.send_video_note() -.. automethod:: pyrogram.Client.send_media_group() -.. automethod:: pyrogram.Client.send_location() -.. automethod:: pyrogram.Client.send_venue() -.. automethod:: pyrogram.Client.send_contact() -.. automethod:: pyrogram.Client.send_cached_media() -.. automethod:: pyrogram.Client.send_chat_action() -.. automethod:: pyrogram.Client.edit_message_text() -.. automethod:: pyrogram.Client.edit_message_caption() -.. automethod:: pyrogram.Client.edit_message_reply_markup() -.. automethod:: pyrogram.Client.edit_message_media() -.. automethod:: pyrogram.Client.delete_messages() -.. automethod:: pyrogram.Client.get_messages() -.. automethod:: pyrogram.Client.get_history() -.. automethod:: pyrogram.Client.get_history_count() -.. automethod:: pyrogram.Client.iter_history() -.. automethod:: pyrogram.Client.send_poll() -.. automethod:: pyrogram.Client.vote_poll() -.. automethod:: pyrogram.Client.stop_poll() -.. automethod:: pyrogram.Client.retract_vote() -.. automethod:: pyrogram.Client.download_media() - -.. Chats - ----- - -.. automethod:: pyrogram.Client.join_chat() -.. automethod:: pyrogram.Client.leave_chat() -.. automethod:: pyrogram.Client.kick_chat_member() -.. automethod:: pyrogram.Client.unban_chat_member() -.. automethod:: pyrogram.Client.restrict_chat_member() -.. automethod:: pyrogram.Client.promote_chat_member() -.. automethod:: pyrogram.Client.export_chat_invite_link() -.. automethod:: pyrogram.Client.set_chat_photo() -.. automethod:: pyrogram.Client.delete_chat_photo() -.. automethod:: pyrogram.Client.set_chat_title() -.. automethod:: pyrogram.Client.set_chat_description() -.. automethod:: pyrogram.Client.pin_chat_message() -.. automethod:: pyrogram.Client.unpin_chat_message() -.. automethod:: pyrogram.Client.get_chat() -.. automethod:: pyrogram.Client.get_chat_preview() -.. automethod:: pyrogram.Client.get_chat_member() -.. automethod:: pyrogram.Client.get_chat_members() -.. automethod:: pyrogram.Client.get_chat_members_count() -.. automethod:: pyrogram.Client.iter_chat_members() -.. automethod:: pyrogram.Client.get_dialogs() -.. automethod:: pyrogram.Client.iter_dialogs() -.. automethod:: pyrogram.Client.get_dialogs_count() -.. automethod:: pyrogram.Client.restrict_chat() -.. automethod:: pyrogram.Client.update_chat_username() - -.. Users - ----- - -.. automethod:: pyrogram.Client.get_me() -.. automethod:: pyrogram.Client.get_users() -.. automethod:: pyrogram.Client.get_user_profile_photos() -.. automethod:: pyrogram.Client.get_user_profile_photos_count() -.. automethod:: pyrogram.Client.set_user_profile_photo() -.. automethod:: pyrogram.Client.delete_user_profile_photos() -.. automethod:: pyrogram.Client.update_username() - -.. Contacts - -------- - -.. automethod:: pyrogram.Client.add_contacts() -.. automethod:: pyrogram.Client.get_contacts() -.. automethod:: pyrogram.Client.get_contacts_count() -.. automethod:: pyrogram.Client.delete_contacts() - -.. Password - -------- - -.. automethod:: pyrogram.Client.enable_cloud_password() -.. automethod:: pyrogram.Client.change_cloud_password() -.. automethod:: pyrogram.Client.remove_cloud_password() - -.. Bots - ---- - -.. automethod:: pyrogram.Client.get_inline_bot_results() -.. automethod:: pyrogram.Client.send_inline_bot_result() -.. automethod:: pyrogram.Client.answer_callback_query() -.. automethod:: pyrogram.Client.answer_inline_query() -.. automethod:: pyrogram.Client.request_callback_answer() -.. automethod:: pyrogram.Client.send_game() -.. automethod:: pyrogram.Client.set_game_score() -.. automethod:: pyrogram.Client.get_game_high_scores() diff --git a/docs/source/core/types.rst b/docs/source/core/types.rst deleted file mode 100644 index 710a772d75e..00000000000 --- a/docs/source/core/types.rst +++ /dev/null @@ -1,278 +0,0 @@ -Available Types -=============== - -All Pyrogram types listed here are accessible through the main package directly. - -**Example:** - -.. code-block:: python - - from pyrogram import User, Message, ... - -.. note:: - - **Optional** fields may not exist when irrelevant -- i.e.: they will contain the value of ``None`` and aren't shown - when, for example, using ``print()``. - -.. currentmodule:: pyrogram - -Users & Chats -------------- - -.. autosummary:: - :nosignatures: - - User - UserStatus - Chat - ChatPreview - ChatPhoto - ChatMember - ChatMembers - ChatPermissions - Dialog - Dialogs - -Messages & Media ----------------- - -.. autosummary:: - :nosignatures: - - Message - Messages - MessageEntity - Photo - PhotoSize - UserProfilePhotos - Audio - Document - Animation - Video - Voice - VideoNote - Contact - Location - Venue - Sticker - Game - Poll - PollOption - -Keyboards ---------- - -.. autosummary:: - :nosignatures: - - ReplyKeyboardMarkup - KeyboardButton - ReplyKeyboardRemove - InlineKeyboardMarkup - InlineKeyboardButton - ForceReply - CallbackQuery - GameHighScore - CallbackGame - -Input Media ------------ - -.. autosummary:: - :nosignatures: - - InputMedia - InputMediaPhoto - InputMediaVideo - InputMediaAudio - InputMediaAnimation - InputMediaDocument - InputPhoneContact - -Inline Mode ------------- - -.. autosummary:: - :nosignatures: - - InlineQuery - InlineQueryResult - InlineQueryResultArticle - -InputMessageContent -------------------- - -.. autosummary:: - :nosignatures: - - InputMessageContent - InputTextMessageContent - -.. User & Chats - ------------ - -.. autoclass:: User() - :members: - -.. autoclass:: UserStatus() - :members: - -.. autoclass:: Chat() - :members: - -.. autoclass:: ChatPreview() - :members: - -.. autoclass:: ChatPhoto() - :members: - -.. autoclass:: ChatMember() - :members: - -.. autoclass:: ChatMembers() - :members: - -.. autoclass:: ChatPermissions() - :members: - -.. autoclass:: Dialog() - :members: - -.. autoclass:: Dialogs() - :members: - -.. Messages & Media - ---------------- - -.. autoclass:: Message() - :members: - -.. autoclass:: Messages() - :members: - -.. autoclass:: MessageEntity() - :members: - -.. autoclass:: Photo() - :members: - -.. autoclass:: PhotoSize() - :members: - -.. autoclass:: UserProfilePhotos() - :members: - -.. autoclass:: Audio() - :members: - -.. autoclass:: Document() - :members: - -.. autoclass:: Animation() - :members: - -.. autoclass:: Video() - :members: - -.. autoclass:: Voice() - :members: - -.. autoclass:: VideoNote() - :members: - -.. autoclass:: Contact() - :members: - -.. autoclass:: Location() - :members: - -.. autoclass:: Venue() - :members: - -.. autoclass:: Sticker() - :members: - -.. autoclass:: Game() - :members: - -.. autoclass:: Poll() - :members: - -.. autoclass:: PollOption() - :members: - -.. Keyboards - --------- - -.. autoclass:: ReplyKeyboardMarkup() - :members: - -.. autoclass:: KeyboardButton() - :members: - -.. autoclass:: ReplyKeyboardRemove() - :members: - -.. autoclass:: InlineKeyboardMarkup() - :members: - -.. autoclass:: InlineKeyboardButton() - :members: - -.. autoclass:: ForceReply() - :members: - -.. autoclass:: CallbackQuery() - :members: - -.. autoclass:: GameHighScore() - :members: - -.. autoclass:: CallbackGame() - :members: - -.. Input Media - ----------- - -.. autoclass:: InputMedia() - :members: - -.. autoclass:: InputMediaPhoto() - :members: - -.. autoclass:: InputMediaVideo() - :members: - -.. autoclass:: InputMediaAudio() - :members: - -.. autoclass:: InputMediaAnimation() - :members: - -.. autoclass:: InputMediaDocument() - :members: - -.. autoclass:: InputPhoneContact() - :members: - - -.. Inline Mode - ----------- - -.. autoclass:: InlineQuery() - :members: - -.. autoclass:: InlineQueryResult() - :members: - -.. autoclass:: InlineQueryResultArticle() - :members: - -.. InputMessageContent - ------------------- - -.. autoclass:: InputMessageContent() - :members: - -.. autoclass:: InputTextMessageContent() - :members: diff --git a/docs/source/errors/bad-request.rst b/docs/source/errors/bad-request.rst deleted file mode 100644 index 2d56434c385..00000000000 --- a/docs/source/errors/bad-request.rst +++ /dev/null @@ -1,7 +0,0 @@ -400 - Bad Request -================= - -.. module:: pyrogram.errors.BadRequest - -.. automodule:: pyrogram.errors.exceptions.bad_request_400 - :members: diff --git a/docs/source/errors/flood.rst b/docs/source/errors/flood.rst deleted file mode 100644 index 55098cbb809..00000000000 --- a/docs/source/errors/flood.rst +++ /dev/null @@ -1,7 +0,0 @@ -420 - Flood -=========== - -.. module:: pyrogram.errors.Flood - -.. automodule:: pyrogram.errors.exceptions.flood_420 - :members: diff --git a/docs/source/errors/forbidden.rst b/docs/source/errors/forbidden.rst deleted file mode 100644 index cd794979297..00000000000 --- a/docs/source/errors/forbidden.rst +++ /dev/null @@ -1,7 +0,0 @@ -403 - Forbidden -=============== - -.. module:: pyrogram.errors.Forbidden - -.. automodule:: pyrogram.errors.exceptions.forbidden_403 - :members: diff --git a/docs/source/errors/internal-server-error.rst b/docs/source/errors/internal-server-error.rst deleted file mode 100644 index 7f78d51945e..00000000000 --- a/docs/source/errors/internal-server-error.rst +++ /dev/null @@ -1,7 +0,0 @@ -500 - Internal Server Error -=========================== - -.. module:: pyrogram.errors.InternalServerError - -.. automodule:: pyrogram.errors.exceptions.internal_server_error_500 - :members: diff --git a/docs/source/errors/not-acceptable.rst b/docs/source/errors/not-acceptable.rst deleted file mode 100644 index 5a8365fc6fa..00000000000 --- a/docs/source/errors/not-acceptable.rst +++ /dev/null @@ -1,7 +0,0 @@ -406 - Not Acceptable -==================== - -.. module:: pyrogram.errors.NotAcceptable - -.. automodule:: pyrogram.errors.exceptions.not_acceptable_406 - :members: diff --git a/docs/source/errors/see-other.rst b/docs/source/errors/see-other.rst deleted file mode 100644 index f90902d0361..00000000000 --- a/docs/source/errors/see-other.rst +++ /dev/null @@ -1,7 +0,0 @@ -303 - See Other -=============== - -.. module:: pyrogram.errors.SeeOther - -.. automodule:: pyrogram.errors.exceptions.see_other_303 - :members: diff --git a/docs/source/errors/unauthorized.rst b/docs/source/errors/unauthorized.rst deleted file mode 100644 index d47ed3fb1f9..00000000000 --- a/docs/source/errors/unauthorized.rst +++ /dev/null @@ -1,7 +0,0 @@ -401 - Unauthorized -================== - -.. module:: pyrogram.errors.Unauthorized - -.. automodule:: pyrogram.errors.exceptions.unauthorized_401 - :members: diff --git a/docs/source/errors/unknown-error.rst b/docs/source/errors/unknown-error.rst deleted file mode 100644 index 21495957d6d..00000000000 --- a/docs/source/errors/unknown-error.rst +++ /dev/null @@ -1,7 +0,0 @@ -520 - Unknown Error -=================== - -.. module:: pyrogram.errors.UnknownError - -.. autoexception:: pyrogram.errors.rpc_error.UnknownError - :members: diff --git a/docs/source/index.rst b/docs/source/index.rst deleted file mode 100644 index 74f6843330c..00000000000 --- a/docs/source/index.rst +++ /dev/null @@ -1,136 +0,0 @@ -Welcome to Pyrogram -=================== - -.. raw:: html - -
- -
Pyrogram Logo
-
-
- -

- Telegram MTProto API Framework for Python - -
- - GitHub - - • - - Community - - - • - - Changelog - - • - - PyPI - -
- - Schema Layer - - - TgCrypto Version - -

- -.. code-block:: python - - from pyrogram import Client, Filters - - app = Client("my_account") - - - @app.on_message(Filters.private) - def hello(client, message): - message.reply("Hello {}".format(message.from_user.first_name)) - - - app.run() - -**Pyrogram** is an elegant, easy-to-use Telegram_ client library and framework written from the ground up in Python and C. -It enables you to easily create custom apps using both user and bot identities (bot API alternative) via the `MTProto API`_. - -How the documentation is organized ----------------------------------- - -Contents are organized into self-contained topics and can be accessed from the sidebar, or by following them in order -using the Next button at the end of each page. - -Relevant Pages -^^^^^^^^^^^^^^ - -- `Quick Start`_ - Concise steps to get you started as fast as possible. -- `API Usage`_ - Guide on how to use Pyrogram's API. -- `Update Handling`_ - Guide on how to handle Telegram updates. -- Client_ - Reference details about the Client class. -- Types_ - All the available Pyrogram types. -- Methods_ - All the available Pyrogram methods. - -**To get started, press the Next button** - -.. toctree:: - :hidden: - :caption: Introduction - - intro/start - intro/install - intro/setup - intro/auth - -.. toctree:: - :hidden: - :caption: Topic Guides - - topics/usage - topics/update-handling - topics/using-filters - topics/more-on-updates - topics/configuration-file - topics/smart-plugins - topics/auto-authorization - topics/customize-sessions - topics/tgcrypto - topics/text-formatting - topics/socks5-proxy - topics/bots-interaction - topics/error-handling - topics/test-servers - topics/advanced-usage - topics/voice-calls - topics/changelog - -.. toctree:: - :hidden: - :caption: API Reference - - core/client - core/types - core/methods - core/handlers - core/decorators - core/filters - core/errors - -.. toctree:: - :hidden: - :caption: Telegram API - - functions/index - types/index - -.. _Telegram: https://telegram.org -.. _TgCrypto: https://docs.pyrogram.ml/resources/TgCrypto -.. _MTProto API: https://core.telegram.org/api#telegram-api -.. _Quick Start: intro/start.html -.. _API Usage: topics/usage.html -.. _Update Handling: topics/update-handling.html -.. _Client: core/client.html -.. _Types: core/types.html -.. _Methods: core/methods diff --git a/docs/source/intro/auth.rst b/docs/source/intro/auth.rst deleted file mode 100644 index 86217137a4e..00000000000 --- a/docs/source/intro/auth.rst +++ /dev/null @@ -1,68 +0,0 @@ -Authorization -============= - -Once a `project is set up`_, you will still have to follow a few steps before you can actually use Pyrogram to make -API calls. This section provides all the information you need in order to authorize yourself as user or a bot. - -User Authorization ------------------- - -In order to use the API, Telegram requires that users be authorized via their phone numbers. -Pyrogram automatically manages this access, all you need to do is create an instance of the -:class:`Client ` class by passing to it a ``session_name`` of your choice (e.g.: "my_account") and call -the :meth:`run() ` method: - -.. code-block:: python - - from pyrogram import Client - - app = Client("my_account") - app.run() - -This starts an interactive shell asking you to input your **phone number** (including your `Country Code`_) and the -**phone code** you will receive in your devices that are already authorized or via SMS: - -.. code-block:: text - - Enter phone number: +39********** - Is "+39**********" correct? (y/n): y - Enter phone code: 32768 - Logged in successfully as Dan - -After successfully authorizing yourself, a new file called ``my_account.session`` will be created allowing Pyrogram to -execute API calls with your identity. This file will be loaded again when you restart your app, and as long as you -keep the session alive, Pyrogram won't ask you again to enter your phone number. - -.. important:: - - Your ``*.session`` files are personal and must be kept secret. - -.. note:: - - The code above does nothing except asking for credentials and keeping the client online, hit ``CTRL+C`` now to stop - your application and keep reading. - -Bot Authorization ------------------ - -Bots are a special kind of users that are authorized via their tokens (instead of phone numbers), which are created by -the `Bot Father`_. Bot tokens replace the users' phone numbers only — you still need to -`configure a Telegram API key `_ with Pyrogram, even when using bots. - -The authorization process is automatically managed. All you need to do is choose a ``session_name`` (can be anything, -usually your bot username) and pass your bot token using the ``bot_token`` parameter. The session file will be named -after the session name, which will be ``pyrogrambot.session`` for the example below. - -.. code-block:: python - - from pyrogram import Client - - app = Client( - "my_bot", - bot_token="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11" - ) - app.run() - -.. _project is set up: setup.html -.. _Country Code: https://en.wikipedia.org/wiki/List_of_country_calling_codes -.. _Bot Father: https://t.me/botfather \ No newline at end of file diff --git a/docs/source/intro/install.rst b/docs/source/intro/install.rst deleted file mode 100644 index 161c7d61e82..00000000000 --- a/docs/source/intro/install.rst +++ /dev/null @@ -1,92 +0,0 @@ -Install Guide -============= - -Being a Python library, **Pyrogram** requires Python to be installed in your system. -We recommend using the latest versions of both Python 3 and pip. - -- Get **Python 3** from https://www.python.org/downloads/ (or with your package manager) -- Get **pip** by following the instructions at https://pip.pypa.io/en/latest/installing/. - -.. important:: - - Pyrogram supports **Python 3** only, starting from version 3.4. **PyPy** is supported too. - -Install Pyrogram ----------------- - -- The easiest way to install and upgrade Pyrogram to its latest stable version is by using **pip**: - - .. code-block:: text - - $ pip3 install -U pyrogram - -- or, with TgCrypto_ as extra requirement (recommended): - - .. code-block:: text - - $ pip3 install -U pyrogram[fast] - -Bleeding Edge -------------- - -Things are constantly evolving in Pyrogram, although new releases are published only when enough changes are added, -but this doesn't mean you can't try new features right now! - -In case you would like to try out the latest Pyrogram features and additions, the `GitHub repo`_ is always kept updated -with new changes; you can install the development version straight from the ``develop`` branch using this command -(note "develop.zip" in the link): - -.. code-block:: text - - $ pip3 install -U https://github.com/pyrogram/pyrogram/archive/develop.zip - -Asynchronous ------------- - -Pyrogram heavily depends on IO-bound network code (it's a cloud-based messaging framework after all), and here's -where asyncio shines the most by providing extra performance while running on a single OS-level thread only. - -**A fully asynchronous variant of Pyrogram is therefore available** (Python 3.5.3+ required). -Use this command to install (note "asyncio.zip" in the link): - -.. code-block:: text - - $ pip3 install -U https://github.com/pyrogram/pyrogram/archive/asyncio.zip - - -Pyrogram API remains the same and features are kept up to date from the non-async, default develop branch, but you -are obviously required Python asyncio knowledge in order to take full advantage of it. - - -.. tip:: - - The idea to turn Pyrogram fully asynchronous is still under consideration, but is wise to expect that in future this - would be the one and only way to work with Pyrogram. - - You can start using Pyrogram Async variant right now as an excuse to learn more about asynchronous programming and - do experiments with it! - -.. raw:: html - - - -.. centered:: Subscribe to `@Pyrogram `_ for news and announcements - -Verifying ---------- - -To verify that Pyrogram is correctly installed, open a Python shell and import it. -If no error shows up you are good to go. - -.. code-block:: python - - >>> import pyrogram - >>> pyrogram.__version__ - '0.12.0' - -.. _TgCrypto: ../resources/tgcrypto.html -.. _`Github repo`: http://github.com/pyrogram/pyrogram diff --git a/docs/source/intro/setup.rst b/docs/source/intro/setup.rst deleted file mode 100644 index b318654d964..00000000000 --- a/docs/source/intro/setup.rst +++ /dev/null @@ -1,61 +0,0 @@ -Project Setup -============= - -We have just `installed Pyrogram`_. In this page we'll discuss what you need to do in order to set up a project with -the library. Let's see how it's done. - -API Keys --------- - -The very first step requires you to obtain a valid Telegram API key (API id/hash pair): - -#. Visit https://my.telegram.org/apps and log in with your Telegram Account. -#. Fill out the form to register a new Telegram application. -#. Done! The API key consists of two parts: **api_id** and **api_hash**. - -.. important:: - - The API key is personal and must be kept secret. - -.. note:: - - The API key is unique for each user, but defines a token for a Telegram *application* you are going to build. This - means that you are able to authorize multiple users (and bots too) to access the Telegram database through the - MTProto API by a single API key. - -Configuration -------------- - -Having the API key from the `previous step <#api-keys>`_ in handy, we can now begin to configure a Pyrogram project. -There are two ways to do so, and you can choose what fits better for you: - -- First option (recommended): create a new ``config.ini`` file at the root of your working directory, copy-paste the - following and replace the **api_id** and **api_hash** values with your own. This is the preferred method because - allows you to keep your credentials out of your code without having to deal with how to load them: - - .. code-block:: ini - - [pyrogram] - api_id = 12345 - api_hash = 0123456789abcdef0123456789abcdef - -- Alternatively, you can pass your API key to Pyrogram by simply using the *api_id* and *api_hash* parameters of the - Client class. This way you can have full control on how to store and load your credentials (e.g.:, you can load the - credentials from the environment variables and directly pass the values into Pyrogram): - - .. code-block:: python - - from pyrogram import Client - - app = Client( - "my_account", - api_id=12345, - api_hash="0123456789abcdef0123456789abcdef" - ) - -.. note:: - - To keep code snippets clean and concise, from now on it is assumed you are making use of the ``config.ini`` file, - thus, the *api_id* and *api_hash* parameters usage won't be shown anymore. - -.. _installed Pyrogram: install.html diff --git a/docs/source/intro/start.rst b/docs/source/intro/start.rst deleted file mode 100644 index d6a8d5e32f8..00000000000 --- a/docs/source/intro/start.rst +++ /dev/null @@ -1,45 +0,0 @@ -Quick Start -=========== - -The next few steps serve as a quick start for all new Pyrogrammers that want to get something done as fast as possible! - -Get Pyrogram Real Fast ----------------------- - -1. Install Pyrogram with ``pip3 install -U pyrogram``. - -2. Get your own Telegram API key from https://my.telegram.org/apps. - -3. Open your best text editor and paste the following: - - .. code-block:: python - - from pyrogram import Client - - api_id = 12345 - api_hash = "0123456789abcdef0123456789abcdef" - - with Client("my_account", api_id, api_hash) as app: - app.send_message("me", "Greetings from **Pyrogram**!") - -4. Replace *api_id* and *api_hash* values with your own. - -5. Save the file as ``pyro.py``. - -6. Run the script with ``python3 pyro.py`` - -7. Follow the instructions on your terminal to login. - -8. Watch Pyrogram send a message to yourself. - -9. Join our `community `_. - -10. Say, "hi!". - -Enjoy the API -------------- - -That was just a quick overview that barely scratched the surface! -In the next few pages of the introduction, we'll take a much more in-depth look of what we have just done. - -Feeling eager? You can take a shortcut to `API Usage <../topics/usage.html>`_ and come back later to learn some more details. diff --git a/docs/source/sitemap.py b/docs/source/sitemap.py deleted file mode 100644 index 539bac0d15f..00000000000 --- a/docs/source/sitemap.py +++ /dev/null @@ -1,68 +0,0 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 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 . - -import datetime -import os -import re - -canonical = "https://docs.pyrogram.ml" - -dirs = { - "start": ("weekly", 0.9), - "resources": ("weekly", 0.8), - "pyrogram": ("weekly", 0.8), - "functions": ("monthly", 0.7), - "types": ("monthly", 0.7), - "errors": ("weekly", 0.6) -} - - -def now(): - return datetime.datetime.today().strftime("%Y-%m-%d") - - -with open("sitemap.xml", "w") as f: - f.write("\n") - f.write("\n") - - urls = [(canonical, now(), "weekly", 1.0)] - - - def search(path): - try: - for j in os.listdir(path): - search("{}/{}".format(path, j)) - except NotADirectoryError: - d = path.split("/")[0] - path = "{}/{}".format(canonical, path.split(".")[0]) - path = re.sub("^(.+)/index$", "\g<1>", path) - urls.append((path, now(), dirs[d][0], dirs[d][1])) - - - for i in dirs.keys(): - search(i) - - for i in urls: - f.write(" \n") - f.write(" {}\n".format(i[0])) - f.write(" {}\n".format(i[1])) - f.write(" {}\n".format(i[2])) - f.write(" {}\n".format(i[3])) - f.write(" \n\n") - - f.write("") diff --git a/docs/source/topics/advanced-usage.rst b/docs/source/topics/advanced-usage.rst deleted file mode 100644 index 8b722b2a9e5..00000000000 --- a/docs/source/topics/advanced-usage.rst +++ /dev/null @@ -1,136 +0,0 @@ -Advanced Usage -============== - -Pyrogram's API, which consists of well documented convenience methods_ and facade types_, exists to provide a much -easier interface to the undocumented and often confusing Telegram API. - -In this section, you'll be shown the alternative way of communicating with Telegram using Pyrogram: the main "raw" -Telegram API with its functions and types. - -Telegram Raw API ----------------- - -If you can't find a high-level method for your needs or if you want complete, low-level access to the whole -Telegram API, you have to use the raw :mod:`functions ` and :mod:`types `. - -As already hinted, raw functions and types can be really confusing, mainly because people don't realize soon enough they -accept *only* the right types and that all required parameters must be filled in. This section will therefore explain -some pitfalls to take into consideration when working with the raw API. - -.. hint:: - - Every available high-level methods in Pyrogram is built on top of these raw functions. - - Nothing stops you from using the raw functions only, but they are rather complex and `plenty of them`_ are already - re-implemented by providing a much simpler and cleaner interface which is very similar to the Bot API (yet much more - powerful). - - If you think a raw function should be wrapped and added as a high-level method, feel free to ask in our Community_! - -Invoking Functions -^^^^^^^^^^^^^^^^^^ - -Unlike the methods_ found in Pyrogram's API, which can be called in the usual simple way, functions to be invoked from -the raw Telegram API have a different way of usage and are more complex. - -First of all, both `raw functions`_ and `raw types`_ live in their respective packages (and sub-packages): -``pyrogram.api.functions``, ``pyrogram.api.types``. They all exist as Python classes, meaning you need to create an -instance of each every time you need them and fill them in with the correct values using named arguments. - -Next, to actually invoke the raw function you have to use the :meth:`send() ` method provided by -the Client class and pass the function object you created. - -Here's some examples: - -- Update first name, last name and bio: - - .. code-block:: python - - from pyrogram import Client - from pyrogram.api import functions - - with Client("my_account") as app: - app.send( - functions.account.UpdateProfile( - first_name="Dan", last_name="Tès", - about="Bio written from Pyrogram" - ) - ) - -- Disable links to your account when someone forwards your messages: - - .. code-block:: python - - from pyrogram import Client - from pyrogram.api import functions, types - - with Client("my_account") as app: - app.send( - functions.account.SetPrivacy( - key=types.PrivacyKeyForwards(), - rules=[types.InputPrivacyValueDisallowAll()] - ) - ) - -- Invite users to your channel/supergroup: - - .. code-block:: python - - from pyrogram import Client - from pyrogram.api import functions, types - - with Client("my_account") as app: - app.send( - functions.channels.InviteToChannel( - channel=app.resolve_peer(123456789), # ID or Username - users=[ # The users you want to invite - app.resolve_peer(23456789), # By ID - app.resolve_peer("username"), # By username - app.resolve_peer("+393281234567"), # By phone number - ] - ) - ) - -Chat IDs -^^^^^^^^ - -The way Telegram works makes it impossible to directly send a message to a user or a chat by using their IDs only. -Instead, a pair of ``id`` and ``access_hash`` wrapped in a so called ``InputPeer`` is always needed. Pyrogram allows -sending messages with IDs only thanks to cached access hashes. - -There are three different InputPeer types, one for each kind of Telegram entity. -Whenever an InputPeer is needed you must pass one of these: - - - `InputPeerUser `_ - Users - - `InputPeerChat `_ - Basic Chats - - `InputPeerChannel `_ - Either Channels or Supergroups - -But you don't necessarily have to manually instantiate each object because, luckily for you, Pyrogram already provides -:meth:`resolve_peer() ` as a convenience utility method that returns the correct InputPeer -by accepting a peer ID only. - -Another thing to take into consideration about chat IDs is the way they are represented: they are all integers and -all positive within their respective raw types. - -Things are different when working with Pyrogram's API because having them in the same space can theoretically lead to -collisions, and that's why Pyrogram (as well as the official Bot API) uses a slightly different representation for each -kind of ID. - -For example, given the ID *123456789*, here's how Pyrogram can tell entities apart: - - - ``+ID`` User: *123456789* - - ``-ID`` Chat: *-123456789* - - ``-100ID`` Channel (and Supergroup): *-100123456789* - -So, every time you take a raw ID, make sure to translate it into the correct ID when you want to use it with an -high-level method. - - - - -.. _methods: ../pyrogram/Client.html#messages -.. _types: ../pyrogram/Types.html -.. _plenty of them: ../pyrogram/Client.html#messages -.. _raw functions: ../pyrogram/functions -.. _raw types: ../pyrogram/types -.. _Community: https://t.me/PyrogramChat \ No newline at end of file diff --git a/docs/source/topics/auto-authorization.rst b/docs/source/topics/auto-authorization.rst deleted file mode 100644 index b5f3a94aaed..00000000000 --- a/docs/source/topics/auto-authorization.rst +++ /dev/null @@ -1,68 +0,0 @@ -Auto Authorization -================== - -Manually writing phone number, phone code and password on the terminal every time you want to login can be tedious. -Pyrogram is able to automate both **Log In** and **Sign Up** processes, all you need to do is pass the relevant -parameters when creating a new :class:`Client `. - -.. note:: If you omit any of the optional parameter required for the authorization, Pyrogram will ask you to - manually write it. For instance, if you don't want to set a ``last_name`` when creating a new account you - have to explicitly pass an empty string ""; the default value (None) will trigger the input() call. - -Log In -------- - -To automate the **Log In** process, pass your ``phone_number`` and ``password`` (if you have one) in the Client parameters. -If you want to retrieve the phone code programmatically, pass a callback function in the ``phone_code`` field — this -function accepts a single positional argument (phone_number) and must return the correct phone code (e.g., "12345") -— otherwise, ignore this parameter, Pyrogram will ask you to input the phone code manually. - -Example: - -.. code-block:: python - - from pyrogram import Client - - def phone_code_callback(phone_number): - code = ... # Get your code programmatically - return code # e.g., "12345" - - - app = Client( - session_name="example", - phone_number="39**********", - phone_code=phone_code_callback, # Note the missing parentheses - password="password" # (if you have one) - ) - - with app: - print(app.get_me()) - -Sign Up -------- - -To automate the **Sign Up** process (i.e., automatically create a new Telegram account), simply fill **both** -``first_name`` and ``last_name`` fields alongside the other parameters; they will be used to automatically create a new -Telegram account in case the phone number you passed is not registered yet. - -Example: - -.. code-block:: python - - from pyrogram import Client - - def phone_code_callback(phone_number): - code = ... # Get your code programmatically - return code # e.g., "12345" - - - app = Client( - session_name="example", - phone_number="39**********", - phone_code=phone_code_callback, # Note the missing parentheses - first_name="Pyrogram", - last_name="" # Can be an empty string - ) - - with app: - print(app.get_me()) diff --git a/docs/source/topics/bots-interaction.rst b/docs/source/topics/bots-interaction.rst deleted file mode 100644 index de7925a272f..00000000000 --- a/docs/source/topics/bots-interaction.rst +++ /dev/null @@ -1,44 +0,0 @@ -Bots Interaction -================ - -Users can interact with other bots via plain text messages as well as inline queries. - -Inline Bots ------------ - -- If a bot accepts inline queries, you can call it by using - :meth:`get_inline_bot_results() ` to get the list of its inline results - for a query: - - .. code-block:: python - - # Get bot results for "Fuzz Universe" from the inline bot @vid - bot_results = app.get_inline_bot_results("vid", "Fuzz Universe") - - .. figure:: https://i.imgur.com/IAqLs54.png - :width: 90% - :align: center - :figwidth: 60% - - ``get_inline_bot_results()`` is the equivalent action of writing ``@vid Fuzz Universe`` and getting the - results list. - -- After you retrieved the bot results, you can use - :meth:`send_inline_bot_result() ` to send a chosen result to any chat: - - .. code-block:: python - - # Send the first result to your own chat - app.send_inline_bot_result( - "me", - bot_results.query_id, - bot_results.results[0].id - ) - - .. figure:: https://i.imgur.com/wwxr7B7.png - :width: 90% - :align: center - :figwidth: 60% - - ``send_inline_bot_result()`` is the equivalent action of choosing a result from the list and sending it - to a chat. diff --git a/docs/source/topics/changelog.rst b/docs/source/topics/changelog.rst deleted file mode 100644 index 732a1311ed5..00000000000 --- a/docs/source/topics/changelog.rst +++ /dev/null @@ -1,11 +0,0 @@ -Changelog -========= - -Currently, all Pyrogram release notes live inside the GitHub repository web page: -https://github.com/pyrogram/pyrogram/releases - -(You will be automatically redirected in 10 seconds.) - -.. raw:: html - - \ No newline at end of file diff --git a/docs/source/topics/configuration-file.rst b/docs/source/topics/configuration-file.rst deleted file mode 100644 index 2a50277fde6..00000000000 --- a/docs/source/topics/configuration-file.rst +++ /dev/null @@ -1,90 +0,0 @@ -Configuration File -================== - -As already mentioned in previous sections, Pyrogram can be configured by the use of an INI file. -This page explains how this file is structured in Pyrogram, how to use it and why. - -Introduction ------------- - -The idea behind using a configuration file is to help keeping your code free of private settings information such as -the API Key and Proxy without having you to even deal with how to load such settings. The configuration file, usually -referred as ``config.ini`` file, is automatically loaded from the root of your working directory; all you need to do is -fill in the necessary parts. - -.. note:: - - The configuration file is optional, but recommended. If, for any reason, you prefer not to use it, there's always an - alternative way to configure Pyrogram via Client's parameters. Doing so, you can have full control on how to store - and load your settings (e.g.: from environment variables). - - Settings specified via Client's parameter have higher priority and will override any setting stored in the - configuration file. - - -The config.ini File -------------------- - -By default, Pyrogram will look for a file named ``config.ini`` placed at the root of your working directory, that is, -the same folder of your running script. You can change the name or location of your configuration file by specifying it -in your Client's parameter *config_file*. - -- Replace the default *config.ini* file with *my_configuration.ini*: - - .. code-block:: python - - from pyrogram import Client - - app = Client("my_account", config_file="my_configuration.ini") - - -Configuration Sections ----------------------- - -These are all the sections Pyrogram uses in its configuration file: - -Pyrogram -^^^^^^^^ - -The ``[pyrogram]`` section contains your Telegram API credentials: *api_id* and *api_hash*. - -.. code-block:: ini - - [pyrogram] - api_id = 12345 - api_hash = 0123456789abcdef0123456789abcdef - -`More info about API Key. <../start/Setup.html#configuration>`_ - -Proxy -^^^^^ - -The ``[proxy]`` section contains settings about your SOCKS5 proxy. - -.. code-block:: ini - - [proxy] - enabled = True - hostname = 11.22.33.44 - port = 1080 - username = - password = - -`More info about SOCKS5 Proxy. `_ - -Plugins -^^^^^^^ - -The ``[plugins]`` section contains settings about Smart Plugins. - -.. code-block:: ini - - [plugins] - root = plugins - include = - module - folder.module - exclude = - module fn2 - -`More info about Smart Plugins. `_ diff --git a/docs/source/topics/customize-sessions.rst b/docs/source/topics/customize-sessions.rst deleted file mode 100644 index 77765287ef6..00000000000 --- a/docs/source/topics/customize-sessions.rst +++ /dev/null @@ -1,66 +0,0 @@ -Customize Sessions -================== - -As you may probably know, Telegram allows users (and bots) having more than one session (authorizations) registered -in the system at the same time. - -Briefly explaining, sessions are simply new logins in your account. They can be reviewed in the settings of an official -app (or by invoking `GetAuthorizations <../functions/account/GetAuthorizations.html>`_ with Pyrogram). They store some -useful information such as the client who's using them and from which country and IP address. - - -.. figure:: https://i.imgur.com/lzGPCdZ.png - :width: 70% - :align: center - - **A Pyrogram session running on Linux, Python 3.6.** - -That's how a session looks like on the Android app, showing the three main pieces of information. - -- ``app_version``: **Pyrogram 🔥 0.7.5** -- ``device_model``: **CPython 3.6.5** -- ``system_version``: **Linux 4.15.0-23-generic** - -Set Custom Values ------------------ - -To set custom values, you can either make use of the ``config.ini`` file, this way: - -.. code-block:: ini - - [pyrogram] - app_version = 1.2.3 - device_model = PC - system_version = Linux - -Or, pass the arguments directly in the Client's constructor. - -.. code-block:: python - - app = Client( - "my_account", - app_version="1.2.3", - device_model="PC", - system_version="Linux" - ) - -Set Custom Languages --------------------- - -To tell Telegram in which language should speak to you (terms of service, bots, service messages, ...) you can -set ``lang_code`` in `ISO 639-1 `_ standard (defaults to "en", -English). - -With the following code we make Telegram know we want it to speak in Italian (it): - -.. code-block:: ini - - [pyrogram] - lang_code = it - -.. code-block:: python - - app = Client( - "my_account", - lang_code="it", - ) \ No newline at end of file diff --git a/docs/source/topics/error-handling.rst b/docs/source/topics/error-handling.rst deleted file mode 100644 index 7e87b94ac1e..00000000000 --- a/docs/source/topics/error-handling.rst +++ /dev/null @@ -1,59 +0,0 @@ -Error Handling -============== - -Errors are inevitable when working with the API, and they must be correctly handled with ``try..except`` blocks. - -There are many errors that Telegram could return, but they all fall in one of these categories -(which are in turn children of the :obj:`RPCError ` superclass): - -- :obj:`303 - See Other ` -- :obj:`400 - Bad Request ` -- :obj:`401 - Unauthorized ` -- :obj:`403 - Forbidden ` -- :obj:`406 - Not Acceptable ` -- :obj:`420 - Flood ` -- :obj:`500 - Internal Server Error ` - -As stated above, there are really many (too many) errors, and in case Pyrogram does not know anything yet about a -specific one, it raises a special :obj:`520 Unknown Error ` exception and logs it -in the ``unknown_errors.txt`` file. Users are invited to report these unknown errors; in later versions of Pyrogram -some kind of automatic error reporting module might be implemented. - -Examples --------- - -.. code-block:: python - - from pyrogram.errors import ( - BadRequest, Flood, InternalServerError, - SeeOther, Unauthorized, UnknownError - ) - - try: - ... - except BadRequest: - pass - except Flood: - pass - except InternalServerError: - pass - except SeeOther: - pass - except Unauthorized: - pass - except UnknownError: - pass - -Exception objects may also contain some informative values. -E.g.: :obj:`FloodWait ` holds the amount of seconds you have to wait -before you can try again. The value is always stored in the ``x`` field of the returned exception object: - -.. code-block:: python - - import time - from pyrogram.errors import FloodWait - - try: - ... - except FloodWait as e: - time.sleep(e.x) diff --git a/docs/source/topics/more-on-updates.rst b/docs/source/topics/more-on-updates.rst deleted file mode 100644 index 8408662260e..00000000000 --- a/docs/source/topics/more-on-updates.rst +++ /dev/null @@ -1,222 +0,0 @@ -More on Updates -=============== - -Here we'll show some advanced usages when working with `update handlers`_ and `filters`_. - -Handler Groups --------------- - -If you register handlers with overlapping (conflicting) filters, only the first one is executed and any other handler -will be ignored. This is intended by design. - -In order to handle the very same update more than once, you have to register your handler in a different dispatching -group. Dispatching groups hold one or more handlers and are processed sequentially, they are identified by a number -(number 0 being the default) and sorted, that is, a lower group number has a higher priority: - -For example, take these two handlers: - -.. code-block:: python - :emphasize-lines: 1, 6 - - @app.on_message(Filters.text | Filters.sticker) - def text_or_sticker(client, message): - print("Text or Sticker") - - - @app.on_message(Filters.text) - def just_text(client, message): - print("Just Text") - -Here, ``just_text`` is never executed because ``text_or_sticker``, which has been registered first, already handles -texts (``Filters.text`` is shared and conflicting). To enable it, register the function using a different group: - -.. code-block:: python - - @app.on_message(Filters.text, group=1) - def just_text(client, message): - print("Just Text") - -Or, if you want ``just_text`` to be fired *before* ``text_or_sticker`` (note ``-1``, which is less than ``0``): - -.. code-block:: python - - @app.on_message(Filters.text, group=-1) - def just_text(client, message): - print("Just Text") - -With :meth:`add_handler() ` (without decorators) the same can be achieved with: - -.. code-block:: python - - app.add_handler(MessageHandler(just_text, Filters.text), -1) - -Update propagation ------------------- - -Registering multiple handlers, each in a different group, becomes useful when you want to handle the same update more -than once. Any incoming update will be sequentially processed by all of your registered functions by respecting the -groups priority policy described above. Even in case any handler raises an unhandled exception, Pyrogram will still -continue to propagate the same update to the next groups until all the handlers are done. Example: - -.. code-block:: python - - @app.on_message(Filters.private) - def _(client, message): - print(0) - - - @app.on_message(Filters.private, group=1) - def _(client, message): - raise Exception("Unhandled exception!") # Simulate an unhandled exception - - - @app.on_message(Filters.private, group=2) - def _(client, message): - print(2) - -All these handlers will handle the same kind of messages, that are, messages sent or received in private chats. -The output for each incoming update will therefore be: - -.. code-block:: text - - 0 - Exception: Unhandled exception! - 2 - -Stop Propagation -^^^^^^^^^^^^^^^^ - -In order to prevent further propagation of an update in the dispatching phase, you can do *one* of the following: - -- Call the update's bound-method ``.stop_propagation()`` (preferred way). -- Manually ``raise StopPropagation`` exception (more suitable for raw updates only). - -.. note:: - - Internally, the propagation is stopped by handling a custom exception. ``.stop_propagation()`` is just an elegant - and intuitive way to ``raise StopPropagation``; this also means that any code coming *after* calling the method - won't be executed as your function just raised an exception to signal the dispatcher not to propagate the - update anymore. - -Example with ``stop_propagation()``: - -.. code-block:: python - - @app.on_message(Filters.private) - def _(client, message): - print(0) - - - @app.on_message(Filters.private, group=1) - def _(client, message): - print(1) - message.stop_propagation() - - - @app.on_message(Filters.private, group=2) - def _(client, message): - print(2) - -Example with ``raise StopPropagation``: - -.. code-block:: python - - from pyrogram import StopPropagation - - @app.on_message(Filters.private) - def _(client, message): - print(0) - - - @app.on_message(Filters.private, group=1) - def _(client, message): - print(1) - raise StopPropagation - - - @app.on_message(Filters.private, group=2) - def _(client, message): - print(2) - -Each handler is registered in a different group, but the handler in group number 2 will never be executed because the -propagation was stopped earlier. The output of both (equivalent) examples will be: - -.. code-block:: text - - 0 - 1 - -Continue Propagation -^^^^^^^^^^^^^^^^^^^^ - -As opposed to `stopping the update propagation <#stop-propagation>`_ and also as an alternative to the -`handler groups <#handler-groups>`_, you can signal the internal dispatcher to continue the update propagation within -**the same group** despite having conflicting filters in the next registered handler. This allows you to register -multiple handlers with overlapping filters in the same group; to let the dispatcher process the next handler you can do -*one* of the following in each handler you want to grant permission to continue: - -- Call the update's bound-method ``.continue_propagation()`` (preferred way). -- Manually ``raise ContinuePropagation`` exception (more suitable for raw updates only). - -.. note:: - - Internally, the propagation is continued by handling a custom exception. ``.continue_propagation()`` is just an - elegant and intuitive way to ``raise ContinuePropagation``; this also means that any code coming *after* calling the - method won't be executed as your function just raised an exception to signal the dispatcher to continue with the - next available handler. - - -Example with ``continue_propagation()``: - -.. code-block:: python - - @app.on_message(Filters.private) - def _(client, message): - print(0) - message.continue_propagation() - - - @app.on_message(Filters.private) - def _(client, message): - print(1) - message.continue_propagation() - - - @app.on_message(Filters.private) - def _(client, message): - print(2) - -Example with ``raise ContinuePropagation``: - -.. code-block:: python - - from pyrogram import ContinuePropagation - - @app.on_message(Filters.private) - def _(client, message): - print(0) - raise ContinuePropagation - - - @app.on_message(Filters.private) - def _(client, message): - print(1) - raise ContinuePropagation - - - @app.on_message(Filters.private) - def _(client, message): - print(2) - -Three handlers are registered in the same group, and all of them will be executed because the propagation was continued -in each handler (except in the last one, where is useless to do so since there is no more handlers after). -The output of both (equivalent) examples will be: - -.. code-block:: text - - 0 - 1 - 2 - -.. _`update handlers`: UpdateHandling.html -.. _`filters`: UsingFilters.html \ No newline at end of file diff --git a/docs/source/topics/smart-plugins.rst b/docs/source/topics/smart-plugins.rst deleted file mode 100644 index 6f266590ecd..00000000000 --- a/docs/source/topics/smart-plugins.rst +++ /dev/null @@ -1,356 +0,0 @@ -Smart Plugins -============= - -Pyrogram embeds a **smart**, lightweight yet powerful plugin system that is meant to further simplify the organization -of large projects and to provide a way for creating pluggable (modular) components that can be **easily shared** across -different Pyrogram applications with **minimal boilerplate code**. - -.. tip:: - - Smart Plugins are completely optional and disabled by default. - -Introduction ------------- - -Prior to the Smart Plugin system, pluggable handlers were already possible. For example, if you wanted to modularize -your applications, you had to put your function definitions in separate files and register them inside your main script -after importing your modules, like this: - -.. note:: - - This is an example application that replies in private chats with two messages: one containing the same - text message you sent and the other containing the reversed text message. - - Example: *"Pyrogram"* replies with *"Pyrogram"* and *"margoryP"* - -.. code-block:: text - - myproject/ - config.ini - handlers.py - main.py - -- ``handlers.py`` - - .. code-block:: python - - def echo(client, message): - message.reply(message.text) - - - def echo_reversed(client, message): - message.reply(message.text[::-1]) - -- ``main.py`` - - .. code-block:: python - - from pyrogram import Client, MessageHandler, Filters - - from handlers import echo, echo_reversed - - app = Client("my_account") - - app.add_handler( - MessageHandler( - echo, - Filters.text & Filters.private)) - - app.add_handler( - MessageHandler( - echo_reversed, - Filters.text & Filters.private), - group=1) - - app.run() - -This is already nice and doesn't add *too much* boilerplate code, but things can get boring still; you have to -manually ``import``, manually :meth:`add_handler ` and manually instantiate each -:obj:`MessageHandler ` object because **you can't use those cool decorators** for your -functions. So, what if you could? Smart Plugins solve this issue by taking care of handlers registration automatically. - -Using Smart Plugins -------------------- - -Setting up your Pyrogram project to accommodate Smart Plugins is pretty straightforward: - -#. Create a new folder to store all the plugins (e.g.: "plugins", "handlers", ...). -#. Put your python files full of plugins inside. Organize them as you wish. -#. Enable plugins in your Client or via the *config.ini* file. - -.. note:: - - This is the same example application `as shown above <#introduction>`_, written using the Smart Plugin system. - -.. code-block:: text - :emphasize-lines: 2, 3 - - myproject/ - plugins/ - handlers.py - config.ini - main.py - -- ``plugins/handlers.py`` - - .. code-block:: python - :emphasize-lines: 4, 9 - - from pyrogram import Client, Filters - - - @Client.on_message(Filters.text & Filters.private) - def echo(client, message): - message.reply(message.text) - - - @Client.on_message(Filters.text & Filters.private, group=1) - def echo_reversed(client, message): - message.reply(message.text[::-1]) - -- ``config.ini`` - - .. code-block:: ini - - [plugins] - root = plugins - -- ``main.py`` - - .. code-block:: python - - from pyrogram import Client - - Client("my_account").run() - - Alternatively, without using the *config.ini* file: - - .. code-block:: python - - from pyrogram import Client - - plugins = dict(root="plugins") - - Client("my_account", plugins=plugins).run() - - -The first important thing to note is the new ``plugins`` folder. You can put *any python file* in *any subfolder* and -each file can contain *any decorated function* (handlers) with one limitation: within a single module (file) you must -use different names for each decorated function. - -The second thing is telling Pyrogram where to look for your plugins: you can either use the *config.ini* file or -the Client parameter "plugins"; the *root* value must match the name of your plugins root folder. Your Pyrogram Client -instance will **automatically** scan the folder upon starting to search for valid handlers and register them for you. - -Then you'll notice you can now use decorators. That's right, you can apply the usual decorators to your callback -functions in a static way, i.e. **without having the Client instance around**: simply use ``@Client`` (Client class) -instead of the usual ``@app`` (Client instance) and things will work just the same. - -Specifying the Plugins to include ---------------------------------- - -By default, if you don't explicitly supply a list of plugins, every valid one found inside your plugins root folder will -be included by following the alphabetical order of the directory structure (files and subfolders); the single handlers -found inside each module will be, instead, loaded in the order they are defined, from top to bottom. - -.. note:: - - Remember: there can be at most one handler, within a group, dealing with a specific update. Plugins with overlapping - filters included a second time will not work. Learn more at `More on Updates `_. - -This default loading behaviour is usually enough, but sometimes you want to have more control on what to include (or -exclude) and in which exact order to load plugins. The way to do this is to make use of ``include`` and ``exclude`` -directives, either in the *config.ini* file or in the dictionary passed as Client argument. Here's how they work: - -- If both ``include`` and ``exclude`` are omitted, all plugins are loaded as described above. -- If ``include`` is given, only the specified plugins will be loaded, in the order they are passed. -- If ``exclude`` is given, the plugins specified here will be unloaded. - -The ``include`` and ``exclude`` value is a **list of strings**. Each string containing the path of the module relative -to the plugins root folder, in Python notation (dots instead of slashes). - - E.g.: ``subfolder.module`` refers to ``plugins/subfolder/module.py``, with ``root="plugins"``. - -You can also choose the order in which the single handlers inside a module are loaded, thus overriding the default -top-to-bottom loading policy. You can do this by appending the name of the functions to the module path, each one -separated by a blank space. - - E.g.: ``subfolder.module fn2 fn1 fn3`` will load *fn2*, *fn1* and *fn3* from *subfolder.module*, in this order. - -Examples -^^^^^^^^ - -Given this plugins folder structure with three modules, each containing their own handlers (fn1, fn2, etc...), which are -also organized in subfolders: - -.. code-block:: text - - myproject/ - plugins/ - subfolder1/ - plugins1.py - - fn1 - - fn2 - - fn3 - subfolder2/ - plugins2.py - ... - plugins0.py - ... - ... - -- Load every handler from every module, namely *plugins0.py*, *plugins1.py* and *plugins2.py* in alphabetical order - (files) and definition order (handlers inside files): - - Using *config.ini* file: - - .. code-block:: ini - - [plugins] - root = plugins - - Using *Client*'s parameter: - - .. code-block:: python - - plugins = dict(root="plugins") - - Client("my_account", plugins=plugins).run() - -- Load only handlers defined inside *plugins2.py* and *plugins0.py*, in this order: - - Using *config.ini* file: - - .. code-block:: ini - - [plugins] - root = plugins - include = - subfolder2.plugins2 - plugins0 - - Using *Client*'s parameter: - - .. code-block:: python - - plugins = dict( - root="plugins", - include=[ - "subfolder2.plugins2", - "plugins0" - ] - ) - - Client("my_account", plugins=plugins).run() - -- Load everything except the handlers inside *plugins2.py*: - - Using *config.ini* file: - - .. code-block:: ini - - [plugins] - root = plugins - exclude = subfolder2.plugins2 - - Using *Client*'s parameter: - - .. code-block:: python - - plugins = dict( - root="plugins", - exclude=["subfolder2.plugins2"] - ) - - Client("my_account", plugins=plugins).run() - -- Load only *fn3*, *fn1* and *fn2* (in this order) from *plugins1.py*: - - Using *config.ini* file: - - .. code-block:: ini - - [plugins] - root = plugins - include = subfolder1.plugins1 fn3 fn1 fn2 - - Using *Client*'s parameter: - - .. code-block:: python - - plugins = dict( - root="plugins", - include=["subfolder1.plugins1 fn3 fn1 fn2"] - ) - - Client("my_account", plugins=plugins).run() - -Load/Unload Plugins at Runtime ------------------------------- - -In the `previous section <#specifying-the-plugins-to-include>`_ we've explained how to specify which plugins to load and -which to ignore before your Client starts. Here we'll show, instead, how to unload and load again a previously -registered plugin at runtime. - -Each function decorated with the usual ``on_message`` decorator (or any other decorator that deals with Telegram updates -) will be modified in such a way that, when you reference them later on, they will be actually pointing to a tuple of -*(handler: Handler, group: int)*. The actual callback function is therefore stored inside the handler's *callback* -attribute. Here's an example: - -- ``plugins/handlers.py`` - - .. code-block:: python - :emphasize-lines: 5, 6 - - @Client.on_message(Filters.text & Filters.private) - def echo(client, message): - message.reply(message.text) - - print(echo) - print(echo[0].callback) - -- Printing ``echo`` will show something like ``(, 0)``. - -- Printing ``echo[0].callback``, that is, the *callback* attribute of the first element of the tuple, which is an - Handler, will reveal the actual callback ````. - -Unloading -^^^^^^^^^ - -In order to unload a plugin, or any other handler, all you need to do is obtain a reference to it by importing the -relevant module and call :meth:`remove_handler() ` Client's method with your function -name preceded by the star ``*`` operator as argument. Example: - -- ``main.py`` - - .. code-block:: python - - from plugins.handlers import echo - - ... - - app.remove_handler(*echo) - -The star ``*`` operator is used to unpack the tuple into positional arguments so that *remove_handler* will receive -exactly what is needed. The same could have been achieved with: - -.. code-block:: python - - handler, group = echo - app.remove_handler(handler, group) - -Loading -^^^^^^^ - -Similarly to the unloading process, in order to load again a previously unloaded plugin you do the same, but this time -using :meth:`add_handler() ` instead. Example: - -- ``main.py`` - - .. code-block:: python - - from plugins.handlers import echo - - ... - - app.add_handler(*echo) \ No newline at end of file diff --git a/docs/source/topics/socks5-proxy.rst b/docs/source/topics/socks5-proxy.rst deleted file mode 100644 index 761899e621e..00000000000 --- a/docs/source/topics/socks5-proxy.rst +++ /dev/null @@ -1,50 +0,0 @@ -SOCKS5 Proxy -============ - -Pyrogram supports proxies with and without authentication. This feature allows Pyrogram to exchange data with Telegram -through an intermediate SOCKS5 proxy server. - -Usage ------ - -- To use Pyrogram with a proxy, simply append the following to your ``config.ini`` file and replace the values - with your own settings: - - .. code-block:: ini - - [proxy] - enabled = True - hostname = 11.22.33.44 - port = 1080 - username = - password = - - To enable or disable the proxy without deleting your settings from the config file, - change the ``enabled`` value as follows: - - - ``1``, ``yes``, ``True`` or ``on``: Enables the proxy - - ``0``, ``no``, ``False`` or ``off``: Disables the proxy - -- Alternatively, you can setup your proxy without the need of the ``config.ini`` file by using the *proxy* parameter - in the Client class: - - .. code-block:: python - - from pyrogram import Client - - app = Client( - session_name="example", - proxy=dict( - hostname="11.22.33.44", - port=1080, - username="", - password="" - ) - ) - - app.start() - - ... - -.. note:: If your proxy doesn't require authorization you can omit ``username`` and ``password`` by either leaving the - values blank/empty or completely delete the lines. \ No newline at end of file diff --git a/docs/source/topics/test-servers.rst b/docs/source/topics/test-servers.rst deleted file mode 100644 index 2f82f24c3a4..00000000000 --- a/docs/source/topics/test-servers.rst +++ /dev/null @@ -1,39 +0,0 @@ -Test Servers -============ - -If you wish to test your application in a separate environment, Pyrogram is able to authorize your account into -Telegram's test servers without hassle. All you need to do is start a new session (e.g.: "my_account_test") using -``test_mode=True``: - -.. code-block:: python - - from pyrogram import Client - - with Client("my_account_test", test_mode=True) as app: - print(app.get_me()) - -.. note:: - - If this is the first time you login into test servers, you will be asked to register your account first. - Don't worry about your contacts and chats, they will be kept untouched inside the production environment; - accounts authorized on test servers reside in a different, parallel instance of a Telegram database. - -Test Mode in Official Apps --------------------------- - -You can also login yourself into test servers using official desktop apps, such as Webogram and TDesktop: - -- **Webogram**: Login here: https://web.telegram.org/?test=1 -- **TDesktop**: Open settings and type ``testmode``. - -Test Numbers ------------- - -Beside normal numbers, the test environment allows you to login with reserved test numbers. -Valid phone numbers follow the pattern ``99966XYYYY``, where ``X`` is the DC number (1 to 3) and ``YYYY`` are random -numbers. Users with such numbers always get ``XXXXX`` as the confirmation code (the DC number, repeated five times). - -.. important:: - - Do not store any important or private information in such test users' accounts; anyone can make use of the - simplified authorization mechanism and login at any time. diff --git a/docs/source/topics/text-formatting.rst b/docs/source/topics/text-formatting.rst deleted file mode 100644 index 0ab086944c1..00000000000 --- a/docs/source/topics/text-formatting.rst +++ /dev/null @@ -1,97 +0,0 @@ -Text Formatting -=============== - -Pyrogram, just like the `Telegram Bot API`_, natively supports basic Markdown and HTML formatting styles for text -messages and media captions. - -Markdown style uses the same syntax as Telegram Desktop's and is enabled by default. - -Beside bold, italic, and pre-formatted code, **Pyrogram does also support inline URLs and inline mentions of users**. - -Markdown Style --------------- - -To use this mode, pass :obj:`MARKDOWN ` or "markdown" in the *parse_mode* field when using -:obj:`send_message() `. Use the following syntax in your message: - -.. code-block:: text - - **bold text** - - __italic text__ - - [inline URL](https://docs.pyrogram.ml/) - - [inline mention of a user](tg://user?id=23122162) - - `inline fixed-width code` - - ```block_language - pre-formatted fixed-width code block - ``` - - -HTML Style ----------- - -To use this mode, pass :obj:`HTML ` or "html" in the *parse_mode* field when using -:obj:`send_message() `. The following tags are currently supported: - -.. code-block:: text - - bold, bold - - italic, italic - - inline URL - - inline mention of a user - - inline fixed-width code - -
pre-formatted fixed-width code block
- -.. note:: Mentions are only guaranteed to work if you have already met the user (in groups or private chats). - -Examples --------- - -- Markdown: - - .. code-block:: python - - app.send_message( - chat_id="haskell", - text=( - "**bold**, " - "__italic__, " - "[mention](tg://user?id=23122162), " - "[URL](https://docs.pyrogram.ml), " - "`code`, " - "```" - "for i in range(10):\n" - " print(i)```" - ) - ) - -- HTML: - - .. code-block:: python - - app.send_message( - chat_id="haskell", - text=( - "bold, " - "italic, " - "mention, " - "URL, " - "code, " - "
"
-                "for i in range(10):\n"
-                "    print(i)"
-                "
" - ), - parse_mode="html" - ) - -.. _Telegram Bot API: https://core.telegram.org/bots/api#formatting-options \ No newline at end of file diff --git a/docs/source/topics/tgcrypto.rst b/docs/source/topics/tgcrypto.rst deleted file mode 100644 index 2af09a065bb..00000000000 --- a/docs/source/topics/tgcrypto.rst +++ /dev/null @@ -1,32 +0,0 @@ -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. - -TgCrypto is a replacement for the much slower PyAES and implements the crypto algorithms Telegram requires, namely -**AES-IGE 256 bit** (used in MTProto v2.0) and **AES-CTR 256 bit** (used for CDN encrypted files). - -Installation ------------- - -.. code-block:: bash - - $ pip3 install --upgrade tgcrypto - -.. note:: Being a C extension for Python, TgCrypto is an optional but *highly recommended* dependency; when TgCrypto is - not detected in your system, Pyrogram will automatically fall back to PyAES and will show you a warning. - -The reason about being an optional package is that TgCrypto requires some extra system tools in order to be compiled. -The errors you receive when trying to install TgCrypto are system dependent, but also descriptive enough to understand -what you should do next: - -- **Windows**: Install `Visual C++ 2015 Build Tools `_. -- **macOS**: A pop-up will automatically ask you to install the command line developer tools. -- **Linux**: Install a proper C compiler (``gcc``, ``clang``) and the Python header files (``python3-dev``). -- **Termux (Android)**: Install ``clang`` and ``python-dev`` packages. - -.. _TgCrypto: https://github.com/pyrogram/tgcrypto - -.. [#f1] Although TgCrypto is intended for Pyrogram, it is shipped as a standalone package and can thus be used for - other Python projects too. diff --git a/docs/source/topics/update-handling.rst b/docs/source/topics/update-handling.rst deleted file mode 100644 index c32bc2be4e2..00000000000 --- a/docs/source/topics/update-handling.rst +++ /dev/null @@ -1,109 +0,0 @@ -Update Handling -=============== - -Calling `API methods`_ sequentially is cool, but how to react when, for example, a new message arrives? This page deals -with updates and how to handle them in Pyrogram. Let's have a look at how they work. - -First, let's define what are these updates. Updates are simply events that happen in your Telegram account (incoming -messages, new members join, button presses, etc...), which are meant to notify you about a new specific state that -changed. These updates are handled by registering one or more callback functions in your app using -`Handlers <../pyrogram/Handlers.html>`_. - -Each handler deals with a specific event and once a matching update arrives from Telegram, your registered callback -function will be called and its body executed. - -Registering an Handler ----------------------- - -To explain how handlers work let's have a look at the most used one, the -:obj:`MessageHandler `, which will be in charge for handling :obj:`Message ` -updates coming from all around your chats. Every other handler shares the same setup logic; you should not have troubles -settings them up once you learn from this section. - -Using add_handler() -------------------- - -The :meth:`add_handler() ` method takes any handler instance that wraps around your defined -callback function and registers it in your Client. Here's a full example that prints out the content of a message as -soon as it arrives: - -.. code-block:: python - - from pyrogram import Client, MessageHandler - - - def my_function(client, message): - print(message) - - - app = Client("my_account") - - my_handler = MessageHandler(my_function) - app.add_handler(my_handler) - - app.run() - -Let's examine these four new pieces. First one: a callback function we defined which accepts two arguments - -*(client, message)*. This will be the function that gets executed every time a new message arrives and Pyrogram will -call that function by passing the client instance and the new message instance as argument. - -.. code-block:: python - - def my_function(client, message): - print(message) - -Second one: the :obj:`MessageHandler `. This object tells Pyrogram the function we defined -above must only handle updates that are in form of a :obj:`Message `: - -.. code-block:: python - - my_handler = MessageHandler(my_function) - -Third: the method :meth:`add_handler() `. This method is used to actually register the -handler and let Pyrogram know it needs to be taken into consideration when new updates arrive and the dispatching phase -begins. - -.. code-block:: python - - app.add_handler(my_handler) - -Last one, the :meth:`run() ` method. What this does is simply calling -:meth:`start() ` and a special method :meth:`idle() ` that keeps your main -scripts alive until you press ``CTRL+C``; the client will be automatically stopped after that. - -.. code-block:: python - - app.run() - -Using Decorators ----------------- - -All of the above will become quite verbose, especially in case you have lots of handlers to register. A much nicer way -to do so is by decorating your callback function with the :meth:`on_message() ` decorator. - -.. code-block:: python - - from pyrogram import Client - - app = Client("my_account") - - - @app.on_message() - def my_handler(client, message): - print(message) - - - app.run() - - -.. note:: - - Due to how these decorators work in Pyrogram, they will wrap your defined callback function in a tuple consisting of - ``(handler, group)``; this will be the value held by your function identifier (e.g.: *my_function* from the example - above). - - In case, for some reason, you want to get your own function back after it has been decorated, you need to access - ``my_function[0].callback``, that is, the *callback* field of the *handler* object which is the first element in the - tuple, accessed by bracket notation *[0]*. - -.. _API methods: usage.html \ No newline at end of file diff --git a/docs/source/topics/usage.rst b/docs/source/topics/usage.rst deleted file mode 100644 index 34f5787650f..00000000000 --- a/docs/source/topics/usage.rst +++ /dev/null @@ -1,85 +0,0 @@ -API Usage -========= - -At this point, we have successfully `installed Pyrogram`_ and authorized_ our account and we are now pointing towards -the core of the library. It's time to start playing with the API! - -Make API Method Calls ---------------------- - -Making API method calls with Pyrogram is very simple. -Here's an example we are going to examine: - -.. code-block:: python - - from pyrogram import Client - - app = Client("my_account") - - app.start() - - print(app.get_me()) - app.send_message("me", "Hi, it's me!") - app.send_location("me", 51.500729, -0.124583) - app.send_sticker("me", "CAADBAADyg4AAvLQYAEYD4F7vcZ43AI") - - app.stop() - -Let's begin by importing the Client class from the Pyrogram package: - -.. code-block:: python - - from pyrogram import Client - -Now instantiate a new Client object, "my_account" is a session name of your choice: - -.. code-block:: python - - app = Client("my_account") - -To actually make use of any method, the client has to be started: - -.. code-block:: python - - app.start() - -Now, you can call any method you like: - -.. code-block:: python - - print(app.get_me()) # Print information about yourself - - # Send messages to yourself: - app.send_message("me", "Hi!") # Text message - app.send_location("me", 51.500729, -0.124583) # Location - app.send_sticker("me", "CAADBAADyg4AAvLQYAEYD4F7vcZ43AI") # Sticker - -Finally, when done, simply stop the client: - -.. code-block:: python - - app.stop() - -Context Manager ---------------- - -You can also use Pyrogram's Client in a context manager with the ``with`` statement. The client will automatically -:meth:`start ` and :meth:`stop ` gracefully, even in case of unhandled -exceptions in your code. The example above can be therefore rewritten in a much nicer way, this way: - -.. code-block:: python - - from pyrogram import Client - - app = Client("my_account") - - with app: - print(app.get_me()) - app.send_message("me", "Hi there! I'm using **Pyrogram**") - app.send_location("me", 51.500729, -0.124583) - app.send_sticker("me", "CAADBAADyg4AAvLQYAEYD4F7vcZ43AI") - -More examples can be found on `GitHub `_. - -.. _installed Pyrogram: ../intro/install.html -.. _authorized: ../intro/setup.html diff --git a/docs/source/topics/using-filters.rst b/docs/source/topics/using-filters.rst deleted file mode 100644 index ec3e2e10c02..00000000000 --- a/docs/source/topics/using-filters.rst +++ /dev/null @@ -1,194 +0,0 @@ -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:`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. - -Let's start right away with a simple example: - -- This example will show you how to **only** handle messages containing an :obj:`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: - - .. 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:`command() ` or :meth:`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") - -Custom Filters --------------- - -Pyrogram already provides lots of built-in :class:`Filters ` to work with, but in case you can't find -a specific one for your needs or want to build a custom filter by yourself (to be used in a different kind of handler, -for example) you can use :meth:`Filters.create() `. - -.. note:: - At the moment, the built-in filters are intended to be used with the :obj:`MessageHandler ` - only. - -An example to demonstrate how custom filters work is to show how to create and use one for the -:obj:`CallbackQueryHandler `. Note that callback queries updates are only received by -bots; create and `authorize your bot <../start/Setup.html#bot-authorization>`_, then send a message with an inline -keyboard to yourself. This allows you to test your filter by pressing the inline button: - -.. code-block:: python - - from pyrogram import InlineKeyboardMarkup, InlineKeyboardButton - - app.send_message( - "username", # Change this to your username or id - "Pyrogram's custom filter test", - reply_markup=InlineKeyboardMarkup( - [[InlineKeyboardButton("Press me", b"pyrogram")]] - ) - ) - -Basic Filters -^^^^^^^^^^^^^ - -For this basic filter we will be using only the first two parameters of :meth:`Filters.create() `. - -The code below creates a simple filter for hardcoded, static callback data. This filter will only allow callback queries -containing "Pyrogram" as data, that is, the function *func* you pass returns True in case the callback query data -equals to ``b"Pyrogram"``. - -.. code-block:: python - - static_data = Filters.create( - name="StaticdData", - func=lambda flt, callback_query: callback_query.data == b"Pyrogram" - ) - -The ``lambda`` operator in python is used to create small anonymous functions and is perfect for this example, the same -could be achieved with a normal function, but we don't really need it as it makes sense only inside the filter's scope: - -.. code-block:: python - - def func(flt, callback_query): - return callback_query.data == b"Pyrogram" - - static_data = Filters.create( - name="StaticData", - func=func - ) - -The filter usage remains the same: - -.. code-block:: python - - @app.on_callback_query(static_data) - def pyrogram_data(client, callback_query): - client.answer_callback_query(callback_query.id, "it works!") - -Filters with Arguments -^^^^^^^^^^^^^^^^^^^^^^ - -A much cooler filter would be one that accepts "Pyrogram" or any other data as argument at usage time. -A dynamic filter like this will make use of the third parameter of :meth:`Filters.create() `. - -This is how a dynamic custom filter looks like: - -.. code-block:: python - - def dynamic_data(data): - return Filters.create( - name="DynamicData", - func=lambda flt, callback_query: flt.data == callback_query.data, - data=data # "data" kwarg is accessed with "filter.data" - ) - -And its usage: - -.. code-block:: python - - @app.on_callback_query(dynamic_data(b"Pyrogram")) - def pyrogram_data(client, callback_query): - client.answer_callback_query(callback_query.id, "it works!") \ No newline at end of file diff --git a/docs/source/topics/voice-calls.rst b/docs/source/topics/voice-calls.rst deleted file mode 100644 index c1a8cc53c34..00000000000 --- a/docs/source/topics/voice-calls.rst +++ /dev/null @@ -1,10 +0,0 @@ -Voice Calls -=========== - -A working proof-of-concept of Telegram voice calls using Pyrogram can be found here: -https://github.com/bakatrouble/pylibtgvoip. Thanks to `@bakatrouble `_. - -.. note:: - - This page will be updated with more information once voice calls become eventually more usable and more integrated - in Pyrogram itself. diff --git a/examples/LICENSE b/examples/LICENSE deleted file mode 100644 index 1625c179360..00000000000 --- a/examples/LICENSE +++ /dev/null @@ -1,121 +0,0 @@ -Creative Commons Legal Code - -CC0 1.0 Universal - - CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE - LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN - ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS - INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES - REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS - PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM - THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED - HEREUNDER. - -Statement of Purpose - -The laws of most jurisdictions throughout the world automatically confer -exclusive Copyright and Related Rights (defined below) upon the creator -and subsequent owner(s) (each and all, an "owner") of an original work of -authorship and/or a database (each, a "Work"). - -Certain owners wish to permanently relinquish those rights to a Work for -the purpose of contributing to a commons of creative, cultural and -scientific works ("Commons") that the public can reliably and without fear -of later claims of infringement build upon, modify, incorporate in other -works, reuse and redistribute as freely as possible in any form whatsoever -and for any purposes, including without limitation commercial purposes. -These owners may contribute to the Commons to promote the ideal of a free -culture and the further production of creative, cultural and scientific -works, or to gain reputation or greater distribution for their Work in -part through the use and efforts of others. - -For these and/or other purposes and motivations, and without any -expectation of additional consideration or compensation, the person -associating CC0 with a Work (the "Affirmer"), to the extent that he or she -is an owner of Copyright and Related Rights in the Work, voluntarily -elects to apply CC0 to the Work and publicly distribute the Work under its -terms, with knowledge of his or her Copyright and Related Rights in the -Work and the meaning and intended legal effect of CC0 on those rights. - -1. Copyright and Related Rights. A Work made available under CC0 may be -protected by copyright and related or neighboring rights ("Copyright and -Related Rights"). Copyright and Related Rights include, but are not -limited to, the following: - - i. the right to reproduce, adapt, distribute, perform, display, - communicate, and translate a Work; - ii. moral rights retained by the original author(s) and/or performer(s); -iii. publicity and privacy rights pertaining to a person's image or - likeness depicted in a Work; - iv. rights protecting against unfair competition in regards to a Work, - subject to the limitations in paragraph 4(a), below; - v. rights protecting the extraction, dissemination, use and reuse of data - in a Work; - vi. database rights (such as those arising under Directive 96/9/EC of the - European Parliament and of the Council of 11 March 1996 on the legal - protection of databases, and under any national implementation - thereof, including any amended or successor version of such - directive); and -vii. other similar, equivalent or corresponding rights throughout the - world based on applicable law or treaty, and any national - implementations thereof. - -2. Waiver. To the greatest extent permitted by, but not in contravention -of, applicable law, Affirmer hereby overtly, fully, permanently, -irrevocably and unconditionally waives, abandons, and surrenders all of -Affirmer's Copyright and Related Rights and associated claims and causes -of action, whether now known or unknown (including existing as well as -future claims and causes of action), in the Work (i) in all territories -worldwide, (ii) for the maximum duration provided by applicable law or -treaty (including future time extensions), (iii) in any current or future -medium and for any number of copies, and (iv) for any purpose whatsoever, -including without limitation commercial, advertising or promotional -purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each -member of the public at large and to the detriment of Affirmer's heirs and -successors, fully intending that such Waiver shall not be subject to -revocation, rescission, cancellation, termination, or any other legal or -equitable action to disrupt the quiet enjoyment of the Work by the public -as contemplated by Affirmer's express Statement of Purpose. - -3. Public License Fallback. Should any part of the Waiver for any reason -be judged legally invalid or ineffective under applicable law, then the -Waiver shall be preserved to the maximum extent permitted taking into -account Affirmer's express Statement of Purpose. In addition, to the -extent the Waiver is so judged Affirmer hereby grants to each affected -person a royalty-free, non transferable, non sublicensable, non exclusive, -irrevocable and unconditional license to exercise Affirmer's Copyright and -Related Rights in the Work (i) in all territories worldwide, (ii) for the -maximum duration provided by applicable law or treaty (including future -time extensions), (iii) in any current or future medium and for any number -of copies, and (iv) for any purpose whatsoever, including without -limitation commercial, advertising or promotional purposes (the -"License"). The License shall be deemed effective as of the date CC0 was -applied by Affirmer to the Work. Should any part of the License for any -reason be judged legally invalid or ineffective under applicable law, such -partial invalidity or ineffectiveness shall not invalidate the remainder -of the License, and in such case Affirmer hereby affirms that he or she -will not (i) exercise any of his or her remaining Copyright and Related -Rights in the Work or (ii) assert any associated claims and causes of -action with respect to the Work, in either case contrary to Affirmer's -express Statement of Purpose. - -4. Limitations and Disclaimers. - - a. No trademark or patent rights held by Affirmer are waived, abandoned, - surrendered, licensed or otherwise affected by this document. - b. Affirmer offers the Work as-is and makes no representations or - warranties of any kind concerning the Work, express, implied, - statutory or otherwise, including without limitation warranties of - title, merchantability, fitness for a particular purpose, non - infringement, or the absence of latent or other defects, accuracy, or - the present or absence of errors, whether or not discoverable, all to - the greatest extent permissible under applicable law. - c. Affirmer disclaims responsibility for clearing rights of other persons - that may apply to the Work or any use thereof, including without - limitation any person's Copyright and Related Rights in the Work. - Further, Affirmer disclaims responsibility for obtaining any necessary - consents, permissions or other rights required for any use of the - Work. - d. Affirmer understands and acknowledges that Creative Commons is not a - party to this document and has no duty or obligation with respect to - this CC0 or use of the Work. \ No newline at end of file diff --git a/examples/README.md b/examples/README.md deleted file mode 100644 index b8898a718a4..00000000000 --- a/examples/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Examples - -This folder contains example scripts to show you how **Pyrogram** looks like. - -Every script is working right away (provided you correctly set up your credentials), meaning you can simply copy-paste -and run. The only things you have to change are session names and target chats. - -All the examples listed in this directory are licensed under the terms of the [CC0 1.0 Universal](LICENSE) license and -can be freely used as basic building blocks for your own applications without worrying about copyrights. - -Example | Description ----: | :--- -[**hello_world**](hello_world.py) | Demonstration of basic API usage -[**echobot**](echobot.py) | Echo every private text message -[**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/bot_keyboards.py b/examples/bot_keyboards.py deleted file mode 100644 index 4cbe8eaab02..00000000000 --- a/examples/bot_keyboards.py +++ /dev/null @@ -1,57 +0,0 @@ -"""This example will show you how to send normal and inline keyboards (as bot). - -You must log-in as a regular bot in order to send keyboards (use the token from @BotFather). -Any attempt in sending keyboards with a user account will be simply ignored by the server. - -send_message() is used as example, but a keyboard can be sent with any other send_* methods, -like send_audio(), send_document(), send_location(), etc... -""" - -from pyrogram import Client, ReplyKeyboardMarkup, InlineKeyboardMarkup, InlineKeyboardButton - -# Create a client using your bot token -app = Client("my_bot", bot_token="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11") - -with app: - app.send_message( - "haskell", # Edit this - "This is a ReplyKeyboardMarkup example", - reply_markup=ReplyKeyboardMarkup( - [ - ["A", "B", "C", "D"], # First row - ["E", "F", "G"], # Second row - ["H", "I"], # Third row - ["J"] # Fourth row - ], - resize_keyboard=True # Make the keyboard smaller - ) - ) - - app.send_message( - "haskell", # Edit this - "This is a InlineKeyboardMarkup example", - reply_markup=InlineKeyboardMarkup( - [ - [ # First row - InlineKeyboardButton( # Generates a callback query when pressed - "Button", - callback_data=b"data" # Note how callback_data must be bytes - ), - InlineKeyboardButton( # Opens a web URL - "URL", - url="https://docs.pyrogram.ml" - ), - ], - [ # Second row - InlineKeyboardButton( # Opens the inline interface - "Choose chat", - switch_inline_query="pyrogram" - ), - InlineKeyboardButton( # Opens the inline interface in the current chat - "Inline here", - switch_inline_query_current_chat="pyrogram" - ) - ] - ] - ) - ) diff --git a/examples/callback_queries.py b/examples/callback_queries.py deleted file mode 100644 index f4a87b0041a..00000000000 --- a/examples/callback_queries.py +++ /dev/null @@ -1,16 +0,0 @@ -"""This example shows how to handle callback queries, i.e.: queries coming from inline button presses. - -It uses the @on_callback_query decorator to register a CallbackQueryHandler. -""" - -from pyrogram import Client - -app = Client("my_bot", bot_token="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11") - - -@app.on_callback_query() -def answer(client, callback_query): - callback_query.answer("Button contains: '{}'".format(callback_query.data), show_alert=True) - - -app.run() # Automatically start() and idle() diff --git a/examples/echobot.py b/examples/echobot.py deleted file mode 100644 index c60ae2917bf..00000000000 --- a/examples/echobot.py +++ /dev/null @@ -1,17 +0,0 @@ -"""This simple echo bot replies to every private text message. - -It uses the @on_message decorator to register a MessageHandler and applies two filters on it: -Filters.text and Filters.private to make sure it will reply to private text messages only. -""" - -from pyrogram import Client, Filters - -app = Client("my_account") - - -@app.on_message(Filters.text & Filters.private) -def echo(client, message): - message.reply(message.text) - - -app.run() # Automatically start() and idle() diff --git a/examples/get_chat_members.py b/examples/get_chat_members.py deleted file mode 100644 index 468ac7dec5c..00000000000 --- a/examples/get_chat_members.py +++ /dev/null @@ -1,10 +0,0 @@ -"""This example shows how to get all the members of a chat.""" - -from pyrogram import Client - -app = Client("my_account") -target = "pyrogramchat" # Target channel/supergroup - -with app: - for member in app.iter_chat_members(target): - print(member.user.first_name) diff --git a/examples/get_dialogs.py b/examples/get_dialogs.py deleted file mode 100644 index 92da8834c91..00000000000 --- a/examples/get_dialogs.py +++ /dev/null @@ -1,9 +0,0 @@ -"""This example shows how to get the full dialogs list (as user).""" - -from pyrogram import Client - -app = Client("my_account") - -with app: - for dialog in app.iter_dialogs(): - print(dialog.chat.title or dialog.chat.first_name) diff --git a/examples/get_history.py b/examples/get_history.py deleted file mode 100644 index e8bb14e31d8..00000000000 --- a/examples/get_history.py +++ /dev/null @@ -1,10 +0,0 @@ -"""This example shows how to get the full message history of a chat, starting from the latest message""" - -from pyrogram import Client - -app = Client("my_account") -target = "me" # "me" refers to your own chat (Saved Messages) - -with app: - for message in app.iter_history(target): - print(message.text) diff --git a/examples/hello_world.py b/examples/hello_world.py deleted file mode 100644 index 19d0ffe76fe..00000000000 --- a/examples/hello_world.py +++ /dev/null @@ -1,16 +0,0 @@ -"""This example demonstrates a basic API usage""" - -from pyrogram import Client - -# Create a new Client instance -app = Client("my_account") - -with app: - # Send a message, Markdown is enabled by default - app.send_message("me", "Hi there! I'm using **Pyrogram**") - - # Send a location - app.send_location("me", 51.500729, -0.124583) - - # Send a sticker - app.send_sticker("me", "CAADBAADyg4AAvLQYAEYD4F7vcZ43AI") diff --git a/examples/inline_queries.py b/examples/inline_queries.py deleted file mode 100644 index c1727fe6a91..00000000000 --- a/examples/inline_queries.py +++ /dev/null @@ -1,54 +0,0 @@ -"""This example shows how to handle inline queries. -Two results are generated when users invoke the bot inline mode, e.g.: @pyrogrambot hi. -It uses the @on_inline_query decorator to register an InlineQueryHandler. -""" - -from uuid import uuid4 - -from pyrogram import ( - Client, InlineQueryResultArticle, InputTextMessageContent, InlineKeyboardMarkup, InlineKeyboardButton -) - -app = Client("my_bot", bot_token="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11") - - -@app.on_inline_query() -def answer(client, inline_query): - inline_query.answer( - results=[ - InlineQueryResultArticle( - id=uuid4(), - title="Installation", - input_message_content=InputTextMessageContent( - "Here's how to install **Pyrogram**" - ), - url="https://docs.pyrogram.ml/start/Installation", - 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")] - ] - ) - ), - InlineQueryResultArticle( - id=uuid4(), - title="Usage", - input_message_content=InputTextMessageContent( - "Here's how to use **Pyrogram**" - ), - url="https://docs.pyrogram.ml/start/Usage", - 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")] - ] - ) - ) - ], - cache_time=1 - ) - - -app.run() # Automatically start() and idle() diff --git a/examples/raw_updates.py b/examples/raw_updates.py deleted file mode 100644 index 27d87eb37be..00000000000 --- a/examples/raw_updates.py +++ /dev/null @@ -1,13 +0,0 @@ -"""This example shows how to handle raw updates""" - -from pyrogram import Client - -app = Client("my_account") - - -@app.on_raw_update() -def raw(client, update, users, chats): - print(update) - - -app.run() # Automatically start() and idle() diff --git a/examples/use_inline_bots.py b/examples/use_inline_bots.py deleted file mode 100644 index 5681df8778d..00000000000 --- a/examples/use_inline_bots.py +++ /dev/null @@ -1,13 +0,0 @@ -"""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/welcomebot.py b/examples/welcomebot.py deleted file mode 100644 index ab252672017..00000000000 --- a/examples/welcomebot.py +++ /dev/null @@ -1,29 +0,0 @@ -"""This is the Welcome Bot in @PyrogramChat. - -It uses the Emoji module to easily add emojis in your text messages and Filters -to make it only work for specific messages in a specific chat. -""" - -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 - -app = Client("my_account") - - -# Filter in only new_chat_members updates generated in TARGET chat -@app.on_message(Filters.chat(TARGET) & Filters.new_chat_members) -def welcome(client, message): - # Build the new members list (with mentions) by using their first_name - new_members = [MENTION.format(i.first_name, i.id) for i in message.new_chat_members] - - # Build the welcome message by using an emoji and the list we built above - text = MESSAGE.format(Emoji.SPARKLES, ", ".join(new_members)) - - # Send the welcome message, without the web page preview - message.reply(text, disable_web_page_preview=True) - - -app.run() # Automatically start() and idle() diff --git a/pyrogram/__init__.py b/pyrogram/__init__.py index bf688797d0a..9a6a44accb8 100644 --- a/pyrogram/__init__.py +++ b/pyrogram/__init__.py @@ -1,36 +1,42 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2019 Dan Tès +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan # -# This file is part of Pyrogram. +# 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 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. +# 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 . +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . -import sys +__version__ = "2.0.106" +__license__ = "GNU Lesser General Public License v3.0 (LGPL-3.0)" +__copyright__ = "Copyright (C) 2017-present Dan " -if sys.version_info[:3] in [(3, 5, 0), (3, 5, 1), (3, 5, 2)]: - from .vendor import typing +from concurrent.futures.thread import ThreadPoolExecutor - # 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.13.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" -) +class StopTransmission(Exception): + pass -from .errors import RPCError -from .client import * -from .client.handlers import * -from .client.types import * + +class StopPropagation(StopAsyncIteration): + pass + + +class ContinuePropagation(StopAsyncIteration): + pass + + +from . import raw, types, filters, handlers, emoji, enums +from .client import Client +from .sync import idle, compose + +crypto_executor = ThreadPoolExecutor(1, thread_name_prefix="CryptoWorker") diff --git a/pyrogram/api/__init__.py b/pyrogram/api/__init__.py deleted file mode 100644 index e57f066155c..00000000000 --- a/pyrogram/api/__init__.py +++ /dev/null @@ -1,26 +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 importlib import import_module - -from .all import objects -from .core.object import Object - -for k, v in objects.items(): - path, name = v.rsplit(".", 1) - Object.all[k] = getattr(import_module(path), name) diff --git a/pyrogram/api/core/__init__.py b/pyrogram/api/core/__init__.py deleted file mode 100644 index daba6b7ce9b..00000000000 --- a/pyrogram/api/core/__init__.py +++ /dev/null @@ -1,28 +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 .future_salt import FutureSalt -from .future_salts import FutureSalts -from .gzip_packed import GzipPacked -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 -) diff --git a/pyrogram/api/core/future_salt.py b/pyrogram/api/core/future_salt.py deleted file mode 100644 index 4ee8197b8e8..00000000000 --- a/pyrogram/api/core/future_salt.py +++ /dev/null @@ -1,44 +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 datetime import datetime -from io import BytesIO - -from .object import Object -from .primitives import Int, Long - - -class FutureSalt(Object): - 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): - 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)) - 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 deleted file mode 100644 index cf6a9902548..00000000000 --- a/pyrogram/api/core/future_salts.py +++ /dev/null @@ -1,47 +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 datetime import datetime -from io import BytesIO - -from . import FutureSalt -from .object import Object -from .primitives import Int, Long - - -class FutureSalts(Object): - ID = 0xae500895 - - __slots__ = ["req_msg_id", "now", "salts"] - - QUALNAME = "FutureSalts" - - def __init__(self, req_msg_id: int, now: int or datetime, salts: list): - self.req_msg_id = req_msg_id - self.now = now - self.salts = salts - - @staticmethod - def read(b: BytesIO, *args) -> "FutureSalts": - req_msg_id = Long.read(b) - now = datetime.fromtimestamp(Int.read(b)) - - count = Int.read(b) - salts = [FutureSalt.read(b) for _ in range(count)] - - return FutureSalts(req_msg_id, now, salts) diff --git a/pyrogram/api/core/gzip_packed.py b/pyrogram/api/core/gzip_packed.py deleted file mode 100644 index 135c36bf095..00000000000 --- a/pyrogram/api/core/gzip_packed.py +++ /dev/null @@ -1,60 +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 gzip import compress, decompress -from io import BytesIO - -from .object import Object -from .primitives import Int, Bytes - - -class GzipPacked(Object): - ID = 0x3072cfa1 - - __slots__ = ["packed_data"] - - QUALNAME = "GzipPacked" - - def __init__(self, packed_data: Object): - 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( - BytesIO( - decompress( - Bytes.read(b) - ) - ) - ) - - def write(self) -> bytes: - b = BytesIO() - - b.write(Int(self.ID, False)) - - b.write( - Bytes( - compress( - self.packed_data.write() - ) - ) - ) - - return b.getvalue() diff --git a/pyrogram/api/core/message.py b/pyrogram/api/core/message.py deleted file mode 100644 index 5b2e5b6429c..00000000000 --- a/pyrogram/api/core/message.py +++ /dev/null @@ -1,55 +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 io import BytesIO - -from .object import Object -from .primitives import Int, Long - - -class Message(Object): - 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): - self.msg_id = msg_id - self.seq_no = seq_no - self.length = length - self.body = body - - @staticmethod - def read(b: BytesIO, *args) -> "Message": - msg_id = Long.read(b) - seq_no = Int.read(b) - length = Int.read(b) - body = b.read(length) - - return Message(Object.read(BytesIO(body)), msg_id, seq_no, length) - - def write(self) -> bytes: - b = BytesIO() - - b.write(Long(self.msg_id)) - b.write(Int(self.seq_no)) - b.write(Int(self.length)) - b.write(self.body.write()) - - return b.getvalue() diff --git a/pyrogram/api/core/msg_container.py b/pyrogram/api/core/msg_container.py deleted file mode 100644 index bfc413337ab..00000000000 --- a/pyrogram/api/core/msg_container.py +++ /dev/null @@ -1,52 +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 io import BytesIO - -from .message import Message -from .object import Object -from .primitives import Int - - -class MsgContainer(Object): - ID = 0x73f1f8dc - - __slots__ = ["messages"] - - QUALNAME = "MsgContainer" - - def __init__(self, messages: list): - self.messages = messages - - @staticmethod - def read(b: BytesIO, *args) -> "MsgContainer": - count = Int.read(b) - return MsgContainer([Message.read(b) for _ in range(count)]) - - def write(self) -> bytes: - b = BytesIO() - - b.write(Int(self.ID, False)) - - count = len(self.messages) - b.write(Int(count)) - - for message in self.messages: - b.write(message.write()) - - return b.getvalue() diff --git a/pyrogram/api/core/object.py b/pyrogram/api/core/object.py deleted file mode 100644 index a479fb6ec43..00000000000 --- 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/__init__.py b/pyrogram/api/core/primitives/__init__.py deleted file mode 100644 index 8885878bc85..00000000000 --- a/pyrogram/api/core/primitives/__init__.py +++ /dev/null @@ -1,25 +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 .bool import Bool, BoolTrue, BoolFalse -from .bytes import Bytes -from .double import Double -from .int import Int, Long, Int128, Int256 -from .null import Null -from .string import String -from .vector import Vector diff --git a/pyrogram/api/core/primitives/bool.py b/pyrogram/api/core/primitives/bool.py deleted file mode 100644 index 117ee7a4403..00000000000 --- a/pyrogram/api/core/primitives/bool.py +++ /dev/null @@ -1,47 +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 io import BytesIO - -from ..object import Object - - -class BoolFalse(Object): - ID = 0xbc799737 - value = False - - @classmethod - def read(cls, *args) -> bool: - return cls.value - - def __new__(cls) -> bytes: - return cls.ID.to_bytes(4, "little") - - -class BoolTrue(BoolFalse): - ID = 0x997275b5 - value = True - - -class Bool(Object): - @classmethod - def read(cls, b: BytesIO) -> bool: - return int.from_bytes(b.read(4), "little") == BoolTrue.ID - - def __new__(cls, value: bool) -> BoolTrue or BoolFalse: - return BoolTrue() if value else BoolFalse() diff --git a/pyrogram/api/core/primitives/bytes.py b/pyrogram/api/core/primitives/bytes.py deleted file mode 100644 index 8030b598eb3..00000000000 --- a/pyrogram/api/core/primitives/bytes.py +++ /dev/null @@ -1,54 +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 io import BytesIO - -from ..object import Object - - -class Bytes(Object): - @staticmethod - def read(b: BytesIO, *args) -> bytes: - length = int.from_bytes(b.read(1), "little") - - if length <= 253: - x = b.read(length) - b.read(-(length + 1) % 4) - else: - length = int.from_bytes(b.read(3), "little") - x = b.read(length) - b.read(-length % 4) - - return x - - def __new__(cls, value: bytes) -> bytes: - length = len(value) - - if length <= 253: - return ( - bytes([length]) - + value - + bytes(-(length + 1) % 4) - ) - else: - return ( - bytes([254]) - + length.to_bytes(3, "little") - + value - + bytes(-length % 4) - ) diff --git a/pyrogram/api/core/primitives/double.py b/pyrogram/api/core/primitives/double.py deleted file mode 100644 index 3dcaa461c69..00000000000 --- a/pyrogram/api/core/primitives/double.py +++ /dev/null @@ -1,31 +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 io import BytesIO -from struct import unpack, pack - -from ..object import Object - - -class Double(Object): - @staticmethod - def read(b: BytesIO, *args) -> float: - return unpack("d", b.read(8))[0] - - def __new__(cls, value: float) -> bytes: - return pack("d", value) diff --git a/pyrogram/api/core/primitives/int.py b/pyrogram/api/core/primitives/int.py deleted file mode 100644 index 7833a610b1e..00000000000 --- a/pyrogram/api/core/primitives/int.py +++ /dev/null @@ -1,44 +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 io import BytesIO - -from ..object import Object - - -class Int(Object): - SIZE = 4 - - @classmethod - def read(cls, b: BytesIO, signed: bool = True) -> int: - return int.from_bytes(b.read(cls.SIZE), "little", signed=signed) - - def __new__(cls, value: int, signed: bool = True) -> bytes: - return value.to_bytes(cls.SIZE, "little", signed=signed) - - -class Long(Int): - SIZE = 8 - - -class Int128(Int): - SIZE = 16 - - -class Int256(Int): - SIZE = 32 diff --git a/pyrogram/api/core/primitives/null.py b/pyrogram/api/core/primitives/null.py deleted file mode 100644 index d2d3b1c0454..00000000000 --- a/pyrogram/api/core/primitives/null.py +++ /dev/null @@ -1,32 +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 io import BytesIO - -from ..object import Object - - -class Null(Object): - ID = 0x56730bcc - - @staticmethod - def read(b: BytesIO, *args) -> None: - return None - - def __new__(cls) -> bytes: - return cls.ID.to_bytes(4, "little") diff --git a/pyrogram/api/core/primitives/string.py b/pyrogram/api/core/primitives/string.py deleted file mode 100644 index a271695a6f9..00000000000 --- a/pyrogram/api/core/primitives/string.py +++ /dev/null @@ -1,30 +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 io import BytesIO - -from . import Bytes - - -class String(Bytes): - @staticmethod - def read(b: BytesIO, *args) -> str: - return super(String, String).read(b).decode(errors="replace") - - def __new__(cls, value: str) -> bytes: - return super().__new__(cls, value.encode()) diff --git a/pyrogram/api/core/primitives/vector.py b/pyrogram/api/core/primitives/vector.py deleted file mode 100644 index cd24ec352d5..00000000000 --- a/pyrogram/api/core/primitives/vector.py +++ /dev/null @@ -1,54 +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 io import BytesIO - -from . import Int -from ..object import Object - - -class Vector(Object): - 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: - try: - return Object.read(b) - except KeyError: - b.seek(-4, 1) - return Int.read(b) - - @staticmethod - def read(b: BytesIO, t: Object = None) -> list: - return [ - t.read(b) if t - else Vector._read(b) - for _ in range(Int.read(b)) - ] - - def __new__(cls, value: list, t: Object = None) -> bytes: - return b"".join( - [Int(cls.ID, False), Int(len(value))] - + [ - t(i) if t - else i.write() - for i in value - ] - ) diff --git a/pyrogram/client.py b/pyrogram/client.py new file mode 100644 index 00000000000..c74634ea8e4 --- /dev/null +++ b/pyrogram/client.py @@ -0,0 +1,1048 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import asyncio +import functools +import inspect +import logging +import os +import platform +import re +import shutil +import sys +from concurrent.futures.thread import ThreadPoolExecutor +from datetime import datetime, timedelta +from hashlib import sha256 +from importlib import import_module +from io import StringIO, BytesIO +from mimetypes import MimeTypes +from pathlib import Path +from typing import Union, List, Optional, Callable, AsyncGenerator + +import pyrogram +from pyrogram import __version__, __license__ +from pyrogram import enums +from pyrogram import raw +from pyrogram import utils +from pyrogram.crypto import aes +from pyrogram.errors import CDNFileHashMismatch +from pyrogram.errors import ( + SessionPasswordNeeded, + VolumeLocNotFound, ChannelPrivate, + BadRequest +) +from pyrogram.handlers.handler import Handler +from pyrogram.methods import Methods +from pyrogram.session import Auth, Session +from pyrogram.storage import FileStorage, MemoryStorage +from pyrogram.types import User, TermsOfService +from pyrogram.utils import ainput +from .dispatcher import Dispatcher +from .file_id import FileId, FileType, ThumbnailSource +from .mime_types import mime_types +from .parser import Parser +from .session.internals import MsgId + +log = logging.getLogger(__name__) + + +class Client(Methods): + """Pyrogram Client, the main means for interacting with Telegram. + + Parameters: + name (``str``): + A name for the client, e.g.: "my_account". + + api_id (``int`` | ``str``, *optional*): + The *api_id* part of the Telegram API key, as integer or string. + E.g.: 12345 or "12345". + + api_hash (``str``, *optional*): + The *api_hash* part of the Telegram API key, as string. + E.g.: "0123456789abcdef0123456789abcdef". + + app_version (``str``, *optional*): + Application version. + Defaults to "Pyrogram x.y.z". + + device_model (``str``, *optional*): + Device model. + Defaults to *platform.python_implementation() + " " + platform.python_version()*. + + system_version (``str``, *optional*): + Operating System version. + Defaults to *platform.system() + " " + platform.release()*. + + lang_code (``str``, *optional*): + Code of the language used on the client, in ISO 639-1 standard. + Defaults to "en". + + ipv6 (``bool``, *optional*): + Pass True to connect to Telegram using IPv6. + Defaults to False (IPv4). + + proxy (``dict``, *optional*): + The Proxy settings as dict. + E.g.: *dict(scheme="socks5", hostname="11.22.33.44", port=1234, username="user", password="pass")*. + The *username* and *password* can be omitted if the proxy doesn't require authorization. + + test_mode (``bool``, *optional*): + Enable or disable login to the test servers. + Only applicable for new sessions and will be ignored in case previously created sessions are loaded. + Defaults to False. + + bot_token (``str``, *optional*): + Pass the Bot API token to create a bot session, e.g.: "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11" + Only applicable for new sessions. + + session_string (``str``, *optional*): + Pass a session string to load the session in-memory. + Implies ``in_memory=True``. + + in_memory (``bool``, *optional*): + Pass True to start an in-memory session that will be discarded as soon as the client stops. + In order to reconnect again using an in-memory session without having to login again, you can use + :meth:`~pyrogram.Client.export_session_string` before stopping the client to get a session string you can + pass to the ``session_string`` parameter. + Defaults to False. + + phone_number (``str``, *optional*): + Pass the phone number as string (with the Country Code prefix included) to avoid entering it manually. + Only applicable for new sessions. + + phone_code (``str``, *optional*): + Pass the phone code as string (for test numbers only) to avoid entering it manually. + Only applicable for new sessions. + + password (``str``, *optional*): + Pass the Two-Step Verification password as string (if required) to avoid entering it manually. + Only applicable for new sessions. + + workers (``int``, *optional*): + Number of maximum concurrent workers for handling incoming updates. + Defaults to ``min(32, os.cpu_count() + 4)``. + + workdir (``str``, *optional*): + Define a custom working directory. + The working directory is the location in the filesystem where Pyrogram will store the session files. + Defaults to the parent directory of the main script. + + plugins (``dict``, *optional*): + Smart Plugins settings as dict, e.g.: *dict(root="plugins")*. + + parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*): + Set the global parse mode of the client. By default, texts are parsed using both Markdown and HTML styles. + You can combine both syntaxes together. + + no_updates (``bool``, *optional*): + Pass True to disable incoming updates. + When updates are disabled the client can't receive messages or other updates. + Useful for batch programs that don't need to deal with updates. + Defaults to False (updates enabled and received). + + takeout (``bool``, *optional*): + Pass True to let the client use a takeout session instead of a normal one, implies *no_updates=True*. + Useful for exporting Telegram data. Methods invoked inside a takeout session (such as get_chat_history, + download_media, ...) are less prone to throw FloodWait exceptions. + Only available for users, bots will ignore this parameter. + Defaults to False (normal session). + + sleep_threshold (``int``, *optional*): + Set a sleep threshold for flood wait exceptions happening globally in this client instance, below which any + request that raises a flood wait will be automatically invoked again after sleeping for the required amount + of time. Flood wait exceptions requiring higher waiting times will be raised. + Defaults to 10 seconds. + + hide_password (``bool``, *optional*): + Pass True to hide the password when typing it during the login. + Defaults to False, because ``getpass`` (the library used) is known to be problematic in some + terminal environments. + + max_concurrent_transmissions (``bool``, *optional*): + Set the maximum amount of concurrent transmissions (uploads & downloads). + A value that is too high may result in network related issues. + Defaults to 1. + """ + + APP_VERSION = f"Pyrogram {__version__}" + DEVICE_MODEL = f"{platform.python_implementation()} {platform.python_version()}" + SYSTEM_VERSION = f"{platform.system()} {platform.release()}" + + 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-]+)$") + WORKERS = min(32, (os.cpu_count() or 0) + 4) # os.cpu_count() can be None + WORKDIR = PARENT_DIR + + # Interval of seconds in which the updates watchdog will kick in + UPDATES_WATCHDOG_INTERVAL = 5 * 60 + + MAX_CONCURRENT_TRANSMISSIONS = 1 + + mimetypes = MimeTypes() + mimetypes.readfp(StringIO(mime_types)) + + def __init__( + self, + name: str, + api_id: Union[int, str] = None, + api_hash: str = None, + app_version: str = APP_VERSION, + device_model: str = DEVICE_MODEL, + system_version: str = SYSTEM_VERSION, + lang_code: str = LANG_CODE, + ipv6: bool = False, + proxy: dict = None, + test_mode: bool = False, + bot_token: str = None, + session_string: str = None, + in_memory: bool = None, + phone_number: str = None, + phone_code: str = None, + password: str = None, + workers: int = WORKERS, + workdir: str = WORKDIR, + plugins: dict = None, + parse_mode: "enums.ParseMode" = enums.ParseMode.DEFAULT, + no_updates: bool = None, + takeout: bool = None, + sleep_threshold: int = Session.SLEEP_THRESHOLD, + hide_password: bool = False, + max_concurrent_transmissions: int = MAX_CONCURRENT_TRANSMISSIONS + ): + super().__init__() + + self.name = name + self.api_id = int(api_id) if api_id else None + self.api_hash = api_hash + self.app_version = app_version + self.device_model = device_model + self.system_version = system_version + self.lang_code = lang_code.lower() + self.ipv6 = ipv6 + self.proxy = proxy + self.test_mode = test_mode + self.bot_token = bot_token + self.session_string = session_string + self.in_memory = in_memory + self.phone_number = phone_number + self.phone_code = phone_code + self.password = password + self.workers = workers + self.workdir = Path(workdir) + self.plugins = plugins + self.parse_mode = parse_mode + self.no_updates = no_updates + self.takeout = takeout + self.sleep_threshold = sleep_threshold + self.hide_password = hide_password + self.max_concurrent_transmissions = max_concurrent_transmissions + + self.executor = ThreadPoolExecutor(self.workers, thread_name_prefix="Handler") + + if self.session_string: + self.storage = MemoryStorage(self.name, self.session_string) + elif self.in_memory: + self.storage = MemoryStorage(self.name) + else: + self.storage = FileStorage(self.name, self.workdir) + + self.dispatcher = Dispatcher(self) + + self.rnd_id = MsgId + + self.parser = Parser(self) + + self.session = None + + self.media_sessions = {} + self.media_sessions_lock = asyncio.Lock() + + self.save_file_semaphore = asyncio.Semaphore(self.max_concurrent_transmissions) + self.get_file_semaphore = asyncio.Semaphore(self.max_concurrent_transmissions) + + self.is_connected = None + self.is_initialized = None + + self.takeout_id = None + + self.disconnect_handler = None + + self.me: Optional[User] = None + + self.message_cache = Cache(10000) + + # Sometimes, for some reason, the server will stop sending updates and will only respond to pings. + # This watchdog will invoke updates.GetState in order to wake up the server and enable it sending updates again + # after some idle time has been detected. + self.updates_watchdog_task = None + self.updates_watchdog_event = asyncio.Event() + self.last_update_time = datetime.now() + + self.loop = asyncio.get_event_loop() + + def __enter__(self): + return self.start() + + def __exit__(self, *args): + try: + self.stop() + except ConnectionError: + pass + + async def __aenter__(self): + return await self.start() + + async def __aexit__(self, *args): + try: + await self.stop() + except ConnectionError: + pass + + async def updates_watchdog(self): + while True: + try: + await asyncio.wait_for(self.updates_watchdog_event.wait(), self.UPDATES_WATCHDOG_INTERVAL) + except asyncio.TimeoutError: + pass + else: + break + + if datetime.now() - self.last_update_time > timedelta(seconds=self.UPDATES_WATCHDOG_INTERVAL): + await self.invoke(raw.functions.updates.GetState()) + + async def authorize(self) -> User: + if self.bot_token: + return await self.sign_in_bot(self.bot_token) + + print(f"Welcome to Pyrogram (version {__version__})") + print(f"Pyrogram is free software and comes with ABSOLUTELY NO WARRANTY. Licensed\n" + f"under the terms of the {__license__}.\n") + + while True: + try: + if not self.phone_number: + while True: + value = await ainput("Enter phone number or bot token: ") + + if not value: + continue + + confirm = (await ainput(f'Is "{value}" correct? (y/N): ')).lower() + + if confirm == "y": + break + + if ":" in value: + self.bot_token = value + return await self.sign_in_bot(value) + else: + self.phone_number = value + + sent_code = await self.send_code(self.phone_number) + except BadRequest as e: + print(e.MESSAGE) + self.phone_number = None + self.bot_token = None + else: + break + + sent_code_descriptions = { + enums.SentCodeType.APP: "Telegram app", + enums.SentCodeType.SMS: "SMS", + enums.SentCodeType.CALL: "phone call", + enums.SentCodeType.FLASH_CALL: "phone flash call", + enums.SentCodeType.FRAGMENT_SMS: "Fragment SMS", + enums.SentCodeType.EMAIL_CODE: "email code" + } + + print(f"The confirmation code has been sent via {sent_code_descriptions[sent_code.type]}") + + while True: + if not self.phone_code: + self.phone_code = await ainput("Enter confirmation code: ") + + try: + signed_in = await self.sign_in(self.phone_number, sent_code.phone_code_hash, self.phone_code) + except BadRequest as e: + print(e.MESSAGE) + self.phone_code = None + except SessionPasswordNeeded as e: + print(e.MESSAGE) + + while True: + print("Password hint: {}".format(await self.get_password_hint())) + + if not self.password: + self.password = await ainput("Enter password (empty to recover): ", hide=self.hide_password) + + try: + if not self.password: + confirm = await ainput("Confirm password recovery (y/n): ") + + if confirm == "y": + email_pattern = await self.send_recovery_code() + print(f"The recovery code has been sent to {email_pattern}") + + while True: + recovery_code = await ainput("Enter recovery code: ") + + try: + return await self.recover_password(recovery_code) + except BadRequest as e: + print(e.MESSAGE) + except Exception as e: + log.exception(e) + raise + else: + self.password = None + else: + return await self.check_password(self.password) + except BadRequest as e: + print(e.MESSAGE) + self.password = None + else: + break + + if isinstance(signed_in, User): + return signed_in + + while True: + first_name = await ainput("Enter first name: ") + last_name = await ainput("Enter last name (empty to skip): ") + + try: + signed_up = await self.sign_up( + self.phone_number, + sent_code.phone_code_hash, + first_name, + last_name + ) + except BadRequest as e: + print(e.MESSAGE) + else: + break + + if isinstance(signed_in, TermsOfService): + print("\n" + signed_in.text + "\n") + await self.accept_terms_of_service(signed_in.id) + + return signed_up + + def set_parse_mode(self, parse_mode: Optional["enums.ParseMode"]): + """Set the parse mode to be used globally by the client. + + When setting the parse mode with this method, all other methods having a *parse_mode* parameter will follow the + global value by default. + + Parameters: + parse_mode (:obj:`~pyrogram.enums.ParseMode`): + By default, texts are parsed using both Markdown and HTML styles. + You can combine both syntaxes together. + + Example: + .. code-block:: python + + from pyrogram import enums + + # Default combined mode: Markdown + HTML + await app.send_message("me", "1. **markdown** and html") + + # Force Markdown-only, HTML is disabled + app.set_parse_mode(enums.ParseMode.MARKDOWN) + await app.send_message("me", "2. **markdown** and html") + + # Force HTML-only, Markdown is disabled + app.set_parse_mode(enums.ParseMode.HTML) + await app.send_message("me", "3. **markdown** and html") + + # Disable the parser completely + app.set_parse_mode(enums.ParseMode.DISABLED) + await app.send_message("me", "4. **markdown** and html") + + # Bring back the default combined mode + app.set_parse_mode(enums.ParseMode.DEFAULT) + await app.send_message("me", "5. **markdown** and html") + """ + + self.parse_mode = parse_mode + + async def fetch_peers(self, peers: List[Union[raw.types.User, raw.types.Chat, raw.types.Channel]]) -> bool: + is_min = False + parsed_peers = [] + + for peer in peers: + if getattr(peer, "min", False): + is_min = True + continue + + username = None + phone_number = None + + if isinstance(peer, raw.types.User): + peer_id = peer.id + access_hash = peer.access_hash + username = ( + peer.username.lower() if peer.username + else peer.usernames[0].username.lower() if peer.usernames + else None + ) + phone_number = peer.phone + peer_type = "bot" if peer.bot else "user" + elif isinstance(peer, (raw.types.Chat, raw.types.ChatForbidden)): + peer_id = -peer.id + access_hash = 0 + peer_type = "group" + elif isinstance(peer, raw.types.Channel): + peer_id = utils.get_channel_id(peer.id) + access_hash = peer.access_hash + username = ( + peer.username.lower() if peer.username + else peer.usernames[0].username.lower() if peer.usernames + else None + ) + peer_type = "channel" if peer.broadcast else "supergroup" + elif isinstance(peer, raw.types.ChannelForbidden): + peer_id = utils.get_channel_id(peer.id) + access_hash = peer.access_hash + peer_type = "channel" if peer.broadcast else "supergroup" + else: + continue + + parsed_peers.append((peer_id, access_hash, peer_type, username, phone_number)) + + await self.storage.update_peers(parsed_peers) + + return is_min + + async def handle_updates(self, updates): + self.last_update_time = datetime.now() + + if isinstance(updates, (raw.types.Updates, raw.types.UpdatesCombined)): + is_min = any(( + await self.fetch_peers(updates.users), + await 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( + getattr( + getattr( + update, "message", None + ), "peer_id", None + ), "channel_id", None + ) or getattr(update, "channel_id", None) + + pts = getattr(update, "pts", None) + pts_count = getattr(update, "pts_count", None) + + if isinstance(update, raw.types.UpdateChannelTooLong): + log.info(update) + + if isinstance(update, raw.types.UpdateNewChannelMessage) and is_min: + message = update.message + + if not isinstance(message, raw.types.MessageEmpty): + try: + diff = await self.invoke( + raw.functions.updates.GetChannelDifference( + channel=await self.resolve_peer(utils.get_channel_id(channel_id)), + filter=raw.types.ChannelMessagesFilter( + ranges=[raw.types.MessageRange( + min_id=update.message.id, + max_id=update.message.id + )] + ), + pts=pts - pts_count, + limit=pts + ) + ) + except ChannelPrivate: + pass + else: + if not isinstance(diff, raw.types.updates.ChannelDifferenceEmpty): + users.update({u.id: u for u in diff.users}) + chats.update({c.id: c for c in diff.chats}) + + self.dispatcher.updates_queue.put_nowait((update, users, chats)) + elif isinstance(updates, (raw.types.UpdateShortMessage, raw.types.UpdateShortChatMessage)): + diff = await self.invoke( + raw.functions.updates.GetDifference( + pts=updates.pts - updates.pts_count, + date=updates.date, + qts=-1 + ) + ) + + if diff.new_messages: + self.dispatcher.updates_queue.put_nowait(( + raw.types.UpdateNewMessage( + message=diff.new_messages[0], + pts=updates.pts, + pts_count=updates.pts_count + ), + {u.id: u for u in diff.users}, + {c.id: c for c in diff.chats} + )) + else: + if diff.other_updates: # The other_updates list can be empty + self.dispatcher.updates_queue.put_nowait((diff.other_updates[0], {}, {})) + elif isinstance(updates, raw.types.UpdateShort): + self.dispatcher.updates_queue.put_nowait((updates.update, {}, {})) + elif isinstance(updates, raw.types.UpdatesTooLong): + log.info(updates) + + async def load_session(self): + await self.storage.open() + + session_empty = any([ + await self.storage.test_mode() is None, + await self.storage.auth_key() is None, + await self.storage.user_id() is None, + await self.storage.is_bot() is None + ]) + + if session_empty: + if not self.api_id or not self.api_hash: + raise AttributeError("The API key is required for new authorizations. " + "More info: https://docs.pyrogram.org/start/auth") + + await self.storage.api_id(self.api_id) + + await self.storage.dc_id(2) + await self.storage.date(0) + + await self.storage.test_mode(self.test_mode) + await self.storage.auth_key( + await Auth( + self, await self.storage.dc_id(), + await self.storage.test_mode() + ).create() + ) + await self.storage.user_id(None) + await self.storage.is_bot(None) + else: + # Needed for migration from storage v2 to v3 + if not await self.storage.api_id(): + if self.api_id: + await self.storage.api_id(self.api_id) + else: + while True: + try: + value = int(await ainput("Enter the api_id part of the API key: ")) + + if value <= 0: + print("Invalid value") + continue + + confirm = (await ainput(f'Is "{value}" correct? (y/N): ')).lower() + + if confirm == "y": + await self.storage.api_id(value) + break + except Exception as e: + print(e) + + def load_plugins(self): + if self.plugins: + plugins = self.plugins.copy() + + for option in ["include", "exclude"]: + if plugins.get(option, []): + plugins[option] = [ + (i.split()[0], i.split()[1:] or None) + for i in self.plugins[option] + ] + else: + return + + if plugins.get("enabled", True): + root = plugins["root"] + include = plugins.get("include", []) + exclude = plugins.get("exclude", []) + + count = 0 + + if not include: + for path in sorted(Path(root.replace(".", "/")).rglob("*.py")): + module_path = '.'.join(path.parent.parts + (path.stem,)) + module = import_module(module_path) + + for name in vars(module).keys(): + # noinspection PyBroadException + try: + for handler, group in getattr(module, name).handlers: + if isinstance(handler, Handler) and isinstance(group, int): + self.add_handler(handler, group) + + log.info('[{}] [LOAD] {}("{}") in group {} from "{}"'.format( + self.name, type(handler).__name__, name, group, module_path)) + + count += 1 + except Exception: + pass + else: + for path, handlers in include: + module_path = root + "." + path + warn_non_existent_functions = True + + try: + module = import_module(module_path) + except ImportError: + log.warning('[%s] [LOAD] Ignoring non-existent module "%s"', self.name, module_path) + continue + + if "__path__" in dir(module): + log.warning('[%s] [LOAD] Ignoring namespace "%s"', self.name, module_path) + continue + + if handlers is None: + handlers = vars(module).keys() + warn_non_existent_functions = False + + for name in handlers: + # noinspection PyBroadException + try: + for handler, group in getattr(module, name).handlers: + if isinstance(handler, Handler) and isinstance(group, int): + self.add_handler(handler, group) + + log.info('[{}] [LOAD] {}("{}") in group {} from "{}"'.format( + self.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( + self.name, name, module_path)) + + if exclude: + for path, handlers in exclude: + module_path = root + "." + path + warn_non_existent_functions = True + + try: + module = import_module(module_path) + except ImportError: + log.warning('[%s] [UNLOAD] Ignoring non-existent module "%s"', self.name, module_path) + continue + + if "__path__" in dir(module): + log.warning('[%s] [UNLOAD] Ignoring namespace "%s"', self.name, module_path) + continue + + if handlers is None: + handlers = vars(module).keys() + warn_non_existent_functions = False + + for name in handlers: + # noinspection PyBroadException + try: + for handler, group in getattr(module, name).handlers: + if isinstance(handler, Handler) and isinstance(group, int): + self.remove_handler(handler, group) + + log.info('[{}] [UNLOAD] {}("{}") from group {} in "{}"'.format( + self.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( + self.name, name, module_path)) + + if count > 0: + log.info('[{}] Successfully loaded {} plugin{} from "{}"'.format( + self.name, count, "s" if count > 1 else "", root)) + else: + log.warning('[%s] No plugin loaded from "%s"', self.name, root) + + async def handle_download(self, packet): + file_id, directory, file_name, in_memory, file_size, progress, progress_args = packet + + os.makedirs(directory, exist_ok=True) if not in_memory else None + temp_file_path = os.path.abspath(re.sub("\\\\", "/", os.path.join(directory, file_name))) + ".temp" + file = BytesIO() if in_memory else open(temp_file_path, "wb") + + try: + async for chunk in self.get_file(file_id, file_size, 0, 0, progress, progress_args): + file.write(chunk) + except BaseException as e: + if not in_memory: + file.close() + os.remove(temp_file_path) + + if isinstance(e, asyncio.CancelledError): + raise e + + return None + else: + if in_memory: + file.name = file_name + return file + else: + file.close() + file_path = os.path.splitext(temp_file_path)[0] + shutil.move(temp_file_path, file_path) + return file_path + + async def get_file( + self, + file_id: FileId, + file_size: int = 0, + limit: int = 0, + offset: int = 0, + progress: Callable = None, + progress_args: tuple = () + ) -> Optional[AsyncGenerator[bytes, None]]: + async with self.get_file_semaphore: + file_type = file_id.file_type + + if file_type == FileType.CHAT_PHOTO: + if file_id.chat_id > 0: + peer = raw.types.InputPeerUser( + user_id=file_id.chat_id, + access_hash=file_id.chat_access_hash + ) + else: + if file_id.chat_access_hash == 0: + peer = raw.types.InputPeerChat( + chat_id=-file_id.chat_id + ) + else: + peer = raw.types.InputPeerChannel( + channel_id=utils.get_channel_id(file_id.chat_id), + access_hash=file_id.chat_access_hash + ) + + location = raw.types.InputPeerPhotoFileLocation( + peer=peer, + photo_id=file_id.media_id, + big=file_id.thumbnail_source == ThumbnailSource.CHAT_PHOTO_BIG + ) + elif file_type == FileType.PHOTO: + location = raw.types.InputPhotoFileLocation( + id=file_id.media_id, + access_hash=file_id.access_hash, + file_reference=file_id.file_reference, + thumb_size=file_id.thumbnail_size + ) + else: + location = raw.types.InputDocumentFileLocation( + id=file_id.media_id, + access_hash=file_id.access_hash, + file_reference=file_id.file_reference, + thumb_size=file_id.thumbnail_size + ) + + current = 0 + total = abs(limit) or (1 << 31) - 1 + chunk_size = 1024 * 1024 + offset_bytes = abs(offset) * chunk_size + + dc_id = file_id.dc_id + + session = Session( + self, dc_id, + await Auth(self, dc_id, await self.storage.test_mode()).create() + if dc_id != await self.storage.dc_id() + else await self.storage.auth_key(), + await self.storage.test_mode(), + is_media=True + ) + + try: + await session.start() + + if dc_id != await self.storage.dc_id(): + exported_auth = await self.invoke( + raw.functions.auth.ExportAuthorization( + dc_id=dc_id + ) + ) + + await session.invoke( + raw.functions.auth.ImportAuthorization( + id=exported_auth.id, + bytes=exported_auth.bytes + ) + ) + + r = await session.invoke( + raw.functions.upload.GetFile( + location=location, + offset=offset_bytes, + limit=chunk_size + ), + sleep_threshold=30 + ) + + if isinstance(r, raw.types.upload.File): + while True: + chunk = r.bytes + + yield chunk + + current += 1 + offset_bytes += chunk_size + + if progress: + func = functools.partial( + progress, + min(offset_bytes, file_size) + if file_size != 0 + else offset_bytes, + file_size, + *progress_args + ) + + if inspect.iscoroutinefunction(progress): + await func() + else: + await self.loop.run_in_executor(self.executor, func) + + if len(chunk) < chunk_size or current >= total: + break + + r = await session.invoke( + raw.functions.upload.GetFile( + location=location, + offset=offset_bytes, + limit=chunk_size + ), + sleep_threshold=30 + ) + + elif isinstance(r, raw.types.upload.FileCdnRedirect): + cdn_session = Session( + self, r.dc_id, await Auth(self, r.dc_id, await self.storage.test_mode()).create(), + await self.storage.test_mode(), is_media=True, is_cdn=True + ) + + try: + await cdn_session.start() + + while True: + r2 = await cdn_session.invoke( + raw.functions.upload.GetCdnFile( + file_token=r.file_token, + offset=offset_bytes, + limit=chunk_size + ) + ) + + if isinstance(r2, raw.types.upload.CdnFileReuploadNeeded): + try: + await session.invoke( + raw.functions.upload.ReuploadCdnFile( + file_token=r.file_token, + request_token=r2.request_token + ) + ) + except VolumeLocNotFound: + break + else: + continue + + chunk = r2.bytes + + # https://core.telegram.org/cdn#decrypting-files + decrypted_chunk = aes.ctr256_decrypt( + chunk, + r.encryption_key, + bytearray( + r.encryption_iv[:-4] + + (offset_bytes // 16).to_bytes(4, "big") + ) + ) + + hashes = await session.invoke( + raw.functions.upload.GetCdnFileHashes( + file_token=r.file_token, + offset=offset_bytes + ) + ) + + # https://core.telegram.org/cdn#verifying-files + for i, h in enumerate(hashes): + cdn_chunk = decrypted_chunk[h.limit * i: h.limit * (i + 1)] + CDNFileHashMismatch.check( + h.hash == sha256(cdn_chunk).digest(), + "h.hash == sha256(cdn_chunk).digest()" + ) + + yield decrypted_chunk + + current += 1 + offset_bytes += chunk_size + + if progress: + func = functools.partial( + progress, + min(offset_bytes, file_size) if file_size != 0 else offset_bytes, + file_size, + *progress_args + ) + + if inspect.iscoroutinefunction(progress): + await func() + else: + await self.loop.run_in_executor(self.executor, func) + + if len(chunk) < chunk_size or current >= total: + break + except Exception as e: + raise e + finally: + await cdn_session.stop() + except pyrogram.StopTransmission: + raise + except Exception as e: + log.exception(e) + finally: + await session.stop() + + def guess_mime_type(self, filename: str) -> Optional[str]: + return self.mimetypes.guess_type(filename)[0] + + def guess_extension(self, mime_type: str) -> Optional[str]: + return self.mimetypes.guess_extension(mime_type) + + +class Cache: + def __init__(self, capacity: int): + self.capacity = capacity + self.store = {} + + def __getitem__(self, key): + return self.store.get(key, None) + + def __setitem__(self, key, value): + if key in self.store: + del self.store[key] + + self.store[key] = value + + if len(self.store) > self.capacity: + for _ in range(self.capacity // 2 + 1): + del self.store[next(iter(self.store))] diff --git a/pyrogram/client/__init__.py b/pyrogram/client/__init__.py deleted file mode 100644 index f4a954c6fae..00000000000 --- a/pyrogram/client/__init__.py +++ /dev/null @@ -1,25 +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 .client import Client -from .ext import BaseClient, Emoji -from .filters import Filters - -__all__ = [ - "Client", "BaseClient", "Emoji", "Filters", -] diff --git a/pyrogram/client/client.py b/pyrogram/client/client.py deleted file mode 100644 index 823faf279f5..00000000000 --- a/pyrogram/client/client.py +++ /dev/null @@ -1,1757 +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 . - -import base64 -import binascii -import json -import logging -import math -import mimetypes -import os -import re -import shutil -import struct -import tempfile -import threading -import time -from configparser import ConfigParser -from datetime import datetime -from hashlib import sha256, md5 -from importlib import import_module -from pathlib import Path -from signal import signal, SIGINT, SIGTERM, SIGABRT -from threading import Thread -from typing import Union, List - -from pyrogram.api import functions, types -from pyrogram.api.core import Object -from pyrogram.client.handlers import DisconnectHandler -from pyrogram.client.handlers.handler import Handler -from pyrogram.client.methods.password.utils import compute_check -from pyrogram.crypto import AES -from pyrogram.errors import ( - PhoneMigrate, NetworkMigrate, PhoneNumberInvalid, - PhoneNumberUnoccupied, PhoneCodeInvalid, PhoneCodeHashEmpty, - PhoneCodeExpired, PhoneCodeEmpty, SessionPasswordNeeded, - PasswordHashInvalid, FloodWait, PeerIdInvalid, FirstnameInvalid, PhoneNumberBanned, - VolumeLocNotFound, UserMigrate, FileIdInvalid, ChannelPrivate, PhoneNumberOccupied, - PasswordRecoveryNa, PasswordEmpty -) -from pyrogram.session import Auth, Session -from .ext import utils, Syncer, BaseClient, Dispatcher -from .methods import Methods - -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. - - 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" - 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" - This is an alternative way to set it if you don't want to use the *config.ini* file. - - device_model (``str``, *optional*): - Device model. Defaults to *platform.python_implementation() + " " + platform.python_version()* - This is an alternative way to set it if you don't want to use the *config.ini* file. - - system_version (``str``, *optional*): - Operating System version. Defaults to *platform.system() + " " + platform.release()* - This is an alternative way to set it if you don't want to use the *config.ini* file. - - lang_code (``str``, *optional*): - Code of the language used on the client, in ISO 639-1 standard. Defaults to "en". - This is an alternative way to set it if you don't want to use the *config.ini* file. - - ipv6 (``bool``, *optional*): - Pass True to connect to Telegram using IPv6. - Defaults to False (IPv4). - - proxy (``dict``, *optional*): - Your SOCKS5 Proxy settings as dict, - e.g.: *dict(hostname="11.22.33.44", port=1080, username="user", password="pass")*. - *username* and *password* can be omitted if your proxy doesn't require authorization. - 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. - Only applicable for new sessions and will be ignored in case previously - created sessions are loaded. - - 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 - (e.g., "391234567890"). - Only applicable for new sessions. - - phone_code (``str`` | ``callable``, *optional*): - Pass the phone code as string (for test numbers only) to avoid entering it manually. Or pass a callback - function which accepts a single positional argument *(phone_number)* and must return the correct phone code - as string (e.g., "12345"). - Only applicable for new sessions. - - password (``str``, *optional*): - Pass your Two-Step Verification password as string (if you have one) to avoid entering it manually. - Or pass a callback function which accepts a single positional argument *(password_hint)* and must return - the correct password as string (e.g., "password"). - Only applicable for new sessions. - - recovery_code (``callable``, *optional*): - Pass a callback function which accepts a single positional argument *(email_pattern)* and must return the - correct password recovery code as string (e.g., "987654"). - Only applicable for new sessions. - - force_sms (``str``, *optional*): - Pass True to force Telegram sending the authorization code via SMS. - Only applicable for new sessions. - - first_name (``str``, *optional*): - Pass a First Name as string to avoid entering it manually. Or pass a callback function which accepts no - arguments and must return the correct name as string (e.g., "Dan"). It will be used to automatically create - 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. - - workers (``int``, *optional*): - Thread pool size for handling incoming updates. Defaults to 4. - - workdir (``str``, *optional*): - Define a custom working directory. The working directory is the location in your filesystem - where Pyrogram will store your session files. Defaults to "." (current directory). - - config_file (``str``, *optional*): - Path of the configuration file. Defaults to ./config.ini - - plugins (``dict``, *optional*): - Your Smart Plugins settings as dict, e.g.: *dict(root="plugins")*. - This is an alternative way to setup plugins if you don't want to use the *config.ini* file. - - no_updates (``bool``, *optional*): - Pass True to completely disable incoming updates for the current session. - When updates are disabled your client can't receive any new message. - Useful for batch programs that don't need to deal with updates. - 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. - 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. - Defaults to False (normal session). - """ - - terms_of_service_displayed = False - - def __init__( - self, - session_name: str, - api_id: Union[int, str] = None, - api_hash: str = None, - app_version: str = None, - device_model: str = None, - system_version: str = None, - lang_code: str = None, - ipv6: bool = False, - proxy: dict = None, - test_mode: bool = False, - 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, - workdir: str = BaseClient.WORKDIR, - config_file: str = BaseClient.CONFIG_FILE, - plugins: dict = None, - no_updates: bool = None, - takeout: bool = None - ): - super().__init__() - - self.session_name = session_name - self.api_id = int(api_id) if api_id else None - self.api_hash = api_hash - self.app_version = app_version - self.device_model = device_model - self.system_version = system_version - self.lang_code = lang_code - self.ipv6 = ipv6 - # TODO: Make code consistent, use underscore for private/protected fields - self._proxy = proxy - self.test_mode = test_mode - 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 - self.workdir = workdir - self.config_file = config_file - self.plugins = plugins - self.no_updates = no_updates - self.takeout = takeout - - self.dispatcher = Dispatcher(self, workers) - - def __enter__(self): - return self.start() - - def __exit__(self, *args): - self.stop() - - @property - def proxy(self): - return self._proxy - - @proxy.setter - def proxy(self, value): - if value is None: - self._proxy = None - return - - if self._proxy is None: - self._proxy = {} - - self._proxy["enabled"] = bool(value.get("enabled", True)) - self._proxy.update(value) - - def start(self): - """Use this method to start the Client. - - Raises: - 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") - - if self.BOT_TOKEN_RE.match(self.session_name): - self.is_bot = True - self.bot_token = self.session_name - self.session_name = self.session_name.split(":")[0] - log.warning('\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') - - self.load_config() - self.load_session() - self.load_plugins() - - self.session = Session( - self, - self.dc_id, - self.auth_key - ) - - self.session.start() - self.is_started = True - - try: - if self.user_id is None: - if self.bot_token is None: - self.is_bot = False - self.authorize_user() - else: - self.is_bot = True - self.authorize_bot() - - self.save_session() - - if not self.is_bot: - if self.takeout: - self.takeout_id = self.send(functions.account.InitTakeoutSession()).id - log.warning("Takeout session {} initiated".format(self.takeout_id)) - - now = time.time() - - if abs(now - self.date) > Client.OFFLINE_SLEEP: - self.peers_by_username = {} - self.peers_by_phone = {} - - self.get_initial_dialogs() - self.get_contacts() - else: - self.send(functions.messages.GetPinnedDialogs()) - self.get_initial_dialogs_chunk() - else: - self.send(functions.updates.GetState()) - except Exception as e: - self.is_started = False - self.session.stop() - raise e - - for i in range(self.UPDATES_WORKERS): - self.updates_workers_list.append( - Thread( - target=self.updates_worker, - name="UpdatesWorker#{}".format(i + 1) - ) - ) - - self.updates_workers_list[-1].start() - - for i in range(self.DOWNLOAD_WORKERS): - self.download_workers_list.append( - Thread( - target=self.download_worker, - name="DownloadWorker#{}".format(i + 1) - ) - ) - - self.download_workers_list[-1].start() - - self.dispatcher.start() - - mimetypes.init() - Syncer.add(self) - - return self - - def stop(self): - """Use this method to stop the Client. - - Raises: - ConnectionError: In case you try to stop an already stopped Client. - """ - if not self.is_started: - raise ConnectionError("Client is already stopped") - - if self.takeout_id: - self.send(functions.account.FinishTakeoutSession()) - log.warning("Takeout session {} finished".format(self.takeout_id)) - - Syncer.remove(self) - self.dispatcher.stop() - - for _ in range(self.DOWNLOAD_WORKERS): - self.download_queue.put(None) - - for i in self.download_workers_list: - i.join() - - self.download_workers_list.clear() - - for _ in range(self.UPDATES_WORKERS): - self.updates_queue.put(None) - - for i in self.updates_workers_list: - i.join() - - self.updates_workers_list.clear() - - for i in self.media_sessions.values(): - i.stop() - - self.media_sessions.clear() - - self.is_started = False - self.session.stop() - - return self - - def restart(self): - """Use this method to restart the Client. - - Raises: - ConnectionError: In case you try to restart a stopped Client. - """ - self.stop() - self.start() - - def idle(self, stop_signals: tuple = (SIGINT, SIGTERM, SIGABRT)): - """Use this method to 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. - - 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 - - for s in stop_signals: - signal(s, signal_handler) - - self.is_idle = True - - while self.is_idle: - time.sleep(1) - - self.stop() - - def run(self): - """Use this method as a convenience shortcut to automatically start the Client and idle the main script. - - This is a convenience method that literally just calls :meth:`start` and :meth:`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:`idle` will block. - - Raises: - 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. - - 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). - - Parameters: - handler (``Handler``): - The handler to be registered. - - group (``int``, *optional*): - The group identifier, defaults to 0. - - Returns: - ``tuple``: A tuple consisting of (handler, group). - """ - if isinstance(handler, DisconnectHandler): - self.disconnect_handler = handler.callback - else: - self.dispatcher.add_handler(handler, group) - - return handler, group - - def remove_handler(self, handler: Handler, group: int = 0): - """Use this method to 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 - pass it directly. - - Parameters: - handler (``Handler``): - The handler to be removed. - - group (``int``, *optional*): - The group identifier, defaults to 0. - """ - if isinstance(handler, DisconnectHandler): - self.disconnect_handler = None - else: - self.dispatcher.remove_handler(handler, group) - - def stop_transmission(self): - """Use this method to stop downloading or uploading a file. - Must be called inside a progress callback function. - """ - raise Client.StopTransmission - - def authorize_bot(self): - try: - r = self.send( - functions.auth.ImportBotAuthorization( - flags=0, - api_id=self.api_id, - api_hash=self.api_hash, - bot_auth_token=self.bot_token - ) - ) - except UserMigrate as e: - self.session.stop() - - self.dc_id = e.x - self.auth_key = Auth(self.dc_id, self.test_mode, self.ipv6, self._proxy).create() - - self.session = Session( - self, - self.dc_id, - self.auth_key - ) - - self.session.start() - self.authorize_bot() - else: - self.user_id = r.user.id - - print("Logged in successfully as @{}".format(r.user.username)) - - def authorize_user(self): - phone_number_invalid_raises = self.phone_number is not None - phone_code_invalid_raises = self.phone_code is not None - password_invalid_raises = self.password is not None - first_name_invalid_raises = self.first_name is not None - - def default_phone_number_callback(): - while True: - phone_number = input("Enter phone number: ") - confirm = input("Is \"{}\" correct? (y/n): ".format(phone_number)) - - if confirm in ("y", "1"): - return phone_number - elif confirm in ("n", "2"): - continue - - while True: - self.phone_number = ( - default_phone_number_callback() if self.phone_number is None - else str(self.phone_number()) if callable(self.phone_number) - else str(self.phone_number) - ) - - self.phone_number = self.phone_number.strip("+") - - try: - r = self.send( - functions.auth.SendCode( - phone_number=self.phone_number, - api_id=self.api_id, - api_hash=self.api_hash, - settings=types.CodeSettings() - ) - ) - except (PhoneMigrate, NetworkMigrate) as e: - self.session.stop() - - self.dc_id = e.x - - self.auth_key = Auth( - self.dc_id, - self.test_mode, - self.ipv6, - self._proxy - ).create() - - self.session = Session( - self, - self.dc_id, - self.auth_key - ) - - self.session.start() - except (PhoneNumberInvalid, PhoneNumberBanned) as e: - if phone_number_invalid_raises: - raise - else: - print(e.MESSAGE) - self.phone_number = None - except FloodWait as e: - if phone_number_invalid_raises: - raise - else: - print(e.MESSAGE.format(x=e.x)) - time.sleep(e.x) - except Exception as e: - log.error(e, exc_info=True) - raise - else: - break - - phone_registered = r.phone_registered - phone_code_hash = r.phone_code_hash - terms_of_service = r.terms_of_service - - if terms_of_service and not Client.terms_of_service_displayed: - print("\n" + terms_of_service.text + "\n") - Client.terms_of_service_displayed = True - - if self.force_sms: - self.send( - functions.auth.ResendCode( - phone_number=self.phone_number, - phone_code_hash=phone_code_hash - ) - ) - - while True: - if not phone_registered: - self.first_name = ( - input("First name: ") if self.first_name is None - else str(self.first_name()) if callable(self.first_name) - else str(self.first_name) - ) - - self.last_name = ( - input("Last name: ") if self.last_name is None - else str(self.last_name()) if callable(self.last_name) - else str(self.last_name) - ) - - self.phone_code = ( - input("Enter phone code: ") if self.phone_code is None - else str(self.phone_code(self.phone_number)) if callable(self.phone_code) - else str(self.phone_code) - ) - - try: - if phone_registered: - try: - r = self.send( - functions.auth.SignIn( - phone_number=self.phone_number, - phone_code_hash=phone_code_hash, - phone_code=self.phone_code - ) - ) - except PhoneNumberUnoccupied: - log.warning("Phone number unregistered") - phone_registered = False - continue - else: - try: - r = self.send( - functions.auth.SignUp( - phone_number=self.phone_number, - phone_code_hash=phone_code_hash, - phone_code=self.phone_code, - first_name=self.first_name, - last_name=self.last_name - ) - ) - except PhoneNumberOccupied: - log.warning("Phone number already registered") - phone_registered = True - continue - except (PhoneCodeInvalid, PhoneCodeEmpty, PhoneCodeExpired, PhoneCodeHashEmpty) as e: - if phone_code_invalid_raises: - raise - else: - print(e.MESSAGE) - self.phone_code = None - except FirstnameInvalid as e: - if first_name_invalid_raises: - raise - else: - print(e.MESSAGE) - self.first_name = None - except SessionPasswordNeeded as e: - print(e.MESSAGE) - - def default_password_callback(password_hint: str) -> str: - print("Hint: {}".format(password_hint)) - return input("Enter password (empty to recover): ") - - def default_recovery_callback(email_pattern: str) -> str: - print("An e-mail containing the recovery code has been sent to {}".format(email_pattern)) - return input("Enter password recovery code: ") - - while True: - try: - r = self.send(functions.account.GetPassword()) - - self.password = ( - default_password_callback(r.hint) if self.password is None - else str(self.password(r.hint) or "") if callable(self.password) - else str(self.password) - ) - - if self.password == "": - r = self.send(functions.auth.RequestPasswordRecovery()) - - self.recovery_code = ( - default_recovery_callback(r.email_pattern) if self.recovery_code is None - else str(self.recovery_code(r.email_pattern)) if callable(self.recovery_code) - else str(self.recovery_code) - ) - - r = self.send( - functions.auth.RecoverPassword( - code=self.recovery_code - ) - ) - else: - r = self.send( - functions.auth.CheckPassword( - password=compute_check(r, self.password) - ) - ) - except (PasswordEmpty, PasswordRecoveryNa, PasswordHashInvalid) as e: - if password_invalid_raises: - raise - else: - print(e.MESSAGE) - self.password = None - self.recovery_code = None - except FloodWait as e: - if password_invalid_raises: - raise - else: - print(e.MESSAGE.format(x=e.x)) - time.sleep(e.x) - self.password = None - self.recovery_code = None - except Exception as e: - log.error(e, exc_info=True) - raise - else: - break - break - except FloodWait as e: - if phone_code_invalid_raises or first_name_invalid_raises: - raise - else: - print(e.MESSAGE.format(x=e.x)) - time.sleep(e.x) - except Exception as e: - log.error(e, exc_info=True) - raise - else: - break - - if terms_of_service: - assert self.send( - functions.help.AcceptTermsOfService( - id=terms_of_service.id - ) - ) - - self.password = None - self.user_id = r.user.id - - 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 - ] - ] - ): - for entity in entities: - if isinstance(entity, types.User): - user_id = entity.id - - access_hash = entity.access_hash - - if access_hash is None: - continue - - username = entity.username - phone = entity.phone - - input_peer = types.InputPeerUser( - user_id=user_id, - access_hash=access_hash - ) - - self.peers_by_id[user_id] = input_peer - - if username is not None: - self.peers_by_username[username.lower()] = input_peer - - if phone is not None: - self.peers_by_phone[phone] = input_peer - - if isinstance(entity, (types.Chat, types.ChatForbidden)): - chat_id = entity.id - peer_id = -chat_id - - input_peer = types.InputPeerChat( - chat_id=chat_id - ) - - self.peers_by_id[peer_id] = input_peer - - if isinstance(entity, (types.Channel, types.ChannelForbidden)): - channel_id = entity.id - peer_id = int("-100" + str(channel_id)) - - access_hash = entity.access_hash - - if access_hash is None: - continue - - username = getattr(entity, "username", None) - - input_peer = types.InputPeerChannel( - channel_id=channel_id, - access_hash=access_hash - ) - - self.peers_by_id[peer_id] = input_peer - - if username is not None: - self.peers_by_username[username.lower()] = input_peer - - def download_worker(self): - name = threading.current_thread().name - log.debug("{} started".format(name)) - - while True: - media = self.download_queue.get() - - if media 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: - guessed_extension = self.guess_extension(media.mime_type) - - if media_type in (0, 1, 2): - extension = ".jpg" - elif media_type == 3: - extension = guessed_extension or ".ogg" - elif media_type in (4, 10, 13): - extension = guessed_extension or ".mp4" - elif media_type == 5: - extension = guessed_extension or ".zip" - elif media_type == 8: - extension = guessed_extension or ".webp" - elif media_type == 9: - extension = guessed_extension or ".mp3" - 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 - ) - - 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, - progress=progress, - progress_args=progress_args - ) - - if temp_file_path: - final_file_path = os.path.abspath(re.sub("\\\\", "/", os.path.join(directory, file_name))) - os.makedirs(directory, exist_ok=True) - shutil.move(temp_file_path, final_file_path) - except Exception as e: - log.error(e, exc_info=True) - - try: - os.remove(temp_file_path) - except OSError: - pass - else: - # TODO: "" or None for faulty download, which is better? - # os.path methods return "" in case something does not exist, I prefer this. - # For now let's keep None - path[0] = final_file_path or None - finally: - done.set() - - log.debug("{} stopped".format(name)) - - def updates_worker(self): - name = threading.current_thread().name - log.debug("{} started".format(name)) - - while True: - updates = self.updates_queue.get() - - if updates is None: - break - - try: - if isinstance(updates, (types.Update, types.UpdatesCombined)): - self.fetch_peers(updates.users) - self.fetch_peers(updates.chats) - - for update in updates.updates: - channel_id = getattr( - getattr( - getattr( - update, "message", None - ), "to_id", None - ), "channel_id", None - ) or getattr(update, "channel_id", None) - - pts = getattr(update, "pts", None) - pts_count = getattr(update, "pts_count", None) - - if isinstance(update, types.UpdateChannelTooLong): - log.warning(update) - - if isinstance(update, types.UpdateNewChannelMessage): - message = update.message - - if not isinstance(message, types.MessageEmpty): - try: - diff = self.send( - functions.updates.GetChannelDifference( - channel=self.resolve_peer(int("-100" + str(channel_id))), - filter=types.ChannelMessagesFilter( - ranges=[types.MessageRange( - min_id=update.message.id, - max_id=update.message.id - )] - ), - pts=pts - pts_count, - limit=pts - ) - ) - except ChannelPrivate: - pass - else: - if not isinstance(diff, types.updates.ChannelDifferenceEmpty): - updates.users += diff.users - updates.chats += 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)) - elif isinstance(updates, (types.UpdateShortMessage, types.UpdateShortChatMessage)): - diff = self.send( - functions.updates.GetDifference( - pts=updates.pts - updates.pts_count, - date=updates.date, - qts=-1 - ) - ) - - if diff.new_messages: - self.dispatcher.updates_queue.put(( - types.UpdateNewMessage( - message=diff.new_messages[0], - pts=updates.pts, - pts_count=updates.pts_count - ), - diff.users, - diff.chats - )) - else: - self.dispatcher.updates_queue.put((diff.other_updates[0], [], [])) - elif isinstance(updates, types.UpdateShort): - self.dispatcher.updates_queue.put((updates.update, [], [])) - elif isinstance(updates, types.UpdatesTooLong): - log.warning(updates) - except Exception as e: - log.error(e, exc_info=True) - - 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 Telegram queries. - - 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... - - .. 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``): - Number of retries. - - timeout (``float``): - Timeout in seconds. - - Returns: - ``RawType``: The raw type response generated by the query. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - if not self.is_started: - raise ConnectionError("Client has not been started") - - if self.no_updates: - data = functions.InvokeWithoutUpdates(query=data) - - if self.takeout_id: - data = functions.InvokeWithTakeout(takeout_id=self.takeout_id, query=data) - - r = self.session.send(data, retries, timeout) - - self.fetch_peers(getattr(r, "users", [])) - self.fetch_peers(getattr(r, "chats", [])) - - return r - - def load_config(self): - parser = ConfigParser() - parser.read(self.config_file) - - if self.api_id and self.api_hash: - pass - else: - if parser.has_section("pyrogram"): - self.api_id = parser.getint("pyrogram", "api_id") - self.api_hash = parser.get("pyrogram", "api_hash") - else: - raise AttributeError( - "No API Key found. " - "More info: https://docs.pyrogram.ml/start/ProjectSetup#configuration" - ) - - for option in ["app_version", "device_model", "system_version", "lang_code"]: - if getattr(self, option): - pass - else: - if parser.has_section("pyrogram"): - setattr(self, option, parser.get( - "pyrogram", - option, - fallback=getattr(Client, option.upper()) - )) - else: - setattr(self, option, getattr(Client, option.upper())) - - if self._proxy: - self._proxy["enabled"] = bool(self._proxy.get("enabled", True)) - else: - self._proxy = {} - - if parser.has_section("proxy"): - self._proxy["enabled"] = parser.getboolean("proxy", "enabled", fallback=True) - self._proxy["hostname"] = parser.get("proxy", "hostname") - self._proxy["port"] = parser.getint("proxy", "port") - self._proxy["username"] = parser.get("proxy", "username", fallback=None) or None - 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 - 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 - } - 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") - ] - - def load_session(self): - try: - with open(os.path.join(self.workdir, "{}.session".format(self.session_name)), encoding="utf-8") as f: - s = json.load(f) - except FileNotFoundError: - self.dc_id = 1 - self.date = 0 - self.auth_key = Auth(self.dc_id, self.test_mode, self.ipv6, self._proxy).create() - else: - self.dc_id = s["dc_id"] - self.test_mode = s["test_mode"] - self.auth_key = base64.b64decode("".join(s["auth_key"])) - self.user_id = s["user_id"] - self.date = s.get("date", 0) - # TODO: replace default with False once token session name will be deprecated - self.is_bot = s.get("is_bot", self.is_bot) - - for k, v in s.get("peers_by_id", {}).items(): - self.peers_by_id[int(k)] = utils.get_input_peer(int(k), v) - - for k, v in s.get("peers_by_username", {}).items(): - peer = self.peers_by_id.get(v, None) - - if peer: - self.peers_by_username[k] = peer - - for k, v in s.get("peers_by_phone", {}).items(): - peer = self.peers_by_id.get(v, None) - - if peer: - self.peers_by_phone[k] = peer - - def load_plugins(self): - if self.plugins.get("enabled", False): - root = self.plugins["root"] - include = self.plugins["include"] - exclude = self.plugins["exclude"] - - count = 0 - - if include is None: - for path in sorted(Path(root).rglob("*.py")): - module_path = '.'.join(path.parent.parts + (path.stem,)) - module = import_module(module_path) - - for name in vars(module).keys(): - # noinspection PyBroadException - try: - handler, group = getattr(module, name) - - if isinstance(handler, Handler) and isinstance(group, int): - self.add_handler(handler, group) - - log.info('[{}] [LOAD] {}("{}") in group {} from "{}"'.format( - self.session_name, type(handler).__name__, name, group, module_path)) - - count += 1 - except Exception: - pass - else: - for path, handlers in include: - module_path = root + "." + path - warn_non_existent_functions = True - - try: - module = import_module(module_path) - except ImportError: - 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( - self.session_name, module_path)) - continue - - if handlers is None: - handlers = vars(module).keys() - warn_non_existent_functions = False - - for name in handlers: - # noinspection PyBroadException - try: - handler, group = getattr(module, name) - - if isinstance(handler, Handler) and isinstance(group, int): - self.add_handler(handler, group) - - 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( - self.session_name, name, module_path)) - - if exclude is not None: - for path, handlers in exclude: - module_path = root + "." + path - warn_non_existent_functions = True - - try: - module = import_module(module_path) - except ImportError: - 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( - self.session_name, module_path)) - continue - - if handlers is None: - handlers = vars(module).keys() - warn_non_existent_functions = False - - for name in handlers: - # noinspection PyBroadException - try: - handler, group = getattr(module, name) - - if isinstance(handler, Handler) and isinstance(group, int): - self.remove_handler(handler, group) - - 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( - self.session_name, name, module_path)) - - if count > 0: - 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( - self.session_name, root)) - - def save_session(self): - auth_key = base64.b64encode(self.auth_key).decode() - auth_key = [auth_key[i: i + 43] for i in range(0, len(auth_key), 43)] - - os.makedirs(self.workdir, exist_ok=True) - - with open(os.path.join(self.workdir, "{}.session".format(self.session_name)), "w", encoding="utf-8") as f: - json.dump( - dict( - dc_id=self.dc_id, - test_mode=self.test_mode, - auth_key=auth_key, - user_id=self.user_id, - date=self.date, - is_bot=self.is_bot, - ), - f, - indent=4 - ) - - def get_initial_dialogs_chunk(self, offset_date: int = 0): - while True: - try: - r = self.send( - functions.messages.GetDialogs( - offset_date=offset_date, - offset_id=0, - offset_peer=types.InputPeerEmpty(), - limit=self.DIALOGS_AT_ONCE, - hash=0, - exclude_pinned=True - ) - ) - except FloodWait as e: - log.warning("get_dialogs flood: waiting {} seconds".format(e.x)) - time.sleep(e.x) - else: - log.info("Total peers: {}".format(len(self.peers_by_id))) - return r - - def get_initial_dialogs(self): - self.send(functions.messages.GetPinnedDialogs()) - - dialogs = self.get_initial_dialogs_chunk() - offset_date = utils.get_offset_date(dialogs) - - while len(dialogs.dialogs) == self.DIALOGS_AT_ONCE: - dialogs = self.get_initial_dialogs_chunk(offset_date) - offset_date = utils.get_offset_date(dialogs) - - 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. - Useful whenever an InputPeer type is required. - - .. 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: - 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: - ``InputPeer``: On success, the resolved peer id is returned in form of an InputPeer object. - - Raises: - RPCError: In case of a Telegram RPC error. - KeyError: In case the peer doesn't exist in the internal database. - """ - try: - return self.peers_by_id[peer_id] - except KeyError: - if type(peer_id) is str: - if peer_id in ("self", "me"): - return types.InputPeerSelf() - - peer_id = re.sub(r"[@+\s]", "", peer_id.lower()) - - try: - int(peer_id) - except ValueError: - if peer_id not in self.peers_by_username: - self.send( - functions.contacts.ResolveUsername( - username=peer_id - ) - ) - - return self.peers_by_username[peer_id] - else: - try: - return self.peers_by_phone[peer_id] - except KeyError: - raise PeerIdInvalid - - if peer_id > 0: - self.fetch_peers( - self.send( - functions.users.GetUsers( - id=[types.InputUser(user_id=peer_id, access_hash=0)] - ) - ) - ) - else: - if str(peer_id).startswith("-100"): - self.send( - functions.channels.GetChannels( - id=[types.InputChannel(channel_id=int(str(peer_id)[4:]), access_hash=0)] - ) - ) - else: - self.send( - functions.messages.GetChats( - id=[-peer_id] - ) - ) - - try: - return self.peers_by_id[peer_id] - 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. - Useful whenever an InputFile type is required. - - .. 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: - path (``str``): - The path of the file you want to upload that exists on your local machine. - - file_id (``int``, *optional*): - In case a file part expired, pass the file_id and the file_part to retry uploading that specific chunk. - - file_part (``int``, *optional*): - In case a file part expired, pass the file_id and the file_part to retry uploading that specific chunk. - - progress (``callable``, *optional*): - Pass a callback function to view the upload progress. - The function must take *(client, current, total, \*args)* as positional arguments (look at the section - below for a detailed description). - - progress_args (``tuple``, *optional*): - Extra custom arguments for the progress callback function. Useful, for example, if you want to pass - a chat_id and a message_id in order to edit a message with the updated progress. - - Other Parameters: - client (:obj:`Client`): - The Client itself, useful when you want to call other API methods inside the callback function. - - current (``int``): - The amount of bytes uploaded so far. - - total (``int``): - The size of the file. - - *args (``tuple``, *optional*): - Extra custom arguments as defined in the *progress_args* parameter. - You can either keep *\*args* or add every single extra argument in your function signature. - - Returns: - ``InputFile``: On success, the uploaded file is returned in form of an InputFile object. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - part_size = 512 * 1024 - file_size = os.path.getsize(path) - - if file_size == 0: - raise ValueError("File size equals to 0 B") - - if file_size > 1500 * 1024 * 1024: - raise ValueError("Telegram doesn't support uploading files bigger than 1500 MiB") - - file_total_parts = int(math.ceil(file_size / part_size)) - is_big = True if file_size > 10 * 1024 * 1024 else False - is_missing_part = True if file_id is not None else False - file_id = file_id or self.rnd_id() - md5_sum = md5() if not is_big and not is_missing_part else None - - session = Session(self, self.dc_id, self.auth_key, is_media=True) - session.start() - - try: - with open(path, "rb") as f: - f.seek(part_size * file_part) - - while True: - chunk = f.read(part_size) - - if not chunk: - if not is_big: - md5_sum = "".join([hex(i)[2:].zfill(2) for i in md5_sum.digest()]) - break - - for _ in range(3): - if is_big: - rpc = functions.upload.SaveBigFilePart( - file_id=file_id, - file_part=file_part, - file_total_parts=file_total_parts, - bytes=chunk - ) - else: - rpc = functions.upload.SaveFilePart( - file_id=file_id, - file_part=file_part, - bytes=chunk - ) - - if session.send(rpc): - break - else: - raise AssertionError("Telegram didn't accept chunk #{} of {}".format(file_part, path)) - - if is_missing_part: - return - - if not is_big: - md5_sum.update(chunk) - - file_part += 1 - - if progress: - progress(self, min(file_part * part_size, file_size), file_size, *progress_args) - except Client.StopTransmission: - raise - except Exception as e: - log.error(e, exc_info=True) - else: - if is_big: - return types.InputFileBig( - id=file_id, - parts=file_total_parts, - name=os.path.basename(path), - - ) - else: - return types.InputFile( - id=file_id, - parts=file_total_parts, - name=os.path.basename(path), - md5_checksum=md5_sum - ) - 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: - with self.media_sessions_lock: - session = self.media_sessions.get(dc_id, None) - - if session is None: - if dc_id != self.dc_id: - exported_auth = self.send( - functions.auth.ExportAuthorization( - dc_id=dc_id - ) - ) - - session = Session( - self, - dc_id, - Auth(dc_id, self.test_mode, self.ipv6, self._proxy).create(), - is_media=True - ) - - session.start() - - self.media_sessions[dc_id] = session - - session.send( - functions.auth.ImportAuthorization( - id=exported_auth.id, - bytes=exported_auth.bytes - ) - ) - else: - session = Session( - self, - dc_id, - self.auth_key, - is_media=True - ) - - session.start() - - self.media_sessions[dc_id] = session - - if volume_id: # Photos are accessed by volume_id, local_id, secret - location = types.InputFileLocation( - volume_id=volume_id, - local_id=local_id, - secret=secret, - file_reference=b"" - ) - else: # Any other file can be more easily accessed by id and access_hash - location = types.InputDocumentFileLocation( - id=id, - access_hash=access_hash, - file_reference=b"" - ) - - limit = 1024 * 1024 - offset = 0 - file_name = "" - - try: - r = session.send( - functions.upload.GetFile( - location=location, - offset=offset, - limit=limit - ) - ) - - if isinstance(r, types.upload.File): - with tempfile.NamedTemporaryFile("wb", delete=False) as f: - file_name = f.name - - while True: - chunk = r.bytes - - if not chunk: - break - - f.write(chunk) - - offset += limit - - if progress: - progress(self, min(offset, size) if size != 0 else offset, size, *progress_args) - - r = session.send( - functions.upload.GetFile( - location=location, - offset=offset, - limit=limit - ) - ) - - elif isinstance(r, types.upload.FileCdnRedirect): - with self.media_sessions_lock: - cdn_session = self.media_sessions.get(r.dc_id, None) - - if cdn_session is None: - cdn_session = Session( - self, - r.dc_id, - Auth(r.dc_id, self.test_mode, self.ipv6, self._proxy).create(), - is_media=True, - is_cdn=True - ) - - cdn_session.start() - - self.media_sessions[r.dc_id] = cdn_session - - try: - with tempfile.NamedTemporaryFile("wb", delete=False) as f: - file_name = f.name - - while True: - r2 = cdn_session.send( - functions.upload.GetCdnFile( - file_token=r.file_token, - offset=offset, - limit=limit - ) - ) - - if isinstance(r2, types.upload.CdnFileReuploadNeeded): - try: - session.send( - functions.upload.ReuploadCdnFile( - file_token=r.file_token, - request_token=r2.request_token - ) - ) - except VolumeLocNotFound: - break - else: - continue - - chunk = r2.bytes - - # https://core.telegram.org/cdn#decrypting-files - decrypted_chunk = AES.ctr256_decrypt( - chunk, - r.encryption_key, - bytearray( - r.encryption_iv[:-4] - + (offset // 16).to_bytes(4, "big") - ) - ) - - hashes = session.send( - functions.upload.GetCdnFileHashes( - file_token=r.file_token, - offset=offset - ) - ) - - # https://core.telegram.org/cdn#verifying-files - for i, h in enumerate(hashes): - cdn_chunk = decrypted_chunk[h.limit * i: h.limit * (i + 1)] - assert h.hash == sha256(cdn_chunk).digest(), "Invalid CDN hash part {}".format(i) - - f.write(decrypted_chunk) - - offset += limit - - if progress: - progress(self, min(offset, size) if size != 0 else offset, size, *progress_args) - - if len(chunk) < limit: - break - except Exception as e: - raise e - except Exception as e: - if not isinstance(e, Client.StopTransmission): - log.error(e, exc_info=True) - - try: - os.remove(file_name) - except OSError: - pass - - 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 deleted file mode 100644 index 917c9e62434..00000000000 --- a/pyrogram/client/ext/__init__.py +++ /dev/null @@ -1,22 +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 .base_client import BaseClient -from .dispatcher import Dispatcher -from .emoji import Emoji -from .syncer import Syncer diff --git a/pyrogram/client/ext/base_client.py b/pyrogram/client/ext/base_client.py deleted file mode 100644 index ee7b2c243c4..00000000000 --- a/pyrogram/client/ext/base_client.py +++ /dev/null @@ -1,155 +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 . - -import os -import platform -import re -from queue import Queue -from threading import Lock - -from pyrogram import __version__ -from ..style import Markdown, HTML -from ...session.internals import MsgId - - -class BaseClient: - class StopTransmission(StopIteration): - pass - - APP_VERSION = "Pyrogram \U0001f525 {}".format(__version__) - - DEVICE_MODEL = "{} {}".format( - platform.python_implementation(), - platform.python_version() - ) - - SYSTEM_VERSION = "{} {}".format( - platform.system(), - platform.release() - ) - - LANG_CODE = "en" - - 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 = 900 - WORKERS = 4 - WORKDIR = "." - CONFIG_FILE = "./config.ini" - - MEDIA_TYPE_ID = { - 0: "thumbnail", - 1: "chat_photo", - 2: "photo", - 3: "voice", - 4: "video", - 5: "document", - 8: "sticker", - 9: "audio", - 10: "animation", - 13: "video_note" - } - - 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): - self.is_bot = None - self.dc_id = None - self.auth_key = None - self.user_id = None - self.date = None - - self.rnd_id = MsgId - self.channels_pts = {} - - self.peers_by_id = {} - self.peers_by_username = {} - self.peers_by_phone = {} - - self.markdown = Markdown(self) - self.html = HTML(self) - - self.session = None - self.media_sessions = {} - self.media_sessions_lock = Lock() - - self.is_started = None - self.is_idle = None - - self.takeout_id = None - - self.updates_queue = Queue() - self.updates_workers_list = [] - self.download_queue = Queue() - self.download_workers_list = [] - - self.disconnect_handler = None - - def send(self, *args, **kwargs): - pass - - def resolve_peer(self, *args, **kwargs): - pass - - def fetch_peers(self, *args, **kwargs): - pass - - def add_handler(self, *args, **kwargs): - pass - - def save_file(self, *args, **kwargs): - pass - - def get_messages(self, *args, **kwargs): - pass - - def get_history(self, *args, **kwargs): - pass - - def get_dialogs(self, *args, **kwargs): - pass - - def get_chat_members(self, *args, **kwargs): - pass - - def get_chat_members_count(self, *args, **kwargs): - pass - - def answer_inline_query(self, *args, **kwargs): - pass - - def guess_mime_type(self, *args, **kwargs): - pass - - def guess_extension(self, *args, **kwargs): - pass diff --git a/pyrogram/client/ext/dispatcher.py b/pyrogram/client/ext/dispatcher.py deleted file mode 100644 index 1004095b140..00000000000 --- a/pyrogram/client/ext/dispatcher.py +++ /dev/null @@ -1,174 +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 . - -import logging -import threading -from collections import OrderedDict -from queue import Queue -from threading import Thread - -import pyrogram -from pyrogram.api import types -from ..handlers import ( - CallbackQueryHandler, MessageHandler, DeletedMessagesHandler, - UserStatusHandler, RawUpdateHandler, InlineQueryHandler, PollHandler -) - -log = logging.getLogger(__name__) - - -class Dispatcher: - NEW_MESSAGE_UPDATES = ( - types.UpdateNewMessage, - types.UpdateNewChannelMessage - ) - - EDIT_MESSAGE_UPDATES = ( - types.UpdateEditMessage, - types.UpdateEditChannelMessage - ) - - DELETE_MESSAGES_UPDATES = ( - types.UpdateDeleteMessages, - types.UpdateDeleteChannelMessages - ) - - CALLBACK_QUERY_UPDATES = ( - types.UpdateBotCallbackQuery, - types.UpdateInlineBotCallbackQuery - ) - - MESSAGE_UPDATES = NEW_MESSAGE_UPDATES + EDIT_MESSAGE_UPDATES - - def __init__(self, client, workers: int): - self.client = client - self.workers = workers - - self.workers_list = [] - self.updates_queue = Queue() - self.groups = OrderedDict() - - self.update_parsers = { - Dispatcher.MESSAGE_UPDATES: - 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), - - Dispatcher.CALLBACK_QUERY_UPDATES: - lambda upd, usr, cht: (pyrogram.CallbackQuery._parse(self.client, upd, usr), CallbackQueryHandler), - - (types.UpdateUserStatus,): - lambda upd, usr, cht: ( - pyrogram.UserStatus._parse(self.client, upd.status, upd.user_id), UserStatusHandler - ), - - (types.UpdateBotInlineQuery,): - 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} - - def start(self): - for i in range(self.workers): - self.workers_list.append( - Thread( - target=self.update_worker, - name="UpdateWorker#{}".format(i + 1) - ) - ) - - self.workers_list[-1].start() - - def stop(self): - for _ in range(self.workers): - self.updates_queue.put(None) - - for worker in self.workers_list: - worker.join() - - self.workers_list.clear() - - def add_handler(self, handler, group: int): - if group not in self.groups: - self.groups[group] = [] - self.groups = OrderedDict(sorted(self.groups.items())) - - self.groups[group].append(handler) - - def remove_handler(self, handler, group: int): - if group not in self.groups: - raise ValueError("Group {} does not exist. Handler was not removed.".format(group)) - - self.groups[group].remove(handler) - - def update_worker(self): - name = threading.current_thread().name - log.debug("{} started".format(name)) - - while True: - update = self.updates_queue.get() - - if update is None: - break - - try: - users = {i.id: i for i in update[1]} - chats = {i.id: i for i in update[2]} - update = update[0] - - parser = self.update_parsers.get(type(update), None) - - parsed_update, handler_type = ( - parser(update, users, chats) - if parser is not None - else (None, type(None)) - ) - - for group in self.groups.values(): - for handler in group: - args = None - - if isinstance(handler, handler_type): - if handler.check(parsed_update): - args = (parsed_update,) - elif isinstance(handler, RawUpdateHandler): - args = (update, users, chats) - - if args is None: - continue - - try: - handler.callback(self.client, *args) - except pyrogram.StopPropagation: - raise - except pyrogram.ContinuePropagation: - continue - except Exception as e: - log.error(e, exc_info=True) - - break - except pyrogram.StopPropagation: - pass - except Exception as e: - log.error(e, exc_info=True) - - log.debug("{} stopped".format(name)) diff --git a/pyrogram/client/ext/emoji.py b/pyrogram/client/ext/emoji.py deleted file mode 100644 index 20d154f447b..00000000000 --- a/pyrogram/client/ext/emoji.py +++ /dev/null @@ -1,7851 +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 . - - -class Emoji: - HELMET_WITH_WHITE_CROSS_TYPE_1_2 = "\u26d1\U0001f3fb" - HELMET_WITH_WHITE_CROSS_TYPE_3 = "\u26d1\U0001f3fc" - HELMET_WITH_WHITE_CROSS_TYPE_4 = "\u26d1\U0001f3fd" - HELMET_WITH_WHITE_CROSS_TYPE_5 = "\u26d1\U0001f3fe" - HELMET_WITH_WHITE_CROSS_TYPE_6 = "\u26d1\U0001f3ff" - KISS_TYPE_1_2 = "\U0001f48f\U0001f3fb" - KISS_TYPE_3 = "\U0001f48f\U0001f3fc" - KISS_TYPE_4 = "\U0001f48f\U0001f3fd" - KISS_TYPE_5 = "\U0001f48f\U0001f3fe" - KISS_TYPE_6 = "\U0001f48f\U0001f3ff" - COUPLE_WITH_HEART_TYPE_1_2 = "\U0001f491\U0001f3fb" - COUPLE_WITH_HEART_TYPE_3 = "\U0001f491\U0001f3fc" - COUPLE_WITH_HEART_TYPE_4 = "\U0001f491\U0001f3fd" - COUPLE_WITH_HEART_TYPE_5 = "\U0001f491\U0001f3fe" - COUPLE_WITH_HEART_TYPE_6 = "\U0001f491\U0001f3ff" - SKIER_TYPE_1_2 = "\u26f7\U0001f3fb" - SKIER_TYPE_3 = "\u26f7\U0001f3fc" - SKIER_TYPE_4 = "\u26f7\U0001f3fd" - SKIER_TYPE_5 = "\u26f7\U0001f3fe" - SKIER_TYPE_6 = "\u26f7\U0001f3ff" - GRINNING_FACE = "\U0001f600" - BEAMING_FACE_WITH_SMILING_EYES = "\U0001f601" - FACE_WITH_TEARS_OF_JOY = "\U0001f602" - ROLLING_ON_THE_FLOOR_LAUGHING = "\U0001f923" - GRINNING_FACE_WITH_BIG_EYES = "\U0001f603" - GRINNING_FACE_WITH_SMILING_EYES = "\U0001f604" - GRINNING_FACE_WITH_SWEAT = "\U0001f605" - GRINNING_SQUINTING_FACE = "\U0001f606" - WINKING_FACE = "\U0001f609" - SMILING_FACE_WITH_SMILING_EYES = "\U0001f60a" - FACE_SAVORING_FOOD = "\U0001f60b" - SMILING_FACE_WITH_SUNGLASSES = "\U0001f60e" - SMILING_FACE_WITH_HEART_EYES = "\U0001f60d" - FACE_BLOWING_A_KISS = "\U0001f618" - SMILING_FACE_WITH_3_HEARTS = "\U0001f970" - KISSING_FACE = "\U0001f617" - KISSING_FACE_WITH_SMILING_EYES = "\U0001f619" - KISSING_FACE_WITH_CLOSED_EYES = "\U0001f61a" - SMILING_FACE = "\u263a\ufe0f" - SLIGHTLY_SMILING_FACE = "\U0001f642" - HUGGING_FACE = "\U0001f917" - STAR_STRUCK = "\U0001f929" - THINKING_FACE = "\U0001f914" - FACE_WITH_RAISED_EYEBROW = "\U0001f928" - NEUTRAL_FACE = "\U0001f610" - EXPRESSIONLESS_FACE = "\U0001f611" - FACE_WITHOUT_MOUTH = "\U0001f636" - FACE_WITH_ROLLING_EYES = "\U0001f644" - SMIRKING_FACE = "\U0001f60f" - PERSEVERING_FACE = "\U0001f623" - SAD_BUT_RELIEVED_FACE = "\U0001f625" - FACE_WITH_OPEN_MOUTH = "\U0001f62e" - ZIPPER_MOUTH_FACE = "\U0001f910" - HUSHED_FACE = "\U0001f62f" - SLEEPY_FACE = "\U0001f62a" - TIRED_FACE = "\U0001f62b" - SLEEPING_FACE = "\U0001f634" - RELIEVED_FACE = "\U0001f60c" - FACE_WITH_TONGUE = "\U0001f61b" - WINKING_FACE_WITH_TONGUE = "\U0001f61c" - SQUINTING_FACE_WITH_TONGUE = "\U0001f61d" - DROOLING_FACE = "\U0001f924" - UNAMUSED_FACE = "\U0001f612" - DOWNCAST_FACE_WITH_SWEAT = "\U0001f613" - PENSIVE_FACE = "\U0001f614" - CONFUSED_FACE = "\U0001f615" - UPSIDE_DOWN_FACE = "\U0001f643" - MONEY_MOUTH_FACE = "\U0001f911" - ASTONISHED_FACE = "\U0001f632" - FROWNING_FACE = "\u2639\ufe0f" - SLIGHTLY_FROWNING_FACE = "\U0001f641" - CONFOUNDED_FACE = "\U0001f616" - DISAPPOINTED_FACE = "\U0001f61e" - WORRIED_FACE = "\U0001f61f" - FACE_WITH_STEAM_FROM_NOSE = "\U0001f624" - CRYING_FACE = "\U0001f622" - LOUDLY_CRYING_FACE = "\U0001f62d" - FROWNING_FACE_WITH_OPEN_MOUTH = "\U0001f626" - ANGUISHED_FACE = "\U0001f627" - FEARFUL_FACE = "\U0001f628" - WEARY_FACE = "\U0001f629" - EXPLODING_HEAD = "\U0001f92f" - GRIMACING_FACE = "\U0001f62c" - ANXIOUS_FACE_WITH_SWEAT = "\U0001f630" - FACE_SCREAMING_IN_FEAR = "\U0001f631" - HOT_FACE = "\U0001f975" - COLD_FACE = "\U0001f976" - FLUSHED_FACE = "\U0001f633" - ZANY_FACE = "\U0001f92a" - DIZZY_FACE = "\U0001f635" - POUTING_FACE = "\U0001f621" - ANGRY_FACE = "\U0001f620" - FACE_WITH_SYMBOLS_ON_MOUTH = "\U0001f92c" - FACE_WITH_MEDICAL_MASK = "\U0001f637" - FACE_WITH_THERMOMETER = "\U0001f912" - FACE_WITH_HEAD_BANDAGE = "\U0001f915" - NAUSEATED_FACE = "\U0001f922" - FACE_VOMITING = "\U0001f92e" - SNEEZING_FACE = "\U0001f927" - SMILING_FACE_WITH_HALO = "\U0001f607" - COWBOY_HAT_FACE = "\U0001f920" - CLOWN_FACE = "\U0001f921" - PARTYING_FACE = "\U0001f973" - WOOZY_FACE = "\U0001f974" - PLEADING_FACE = "\U0001f97a" - LYING_FACE = "\U0001f925" - SHUSHING_FACE = "\U0001f92b" - FACE_WITH_HAND_OVER_MOUTH = "\U0001f92d" - FACE_WITH_MONOCLE = "\U0001f9d0" - NERD_FACE = "\U0001f913" - SMILING_FACE_WITH_HORNS = "\U0001f608" - ANGRY_FACE_WITH_HORNS = "\U0001f47f" - OGRE = "\U0001f479" - GOBLIN = "\U0001f47a" - SKULL = "\U0001f480" - SKULL_AND_CROSSBONES = "\u2620\ufe0f" - GHOST = "\U0001f47b" - ALIEN = "\U0001f47d" - ALIEN_MONSTER = "\U0001f47e" - ROBOT_FACE = "\U0001f916" - PILE_OF_POO = "\U0001f4a9" - GRINNING_CAT_FACE = "\U0001f63a" - GRINNING_CAT_FACE_WITH_SMILING_EYES = "\U0001f638" - CAT_FACE_WITH_TEARS_OF_JOY = "\U0001f639" - SMILING_CAT_FACE_WITH_HEART_EYES = "\U0001f63b" - CAT_FACE_WITH_WRY_SMILE = "\U0001f63c" - KISSING_CAT_FACE = "\U0001f63d" - WEARY_CAT_FACE = "\U0001f640" - CRYING_CAT_FACE = "\U0001f63f" - POUTING_CAT_FACE = "\U0001f63e" - SEE_NO_EVIL_MONKEY = "\U0001f648" - HEAR_NO_EVIL_MONKEY = "\U0001f649" - SPEAK_NO_EVIL_MONKEY = "\U0001f64a" - LIGHT_SKIN_TONE = "\U0001f3fb" - MEDIUM_LIGHT_SKIN_TONE = "\U0001f3fc" - MEDIUM_SKIN_TONE = "\U0001f3fd" - MEDIUM_DARK_SKIN_TONE = "\U0001f3fe" - DARK_SKIN_TONE = "\U0001f3ff" - BABY = "\U0001f476" - BABY_LIGHT_SKIN_TONE = "\U0001f476\U0001f3fb" - BABY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f476\U0001f3fc" - BABY_MEDIUM_SKIN_TONE = "\U0001f476\U0001f3fd" - BABY_MEDIUM_DARK_SKIN_TONE = "\U0001f476\U0001f3fe" - BABY_DARK_SKIN_TONE = "\U0001f476\U0001f3ff" - CHILD = "\U0001f9d2" - CHILD_LIGHT_SKIN_TONE = "\U0001f9d2\U0001f3fb" - CHILD_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d2\U0001f3fc" - CHILD_MEDIUM_SKIN_TONE = "\U0001f9d2\U0001f3fd" - CHILD_MEDIUM_DARK_SKIN_TONE = "\U0001f9d2\U0001f3fe" - CHILD_DARK_SKIN_TONE = "\U0001f9d2\U0001f3ff" - BOY = "\U0001f466" - BOY_LIGHT_SKIN_TONE = "\U0001f466\U0001f3fb" - BOY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f466\U0001f3fc" - BOY_MEDIUM_SKIN_TONE = "\U0001f466\U0001f3fd" - BOY_MEDIUM_DARK_SKIN_TONE = "\U0001f466\U0001f3fe" - BOY_DARK_SKIN_TONE = "\U0001f466\U0001f3ff" - GIRL = "\U0001f467" - GIRL_LIGHT_SKIN_TONE = "\U0001f467\U0001f3fb" - GIRL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f467\U0001f3fc" - GIRL_MEDIUM_SKIN_TONE = "\U0001f467\U0001f3fd" - GIRL_MEDIUM_DARK_SKIN_TONE = "\U0001f467\U0001f3fe" - GIRL_DARK_SKIN_TONE = "\U0001f467\U0001f3ff" - PERSON = "\U0001f9d1" - ADULT_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fb" - ADULT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fc" - ADULT_MEDIUM_SKIN_TONE = "\U0001f9d1\U0001f3fd" - ADULT_MEDIUM_DARK_SKIN_TONE = "\U0001f9d1\U0001f3fe" - ADULT_DARK_SKIN_TONE = "\U0001f9d1\U0001f3ff" - MAN = "\U0001f468" - MAN_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb" - MAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc" - MAN_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd" - MAN_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe" - MAN_DARK_SKIN_TONE = "\U0001f468\U0001f3ff" - WOMAN = "\U0001f469" - WOMAN_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb" - WOMAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc" - WOMAN_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd" - WOMAN_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe" - WOMAN_DARK_SKIN_TONE = "\U0001f469\U0001f3ff" - OLDER_PERSON = "\U0001f9d3" - OLDER_ADULT_LIGHT_SKIN_TONE = "\U0001f9d3\U0001f3fb" - OLDER_ADULT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d3\U0001f3fc" - OLDER_ADULT_MEDIUM_SKIN_TONE = "\U0001f9d3\U0001f3fd" - OLDER_ADULT_MEDIUM_DARK_SKIN_TONE = "\U0001f9d3\U0001f3fe" - OLDER_ADULT_DARK_SKIN_TONE = "\U0001f9d3\U0001f3ff" - OLD_MAN = "\U0001f474" - OLD_MAN_LIGHT_SKIN_TONE = "\U0001f474\U0001f3fb" - OLD_MAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f474\U0001f3fc" - OLD_MAN_MEDIUM_SKIN_TONE = "\U0001f474\U0001f3fd" - OLD_MAN_MEDIUM_DARK_SKIN_TONE = "\U0001f474\U0001f3fe" - OLD_MAN_DARK_SKIN_TONE = "\U0001f474\U0001f3ff" - OLD_WOMAN = "\U0001f475" - OLD_WOMAN_LIGHT_SKIN_TONE = "\U0001f475\U0001f3fb" - OLD_WOMAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f475\U0001f3fc" - OLD_WOMAN_MEDIUM_SKIN_TONE = "\U0001f475\U0001f3fd" - OLD_WOMAN_MEDIUM_DARK_SKIN_TONE = "\U0001f475\U0001f3fe" - OLD_WOMAN_DARK_SKIN_TONE = "\U0001f475\U0001f3ff" - MAN_HEALTH_WORKER = "\U0001f468\u200d\u2695\ufe0f" - MAN_HEALTH_WORKER_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\u2695\ufe0f" - MAN_HEALTH_WORKER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\u2695\ufe0f" - MAN_HEALTH_WORKER_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\u2695\ufe0f" - MAN_HEALTH_WORKER_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\u2695\ufe0f" - MAN_HEALTH_WORKER_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\u2695\ufe0f" - WOMAN_HEALTH_WORKER = "\U0001f469\u200d\u2695\ufe0f" - WOMAN_HEALTH_WORKER_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\u2695\ufe0f" - WOMAN_HEALTH_WORKER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\u2695\ufe0f" - WOMAN_HEALTH_WORKER_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\u2695\ufe0f" - WOMAN_HEALTH_WORKER_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\u2695\ufe0f" - WOMAN_HEALTH_WORKER_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\u2695\ufe0f" - MAN_STUDENT = "\U0001f468\u200d\U0001f393" - MAN_STUDENT_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f393" - MAN_STUDENT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f393" - MAN_STUDENT_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f393" - MAN_STUDENT_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f393" - MAN_STUDENT_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f393" - WOMAN_STUDENT = "\U0001f469\u200d\U0001f393" - WOMAN_STUDENT_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f393" - WOMAN_STUDENT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f393" - WOMAN_STUDENT_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f393" - WOMAN_STUDENT_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f393" - WOMAN_STUDENT_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f393" - MAN_TEACHER = "\U0001f468\u200d\U0001f3eb" - MAN_TEACHER_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f3eb" - MAN_TEACHER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f3eb" - MAN_TEACHER_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f3eb" - MAN_TEACHER_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f3eb" - MAN_TEACHER_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f3eb" - WOMAN_TEACHER = "\U0001f469\u200d\U0001f3eb" - WOMAN_TEACHER_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f3eb" - WOMAN_TEACHER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f3eb" - WOMAN_TEACHER_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f3eb" - WOMAN_TEACHER_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f3eb" - WOMAN_TEACHER_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f3eb" - MAN_JUDGE = "\U0001f468\u200d\u2696\ufe0f" - MAN_JUDGE_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\u2696\ufe0f" - MAN_JUDGE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\u2696\ufe0f" - MAN_JUDGE_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\u2696\ufe0f" - MAN_JUDGE_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\u2696\ufe0f" - MAN_JUDGE_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\u2696\ufe0f" - WOMAN_JUDGE = "\U0001f469\u200d\u2696\ufe0f" - WOMAN_JUDGE_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\u2696\ufe0f" - WOMAN_JUDGE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\u2696\ufe0f" - WOMAN_JUDGE_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\u2696\ufe0f" - WOMAN_JUDGE_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\u2696\ufe0f" - WOMAN_JUDGE_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\u2696\ufe0f" - MAN_FARMER = "\U0001f468\u200d\U0001f33e" - MAN_FARMER_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f33e" - MAN_FARMER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f33e" - MAN_FARMER_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f33e" - MAN_FARMER_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f33e" - MAN_FARMER_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f33e" - WOMAN_FARMER = "\U0001f469\u200d\U0001f33e" - WOMAN_FARMER_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f33e" - WOMAN_FARMER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f33e" - WOMAN_FARMER_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f33e" - WOMAN_FARMER_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f33e" - WOMAN_FARMER_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f33e" - MAN_COOK = "\U0001f468\u200d\U0001f373" - MAN_COOK_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f373" - MAN_COOK_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f373" - MAN_COOK_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f373" - MAN_COOK_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f373" - MAN_COOK_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f373" - WOMAN_COOK = "\U0001f469\u200d\U0001f373" - WOMAN_COOK_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f373" - WOMAN_COOK_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f373" - WOMAN_COOK_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f373" - WOMAN_COOK_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f373" - WOMAN_COOK_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f373" - MAN_MECHANIC = "\U0001f468\u200d\U0001f527" - MAN_MECHANIC_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f527" - MAN_MECHANIC_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f527" - MAN_MECHANIC_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f527" - MAN_MECHANIC_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f527" - MAN_MECHANIC_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f527" - WOMAN_MECHANIC = "\U0001f469\u200d\U0001f527" - WOMAN_MECHANIC_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f527" - WOMAN_MECHANIC_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f527" - WOMAN_MECHANIC_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f527" - WOMAN_MECHANIC_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f527" - WOMAN_MECHANIC_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f527" - MAN_FACTORY_WORKER = "\U0001f468\u200d\U0001f3ed" - MAN_FACTORY_WORKER_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f3ed" - MAN_FACTORY_WORKER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f3ed" - MAN_FACTORY_WORKER_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f3ed" - MAN_FACTORY_WORKER_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f3ed" - MAN_FACTORY_WORKER_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f3ed" - WOMAN_FACTORY_WORKER = "\U0001f469\u200d\U0001f3ed" - WOMAN_FACTORY_WORKER_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f3ed" - WOMAN_FACTORY_WORKER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f3ed" - WOMAN_FACTORY_WORKER_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f3ed" - WOMAN_FACTORY_WORKER_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f3ed" - WOMAN_FACTORY_WORKER_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f3ed" - MAN_OFFICE_WORKER = "\U0001f468\u200d\U0001f4bc" - MAN_OFFICE_WORKER_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f4bc" - MAN_OFFICE_WORKER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f4bc" - MAN_OFFICE_WORKER_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f4bc" - MAN_OFFICE_WORKER_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f4bc" - MAN_OFFICE_WORKER_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f4bc" - WOMAN_OFFICE_WORKER = "\U0001f469\u200d\U0001f4bc" - WOMAN_OFFICE_WORKER_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f4bc" - WOMAN_OFFICE_WORKER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f4bc" - WOMAN_OFFICE_WORKER_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f4bc" - WOMAN_OFFICE_WORKER_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f4bc" - WOMAN_OFFICE_WORKER_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f4bc" - MAN_SCIENTIST = "\U0001f468\u200d\U0001f52c" - MAN_SCIENTIST_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f52c" - MAN_SCIENTIST_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f52c" - MAN_SCIENTIST_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f52c" - MAN_SCIENTIST_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f52c" - MAN_SCIENTIST_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f52c" - WOMAN_SCIENTIST = "\U0001f469\u200d\U0001f52c" - WOMAN_SCIENTIST_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f52c" - WOMAN_SCIENTIST_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f52c" - WOMAN_SCIENTIST_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f52c" - WOMAN_SCIENTIST_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f52c" - WOMAN_SCIENTIST_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f52c" - MAN_TECHNOLOGIST = "\U0001f468\u200d\U0001f4bb" - MAN_TECHNOLOGIST_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f4bb" - MAN_TECHNOLOGIST_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f4bb" - MAN_TECHNOLOGIST_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f4bb" - MAN_TECHNOLOGIST_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f4bb" - MAN_TECHNOLOGIST_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f4bb" - WOMAN_TECHNOLOGIST = "\U0001f469\u200d\U0001f4bb" - WOMAN_TECHNOLOGIST_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f4bb" - WOMAN_TECHNOLOGIST_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f4bb" - WOMAN_TECHNOLOGIST_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f4bb" - WOMAN_TECHNOLOGIST_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f4bb" - WOMAN_TECHNOLOGIST_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f4bb" - MAN_SINGER = "\U0001f468\u200d\U0001f3a4" - MAN_SINGER_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f3a4" - MAN_SINGER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f3a4" - MAN_SINGER_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f3a4" - MAN_SINGER_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f3a4" - MAN_SINGER_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f3a4" - WOMAN_SINGER = "\U0001f469\u200d\U0001f3a4" - WOMAN_SINGER_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f3a4" - WOMAN_SINGER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f3a4" - WOMAN_SINGER_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f3a4" - WOMAN_SINGER_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f3a4" - WOMAN_SINGER_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f3a4" - MAN_ARTIST = "\U0001f468\u200d\U0001f3a8" - MAN_ARTIST_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f3a8" - MAN_ARTIST_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f3a8" - MAN_ARTIST_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f3a8" - MAN_ARTIST_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f3a8" - MAN_ARTIST_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f3a8" - WOMAN_ARTIST = "\U0001f469\u200d\U0001f3a8" - WOMAN_ARTIST_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f3a8" - WOMAN_ARTIST_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f3a8" - WOMAN_ARTIST_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f3a8" - WOMAN_ARTIST_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f3a8" - WOMAN_ARTIST_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f3a8" - MAN_PILOT = "\U0001f468\u200d\u2708\ufe0f" - MAN_PILOT_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\u2708\ufe0f" - MAN_PILOT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\u2708\ufe0f" - MAN_PILOT_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\u2708\ufe0f" - MAN_PILOT_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\u2708\ufe0f" - MAN_PILOT_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\u2708\ufe0f" - WOMAN_PILOT = "\U0001f469\u200d\u2708\ufe0f" - WOMAN_PILOT_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\u2708\ufe0f" - WOMAN_PILOT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\u2708\ufe0f" - WOMAN_PILOT_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\u2708\ufe0f" - WOMAN_PILOT_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\u2708\ufe0f" - WOMAN_PILOT_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\u2708\ufe0f" - MAN_ASTRONAUT = "\U0001f468\u200d\U0001f680" - MAN_ASTRONAUT_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f680" - MAN_ASTRONAUT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f680" - MAN_ASTRONAUT_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f680" - MAN_ASTRONAUT_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f680" - MAN_ASTRONAUT_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f680" - WOMAN_ASTRONAUT = "\U0001f469\u200d\U0001f680" - WOMAN_ASTRONAUT_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f680" - WOMAN_ASTRONAUT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f680" - WOMAN_ASTRONAUT_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f680" - WOMAN_ASTRONAUT_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f680" - WOMAN_ASTRONAUT_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f680" - MAN_FIREFIGHTER = "\U0001f468\u200d\U0001f692" - MAN_FIREFIGHTER_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f692" - MAN_FIREFIGHTER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f692" - MAN_FIREFIGHTER_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f692" - MAN_FIREFIGHTER_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f692" - MAN_FIREFIGHTER_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f692" - WOMAN_FIREFIGHTER = "\U0001f469\u200d\U0001f692" - WOMAN_FIREFIGHTER_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f692" - WOMAN_FIREFIGHTER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f692" - WOMAN_FIREFIGHTER_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f692" - WOMAN_FIREFIGHTER_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f692" - WOMAN_FIREFIGHTER_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f692" - POLICE_OFFICER = "\U0001f46e" - POLICE_OFFICER_LIGHT_SKIN_TONE = "\U0001f46e\U0001f3fb" - POLICE_OFFICER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f46e\U0001f3fc" - POLICE_OFFICER_MEDIUM_SKIN_TONE = "\U0001f46e\U0001f3fd" - POLICE_OFFICER_MEDIUM_DARK_SKIN_TONE = "\U0001f46e\U0001f3fe" - POLICE_OFFICER_DARK_SKIN_TONE = "\U0001f46e\U0001f3ff" - MAN_POLICE_OFFICER = "\U0001f46e\u200d\u2642\ufe0f" - MAN_POLICE_OFFICER_LIGHT_SKIN_TONE = "\U0001f46e\U0001f3fb\u200d\u2642\ufe0f" - MAN_POLICE_OFFICER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f46e\U0001f3fc\u200d\u2642\ufe0f" - MAN_POLICE_OFFICER_MEDIUM_SKIN_TONE = "\U0001f46e\U0001f3fd\u200d\u2642\ufe0f" - MAN_POLICE_OFFICER_MEDIUM_DARK_SKIN_TONE = "\U0001f46e\U0001f3fe\u200d\u2642\ufe0f" - MAN_POLICE_OFFICER_DARK_SKIN_TONE = "\U0001f46e\U0001f3ff\u200d\u2642\ufe0f" - WOMAN_POLICE_OFFICER = "\U0001f46e\u200d\u2640\ufe0f" - WOMAN_POLICE_OFFICER_LIGHT_SKIN_TONE = "\U0001f46e\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_POLICE_OFFICER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f46e\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_POLICE_OFFICER_MEDIUM_SKIN_TONE = "\U0001f46e\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_POLICE_OFFICER_MEDIUM_DARK_SKIN_TONE = "\U0001f46e\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_POLICE_OFFICER_DARK_SKIN_TONE = "\U0001f46e\U0001f3ff\u200d\u2640\ufe0f" - DETECTIVE = "\U0001f575\ufe0f" - DETECTIVE_LIGHT_SKIN_TONE = "\U0001f575\U0001f3fb" - DETECTIVE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f575\U0001f3fc" - DETECTIVE_MEDIUM_SKIN_TONE = "\U0001f575\U0001f3fd" - DETECTIVE_MEDIUM_DARK_SKIN_TONE = "\U0001f575\U0001f3fe" - DETECTIVE_DARK_SKIN_TONE = "\U0001f575\U0001f3ff" - MAN_DETECTIVE = "\U0001f575\ufe0f\u200d\u2642\ufe0f" - MAN_DETECTIVE_LIGHT_SKIN_TONE = "\U0001f575\U0001f3fb\u200d\u2642\ufe0f" - MAN_DETECTIVE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f575\U0001f3fc\u200d\u2642\ufe0f" - MAN_DETECTIVE_MEDIUM_SKIN_TONE = "\U0001f575\U0001f3fd\u200d\u2642\ufe0f" - MAN_DETECTIVE_MEDIUM_DARK_SKIN_TONE = "\U0001f575\U0001f3fe\u200d\u2642\ufe0f" - MAN_DETECTIVE_DARK_SKIN_TONE = "\U0001f575\U0001f3ff\u200d\u2642\ufe0f" - WOMAN_DETECTIVE = "\U0001f575\ufe0f\u200d\u2640\ufe0f" - WOMAN_DETECTIVE_LIGHT_SKIN_TONE = "\U0001f575\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_DETECTIVE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f575\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_DETECTIVE_MEDIUM_SKIN_TONE = "\U0001f575\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_DETECTIVE_MEDIUM_DARK_SKIN_TONE = "\U0001f575\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_DETECTIVE_DARK_SKIN_TONE = "\U0001f575\U0001f3ff\u200d\u2640\ufe0f" - GUARD = "\U0001f482" - GUARD_LIGHT_SKIN_TONE = "\U0001f482\U0001f3fb" - GUARD_MEDIUM_LIGHT_SKIN_TONE = "\U0001f482\U0001f3fc" - GUARD_MEDIUM_SKIN_TONE = "\U0001f482\U0001f3fd" - GUARD_MEDIUM_DARK_SKIN_TONE = "\U0001f482\U0001f3fe" - GUARD_DARK_SKIN_TONE = "\U0001f482\U0001f3ff" - MAN_GUARD = "\U0001f482\u200d\u2642\ufe0f" - MAN_GUARD_LIGHT_SKIN_TONE = "\U0001f482\U0001f3fb\u200d\u2642\ufe0f" - MAN_GUARD_MEDIUM_LIGHT_SKIN_TONE = "\U0001f482\U0001f3fc\u200d\u2642\ufe0f" - MAN_GUARD_MEDIUM_SKIN_TONE = "\U0001f482\U0001f3fd\u200d\u2642\ufe0f" - MAN_GUARD_MEDIUM_DARK_SKIN_TONE = "\U0001f482\U0001f3fe\u200d\u2642\ufe0f" - MAN_GUARD_DARK_SKIN_TONE = "\U0001f482\U0001f3ff\u200d\u2642\ufe0f" - WOMAN_GUARD = "\U0001f482\u200d\u2640\ufe0f" - WOMAN_GUARD_LIGHT_SKIN_TONE = "\U0001f482\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_GUARD_MEDIUM_LIGHT_SKIN_TONE = "\U0001f482\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_GUARD_MEDIUM_SKIN_TONE = "\U0001f482\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_GUARD_MEDIUM_DARK_SKIN_TONE = "\U0001f482\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_GUARD_DARK_SKIN_TONE = "\U0001f482\U0001f3ff\u200d\u2640\ufe0f" - CONSTRUCTION_WORKER = "\U0001f477" - CONSTRUCTION_WORKER_LIGHT_SKIN_TONE = "\U0001f477\U0001f3fb" - CONSTRUCTION_WORKER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f477\U0001f3fc" - CONSTRUCTION_WORKER_MEDIUM_SKIN_TONE = "\U0001f477\U0001f3fd" - CONSTRUCTION_WORKER_MEDIUM_DARK_SKIN_TONE = "\U0001f477\U0001f3fe" - CONSTRUCTION_WORKER_DARK_SKIN_TONE = "\U0001f477\U0001f3ff" - MAN_CONSTRUCTION_WORKER = "\U0001f477\u200d\u2642\ufe0f" - MAN_CONSTRUCTION_WORKER_LIGHT_SKIN_TONE = "\U0001f477\U0001f3fb\u200d\u2642\ufe0f" - MAN_CONSTRUCTION_WORKER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f477\U0001f3fc\u200d\u2642\ufe0f" - MAN_CONSTRUCTION_WORKER_MEDIUM_SKIN_TONE = "\U0001f477\U0001f3fd\u200d\u2642\ufe0f" - MAN_CONSTRUCTION_WORKER_MEDIUM_DARK_SKIN_TONE = "\U0001f477\U0001f3fe\u200d\u2642\ufe0f" - MAN_CONSTRUCTION_WORKER_DARK_SKIN_TONE = "\U0001f477\U0001f3ff\u200d\u2642\ufe0f" - WOMAN_CONSTRUCTION_WORKER = "\U0001f477\u200d\u2640\ufe0f" - WOMAN_CONSTRUCTION_WORKER_LIGHT_SKIN_TONE = "\U0001f477\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_CONSTRUCTION_WORKER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f477\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_CONSTRUCTION_WORKER_MEDIUM_SKIN_TONE = "\U0001f477\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_CONSTRUCTION_WORKER_MEDIUM_DARK_SKIN_TONE = "\U0001f477\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_CONSTRUCTION_WORKER_DARK_SKIN_TONE = "\U0001f477\U0001f3ff\u200d\u2640\ufe0f" - PRINCE = "\U0001f934" - PRINCE_LIGHT_SKIN_TONE = "\U0001f934\U0001f3fb" - PRINCE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f934\U0001f3fc" - PRINCE_MEDIUM_SKIN_TONE = "\U0001f934\U0001f3fd" - PRINCE_MEDIUM_DARK_SKIN_TONE = "\U0001f934\U0001f3fe" - PRINCE_DARK_SKIN_TONE = "\U0001f934\U0001f3ff" - PRINCESS = "\U0001f478" - PRINCESS_LIGHT_SKIN_TONE = "\U0001f478\U0001f3fb" - PRINCESS_MEDIUM_LIGHT_SKIN_TONE = "\U0001f478\U0001f3fc" - PRINCESS_MEDIUM_SKIN_TONE = "\U0001f478\U0001f3fd" - PRINCESS_MEDIUM_DARK_SKIN_TONE = "\U0001f478\U0001f3fe" - PRINCESS_DARK_SKIN_TONE = "\U0001f478\U0001f3ff" - PERSON_WEARING_TURBAN = "\U0001f473" - PERSON_WEARING_TURBAN_LIGHT_SKIN_TONE = "\U0001f473\U0001f3fb" - PERSON_WEARING_TURBAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f473\U0001f3fc" - PERSON_WEARING_TURBAN_MEDIUM_SKIN_TONE = "\U0001f473\U0001f3fd" - PERSON_WEARING_TURBAN_MEDIUM_DARK_SKIN_TONE = "\U0001f473\U0001f3fe" - PERSON_WEARING_TURBAN_DARK_SKIN_TONE = "\U0001f473\U0001f3ff" - MAN_WEARING_TURBAN = "\U0001f473\u200d\u2642\ufe0f" - MAN_WEARING_TURBAN_LIGHT_SKIN_TONE = "\U0001f473\U0001f3fb\u200d\u2642\ufe0f" - MAN_WEARING_TURBAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f473\U0001f3fc\u200d\u2642\ufe0f" - MAN_WEARING_TURBAN_MEDIUM_SKIN_TONE = "\U0001f473\U0001f3fd\u200d\u2642\ufe0f" - MAN_WEARING_TURBAN_MEDIUM_DARK_SKIN_TONE = "\U0001f473\U0001f3fe\u200d\u2642\ufe0f" - MAN_WEARING_TURBAN_DARK_SKIN_TONE = "\U0001f473\U0001f3ff\u200d\u2642\ufe0f" - WOMAN_WEARING_TURBAN = "\U0001f473\u200d\u2640\ufe0f" - WOMAN_WEARING_TURBAN_LIGHT_SKIN_TONE = "\U0001f473\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_WEARING_TURBAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f473\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_WEARING_TURBAN_MEDIUM_SKIN_TONE = "\U0001f473\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_WEARING_TURBAN_MEDIUM_DARK_SKIN_TONE = "\U0001f473\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_WEARING_TURBAN_DARK_SKIN_TONE = "\U0001f473\U0001f3ff\u200d\u2640\ufe0f" - MAN_WITH_CHINESE_CAP = "\U0001f472" - MAN_WITH_CHINESE_CAP_LIGHT_SKIN_TONE = "\U0001f472\U0001f3fb" - MAN_WITH_CHINESE_CAP_MEDIUM_LIGHT_SKIN_TONE = "\U0001f472\U0001f3fc" - MAN_WITH_CHINESE_CAP_MEDIUM_SKIN_TONE = "\U0001f472\U0001f3fd" - MAN_WITH_CHINESE_CAP_MEDIUM_DARK_SKIN_TONE = "\U0001f472\U0001f3fe" - MAN_WITH_CHINESE_CAP_DARK_SKIN_TONE = "\U0001f472\U0001f3ff" - WOMAN_WITH_HEADSCARF = "\U0001f9d5" - PERSON_WITH_HEADSCARF_LIGHT_SKIN_TONE = "\U0001f9d5\U0001f3fb" - PERSON_WITH_HEADSCARF_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d5\U0001f3fc" - PERSON_WITH_HEADSCARF_MEDIUM_SKIN_TONE = "\U0001f9d5\U0001f3fd" - PERSON_WITH_HEADSCARF_MEDIUM_DARK_SKIN_TONE = "\U0001f9d5\U0001f3fe" - PERSON_WITH_HEADSCARF_DARK_SKIN_TONE = "\U0001f9d5\U0001f3ff" - MAN_BEARD = "\U0001f9d4" - BEARDED_PERSON_LIGHT_SKIN_TONE = "\U0001f9d4\U0001f3fb" - BEARDED_PERSON_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d4\U0001f3fc" - BEARDED_PERSON_MEDIUM_SKIN_TONE = "\U0001f9d4\U0001f3fd" - BEARDED_PERSON_MEDIUM_DARK_SKIN_TONE = "\U0001f9d4\U0001f3fe" - BEARDED_PERSON_DARK_SKIN_TONE = "\U0001f9d4\U0001f3ff" - PERSON_BLOND_HAIR = "\U0001f471" - BLOND_HAIRED_PERSON_LIGHT_SKIN_TONE = "\U0001f471\U0001f3fb" - BLOND_HAIRED_PERSON_MEDIUM_LIGHT_SKIN_TONE = "\U0001f471\U0001f3fc" - BLOND_HAIRED_PERSON_MEDIUM_SKIN_TONE = "\U0001f471\U0001f3fd" - BLOND_HAIRED_PERSON_MEDIUM_DARK_SKIN_TONE = "\U0001f471\U0001f3fe" - BLOND_HAIRED_PERSON_DARK_SKIN_TONE = "\U0001f471\U0001f3ff" - MAN_BLOND_HAIR = "\U0001f471\u200d\u2642\ufe0f" - BLOND_HAIRED_MAN_LIGHT_SKIN_TONE = "\U0001f471\U0001f3fb\u200d\u2642\ufe0f" - BLOND_HAIRED_MAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f471\U0001f3fc\u200d\u2642\ufe0f" - BLOND_HAIRED_MAN_MEDIUM_SKIN_TONE = "\U0001f471\U0001f3fd\u200d\u2642\ufe0f" - BLOND_HAIRED_MAN_MEDIUM_DARK_SKIN_TONE = "\U0001f471\U0001f3fe\u200d\u2642\ufe0f" - BLOND_HAIRED_MAN_DARK_SKIN_TONE = "\U0001f471\U0001f3ff\u200d\u2642\ufe0f" - WOMAN_BLOND_HAIR = "\U0001f471\u200d\u2640\ufe0f" - BLOND_HAIRED_WOMAN_LIGHT_SKIN_TONE = "\U0001f471\U0001f3fb\u200d\u2640\ufe0f" - BLOND_HAIRED_WOMAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f471\U0001f3fc\u200d\u2640\ufe0f" - BLOND_HAIRED_WOMAN_MEDIUM_SKIN_TONE = "\U0001f471\U0001f3fd\u200d\u2640\ufe0f" - BLOND_HAIRED_WOMAN_MEDIUM_DARK_SKIN_TONE = "\U0001f471\U0001f3fe\u200d\u2640\ufe0f" - BLOND_HAIRED_WOMAN_DARK_SKIN_TONE = "\U0001f471\U0001f3ff\u200d\u2640\ufe0f" - MAN_RED_HAIRED = "\U0001f468\u200d\U0001f9b0" - MAN_RED_HAIRED_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f9b0" - MAN_RED_HAIRED_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f9b0" - MAN_RED_HAIRED_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f9b0" - MAN_RED_HAIRED_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f9b0" - MAN_RED_HAIRED_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f9b0" - WOMAN_RED_HAIRED = "\U0001f469\u200d\U0001f9b0" - WOMAN_RED_HAIRED_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f9b0" - WOMAN_RED_HAIRED_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f9b0" - WOMAN_RED_HAIRED_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f9b0" - WOMAN_RED_HAIRED_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f9b0" - WOMAN_RED_HAIRED_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f9b0" - MAN_CURLY_HAIRED = "\U0001f468\u200d\U0001f9b1" - MAN_CURLY_HAIRED_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f9b1" - MAN_CURLY_HAIRED_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f9b1" - MAN_CURLY_HAIRED_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f9b1" - MAN_CURLY_HAIRED_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f9b1" - MAN_CURLY_HAIRED_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f9b1" - WOMAN_CURLY_HAIRED = "\U0001f469\u200d\U0001f9b1" - WOMAN_CURLY_HAIRED_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f9b1" - WOMAN_CURLY_HAIRED_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f9b1" - WOMAN_CURLY_HAIRED_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f9b1" - WOMAN_CURLY_HAIRED_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f9b1" - WOMAN_CURLY_HAIRED_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f9b1" - MAN_BALD = "\U0001f468\u200d\U0001f9b2" - MAN_BALD_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f9b2" - MAN_BALD_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f9b2" - MAN_BALD_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f9b2" - MAN_BALD_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f9b2" - MAN_BALD_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f9b2" - WOMAN_BALD = "\U0001f469\u200d\U0001f9b2" - WOMAN_BALD_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f9b2" - WOMAN_BALD_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f9b2" - WOMAN_BALD_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f9b2" - WOMAN_BALD_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f9b2" - WOMAN_BALD_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f9b2" - MAN_WHITE_HAIRED = "\U0001f468\u200d\U0001f9b3" - MAN_WHITE_HAIRED_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f9b3" - MAN_WHITE_HAIRED_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f9b3" - MAN_WHITE_HAIRED_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f9b3" - MAN_WHITE_HAIRED_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f9b3" - MAN_WHITE_HAIRED_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f9b3" - WOMAN_WHITE_HAIRED = "\U0001f469\u200d\U0001f9b3" - WOMAN_WHITE_HAIRED_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f9b3" - WOMAN_WHITE_HAIRED_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f9b3" - WOMAN_WHITE_HAIRED_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f9b3" - WOMAN_WHITE_HAIRED_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f9b3" - WOMAN_WHITE_HAIRED_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f9b3" - MAN_IN_TUXEDO = "\U0001f935" - MAN_IN_TUXEDO_LIGHT_SKIN_TONE = "\U0001f935\U0001f3fb" - MAN_IN_TUXEDO_MEDIUM_LIGHT_SKIN_TONE = "\U0001f935\U0001f3fc" - MAN_IN_TUXEDO_MEDIUM_SKIN_TONE = "\U0001f935\U0001f3fd" - MAN_IN_TUXEDO_MEDIUM_DARK_SKIN_TONE = "\U0001f935\U0001f3fe" - MAN_IN_TUXEDO_DARK_SKIN_TONE = "\U0001f935\U0001f3ff" - BRIDE_WITH_VEIL = "\U0001f470" - BRIDE_WITH_VEIL_LIGHT_SKIN_TONE = "\U0001f470\U0001f3fb" - BRIDE_WITH_VEIL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f470\U0001f3fc" - BRIDE_WITH_VEIL_MEDIUM_SKIN_TONE = "\U0001f470\U0001f3fd" - BRIDE_WITH_VEIL_MEDIUM_DARK_SKIN_TONE = "\U0001f470\U0001f3fe" - BRIDE_WITH_VEIL_DARK_SKIN_TONE = "\U0001f470\U0001f3ff" - PREGNANT_WOMAN = "\U0001f930" - PREGNANT_WOMAN_LIGHT_SKIN_TONE = "\U0001f930\U0001f3fb" - PREGNANT_WOMAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f930\U0001f3fc" - PREGNANT_WOMAN_MEDIUM_SKIN_TONE = "\U0001f930\U0001f3fd" - PREGNANT_WOMAN_MEDIUM_DARK_SKIN_TONE = "\U0001f930\U0001f3fe" - PREGNANT_WOMAN_DARK_SKIN_TONE = "\U0001f930\U0001f3ff" - BREAST_FEEDING = "\U0001f931" - BREAST_FEEDING_LIGHT_SKIN_TONE = "\U0001f931\U0001f3fb" - BREAST_FEEDING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f931\U0001f3fc" - BREAST_FEEDING_MEDIUM_SKIN_TONE = "\U0001f931\U0001f3fd" - BREAST_FEEDING_MEDIUM_DARK_SKIN_TONE = "\U0001f931\U0001f3fe" - BREAST_FEEDING_DARK_SKIN_TONE = "\U0001f931\U0001f3ff" - BABY_ANGEL = "\U0001f47c" - BABY_ANGEL_LIGHT_SKIN_TONE = "\U0001f47c\U0001f3fb" - BABY_ANGEL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f47c\U0001f3fc" - BABY_ANGEL_MEDIUM_SKIN_TONE = "\U0001f47c\U0001f3fd" - BABY_ANGEL_MEDIUM_DARK_SKIN_TONE = "\U0001f47c\U0001f3fe" - BABY_ANGEL_DARK_SKIN_TONE = "\U0001f47c\U0001f3ff" - SANTA_CLAUS = "\U0001f385" - SANTA_CLAUS_LIGHT_SKIN_TONE = "\U0001f385\U0001f3fb" - SANTA_CLAUS_MEDIUM_LIGHT_SKIN_TONE = "\U0001f385\U0001f3fc" - SANTA_CLAUS_MEDIUM_SKIN_TONE = "\U0001f385\U0001f3fd" - SANTA_CLAUS_MEDIUM_DARK_SKIN_TONE = "\U0001f385\U0001f3fe" - SANTA_CLAUS_DARK_SKIN_TONE = "\U0001f385\U0001f3ff" - MRS_CLAUS = "\U0001f936" - MRS_CLAUS_LIGHT_SKIN_TONE = "\U0001f936\U0001f3fb" - MRS_CLAUS_MEDIUM_LIGHT_SKIN_TONE = "\U0001f936\U0001f3fc" - MRS_CLAUS_MEDIUM_SKIN_TONE = "\U0001f936\U0001f3fd" - MRS_CLAUS_MEDIUM_DARK_SKIN_TONE = "\U0001f936\U0001f3fe" - MRS_CLAUS_DARK_SKIN_TONE = "\U0001f936\U0001f3ff" - SUPERHERO = "\U0001f9b8" - SUPERHERO_LIGHT_SKIN_TONE = "\U0001f9b8\U0001f3fb" - SUPERHERO_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9b8\U0001f3fc" - SUPERHERO_MEDIUM_SKIN_TONE = "\U0001f9b8\U0001f3fd" - SUPERHERO_MEDIUM_DARK_SKIN_TONE = "\U0001f9b8\U0001f3fe" - SUPERHERO_DARK_SKIN_TONE = "\U0001f9b8\U0001f3ff" - WOMAN_SUPERHERO = "\U0001f9b8\u200d\u2640\ufe0f" - WOMAN_SUPERHERO_LIGHT_SKIN_TONE = "\U0001f9b8\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_SUPERHERO_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9b8\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_SUPERHERO_MEDIUM_SKIN_TONE = "\U0001f9b8\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_SUPERHERO_MEDIUM_DARK_SKIN_TONE = "\U0001f9b8\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_SUPERHERO_DARK_SKIN_TONE = "\U0001f9b8\U0001f3ff\u200d\u2640\ufe0f" - MAN_SUPERHERO = "\U0001f9b8\u200d\u2642\ufe0f" - MAN_SUPERHERO_LIGHT_SKIN_TONE = "\U0001f9b8\U0001f3fb\u200d\u2642\ufe0f" - MAN_SUPERHERO_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9b8\U0001f3fc\u200d\u2642\ufe0f" - MAN_SUPERHERO_MEDIUM_SKIN_TONE = "\U0001f9b8\U0001f3fd\u200d\u2642\ufe0f" - MAN_SUPERHERO_MEDIUM_DARK_SKIN_TONE = "\U0001f9b8\U0001f3fe\u200d\u2642\ufe0f" - WOMAN_WITH_BUNNY_EARS_TYPE_1_2 = "\U0001f46f\U0001f3fb" - MAN_SUPERHERO_DARK_SKIN_TONE = "\U0001f9b8\U0001f3ff\u200d\u2642\ufe0f" - WOMAN_WITH_BUNNY_EARS_TYPE_3 = "\U0001f46f\U0001f3fc" - WOMAN_WITH_BUNNY_EARS_TYPE_4 = "\U0001f46f\U0001f3fd" - SUPERVILLAIN = "\U0001f9b9" - WOMAN_WITH_BUNNY_EARS_TYPE_5 = "\U0001f46f\U0001f3fe" - WOMAN_WITH_BUNNY_EARS_TYPE_6 = "\U0001f46f\U0001f3ff" - SUPERVILLAIN_LIGHT_SKIN_TONE = "\U0001f9b9\U0001f3fb" - SUPERVILLAIN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9b9\U0001f3fc" - MEN_WITH_BUNNY_EARS_PARTYING_TYPE_1_2 = "\U0001f46f\U0001f3fb\u200d\u2642\ufe0f" - SUPERVILLAIN_MEDIUM_SKIN_TONE = "\U0001f9b9\U0001f3fd" - MEN_WITH_BUNNY_EARS_PARTYING_TYPE_3 = "\U0001f46f\U0001f3fc\u200d\u2642\ufe0f" - SUPERVILLAIN_MEDIUM_DARK_SKIN_TONE = "\U0001f9b9\U0001f3fe" - MEN_WITH_BUNNY_EARS_PARTYING_TYPE_4 = "\U0001f46f\U0001f3fd\u200d\u2642\ufe0f" - SUPERVILLAIN_DARK_SKIN_TONE = "\U0001f9b9\U0001f3ff" - MEN_WITH_BUNNY_EARS_PARTYING_TYPE_5 = "\U0001f46f\U0001f3fe\u200d\u2642\ufe0f" - WOMAN_SUPERVILLAIN = "\U0001f9b9\u200d\u2640\ufe0f" - MEN_WITH_BUNNY_EARS_PARTYING_TYPE_6 = "\U0001f46f\U0001f3ff\u200d\u2642\ufe0f" - WOMAN_SUPERVILLAIN_LIGHT_SKIN_TONE = "\U0001f9b9\U0001f3fb\u200d\u2640\ufe0f" - WOMEN_WITH_BUNNY_EARS_PARTYING_TYPE_1_2 = "\U0001f46f\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_SUPERVILLAIN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9b9\U0001f3fc\u200d\u2640\ufe0f" - WOMEN_WITH_BUNNY_EARS_PARTYING_TYPE_3 = "\U0001f46f\U0001f3fc\u200d\u2640\ufe0f" - WOMEN_WITH_BUNNY_EARS_PARTYING_TYPE_4 = "\U0001f46f\U0001f3fd\u200d\u2640\ufe0f" - WOMEN_WITH_BUNNY_EARS_PARTYING_TYPE_5 = "\U0001f46f\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_SUPERVILLAIN_MEDIUM_SKIN_TONE = "\U0001f9b9\U0001f3fd\u200d\u2640\ufe0f" - WOMEN_WITH_BUNNY_EARS_PARTYING_TYPE_6 = "\U0001f46f\U0001f3ff\u200d\u2640\ufe0f" - WOMAN_SUPERVILLAIN_MEDIUM_DARK_SKIN_TONE = "\U0001f9b9\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_SUPERVILLAIN_DARK_SKIN_TONE = "\U0001f9b9\U0001f3ff\u200d\u2640\ufe0f" - MAN_SUPERVILLAIN = "\U0001f9b9\u200d\u2642\ufe0f" - MAN_SUPERVILLAIN_LIGHT_SKIN_TONE = "\U0001f9b9\U0001f3fb\u200d\u2642\ufe0f" - MAN_SUPERVILLAIN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9b9\U0001f3fc\u200d\u2642\ufe0f" - MAN_SUPERVILLAIN_MEDIUM_SKIN_TONE = "\U0001f9b9\U0001f3fd\u200d\u2642\ufe0f" - MAN_AND_WOMAN_HOLDING_HANDS_TYPE_1_2 = "\U0001f46b\U0001f3fb" - MAN_AND_WOMAN_HOLDING_HANDS_TYPE_3 = "\U0001f46b\U0001f3fc" - MAN_SUPERVILLAIN_MEDIUM_DARK_SKIN_TONE = "\U0001f9b9\U0001f3fe\u200d\u2642\ufe0f" - MAN_AND_WOMAN_HOLDING_HANDS_TYPE_4 = "\U0001f46b\U0001f3fd" - MAN_AND_WOMAN_HOLDING_HANDS_TYPE_5 = "\U0001f46b\U0001f3fe" - MAN_SUPERVILLAIN_DARK_SKIN_TONE = "\U0001f9b9\U0001f3ff\u200d\u2642\ufe0f" - MAN_AND_WOMAN_HOLDING_HANDS_TYPE_6 = "\U0001f46b\U0001f3ff" - MAGE = "\U0001f9d9" - TWO_MEN_HOLDING_HANDS_TYPE_1_2 = "\U0001f46c\U0001f3fb" - TWO_MEN_HOLDING_HANDS_TYPE_3 = "\U0001f46c\U0001f3fc" - MAGE_LIGHT_SKIN_TONE = "\U0001f9d9\U0001f3fb" - TWO_MEN_HOLDING_HANDS_TYPE_4 = "\U0001f46c\U0001f3fd" - MAGE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d9\U0001f3fc" - TWO_MEN_HOLDING_HANDS_TYPE_5 = "\U0001f46c\U0001f3fe" - MAGE_MEDIUM_SKIN_TONE = "\U0001f9d9\U0001f3fd" - TWO_MEN_HOLDING_HANDS_TYPE_6 = "\U0001f46c\U0001f3ff" - MAGE_MEDIUM_DARK_SKIN_TONE = "\U0001f9d9\U0001f3fe" - MAGE_DARK_SKIN_TONE = "\U0001f9d9\U0001f3ff" - WOMAN_MAGE = "\U0001f9d9\u200d\u2640\ufe0f" - TWO_WOMEN_HOLDING_HANDS_TYPE_1_2 = "\U0001f46d\U0001f3fb" - TWO_WOMEN_HOLDING_HANDS_TYPE_3 = "\U0001f46d\U0001f3fc" - WOMAN_MAGE_LIGHT_SKIN_TONE = "\U0001f9d9\U0001f3fb\u200d\u2640\ufe0f" - TWO_WOMEN_HOLDING_HANDS_TYPE_4 = "\U0001f46d\U0001f3fd" - TWO_WOMEN_HOLDING_HANDS_TYPE_5 = "\U0001f46d\U0001f3fe" - WOMAN_MAGE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d9\U0001f3fc\u200d\u2640\ufe0f" - TWO_WOMEN_HOLDING_HANDS_TYPE_6 = "\U0001f46d\U0001f3ff" - WOMAN_MAGE_MEDIUM_SKIN_TONE = "\U0001f9d9\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_MAGE_MEDIUM_DARK_SKIN_TONE = "\U0001f9d9\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_MAGE_DARK_SKIN_TONE = "\U0001f9d9\U0001f3ff\u200d\u2640\ufe0f" - MAN_MAGE = "\U0001f9d9\u200d\u2642\ufe0f" - MAN_MAGE_LIGHT_SKIN_TONE = "\U0001f9d9\U0001f3fb\u200d\u2642\ufe0f" - FAMILY_TYPE_1_2 = "\U0001f46a\U0001f3fb" - FAMILY_TYPE_3 = "\U0001f46a\U0001f3fc" - MAN_MAGE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d9\U0001f3fc\u200d\u2642\ufe0f" - FAMILY_TYPE_4 = "\U0001f46a\U0001f3fd" - FAMILY_TYPE_5 = "\U0001f46a\U0001f3fe" - MAN_MAGE_MEDIUM_SKIN_TONE = "\U0001f9d9\U0001f3fd\u200d\u2642\ufe0f" - FAMILY_TYPE_6 = "\U0001f46a\U0001f3ff" - MAN_MAGE_MEDIUM_DARK_SKIN_TONE = "\U0001f9d9\U0001f3fe\u200d\u2642\ufe0f" - MAN_MAGE_DARK_SKIN_TONE = "\U0001f9d9\U0001f3ff\u200d\u2642\ufe0f" - FAIRY = "\U0001f9da" - FAIRY_LIGHT_SKIN_TONE = "\U0001f9da\U0001f3fb" - FAIRY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9da\U0001f3fc" - FAIRY_MEDIUM_SKIN_TONE = "\U0001f9da\U0001f3fd" - FAIRY_MEDIUM_DARK_SKIN_TONE = "\U0001f9da\U0001f3fe" - FAIRY_DARK_SKIN_TONE = "\U0001f9da\U0001f3ff" - WOMAN_FAIRY = "\U0001f9da\u200d\u2640\ufe0f" - WOMAN_FAIRY_LIGHT_SKIN_TONE = "\U0001f9da\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_FAIRY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9da\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_FAIRY_MEDIUM_SKIN_TONE = "\U0001f9da\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_FAIRY_MEDIUM_DARK_SKIN_TONE = "\U0001f9da\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_FAIRY_DARK_SKIN_TONE = "\U0001f9da\U0001f3ff\u200d\u2640\ufe0f" - MAN_FAIRY = "\U0001f9da\u200d\u2642\ufe0f" - MAN_FAIRY_LIGHT_SKIN_TONE = "\U0001f9da\U0001f3fb\u200d\u2642\ufe0f" - MAN_FAIRY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9da\U0001f3fc\u200d\u2642\ufe0f" - MAN_FAIRY_MEDIUM_SKIN_TONE = "\U0001f9da\U0001f3fd\u200d\u2642\ufe0f" - MAN_FAIRY_MEDIUM_DARK_SKIN_TONE = "\U0001f9da\U0001f3fe\u200d\u2642\ufe0f" - MAN_FAIRY_DARK_SKIN_TONE = "\U0001f9da\U0001f3ff\u200d\u2642\ufe0f" - VAMPIRE = "\U0001f9db" - VAMPIRE_LIGHT_SKIN_TONE = "\U0001f9db\U0001f3fb" - VAMPIRE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9db\U0001f3fc" - VAMPIRE_MEDIUM_SKIN_TONE = "\U0001f9db\U0001f3fd" - VAMPIRE_MEDIUM_DARK_SKIN_TONE = "\U0001f9db\U0001f3fe" - VAMPIRE_DARK_SKIN_TONE = "\U0001f9db\U0001f3ff" - WOMAN_VAMPIRE = "\U0001f9db\u200d\u2640\ufe0f" - WOMAN_VAMPIRE_LIGHT_SKIN_TONE = "\U0001f9db\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_VAMPIRE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9db\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_VAMPIRE_MEDIUM_SKIN_TONE = "\U0001f9db\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_VAMPIRE_MEDIUM_DARK_SKIN_TONE = "\U0001f9db\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_VAMPIRE_DARK_SKIN_TONE = "\U0001f9db\U0001f3ff\u200d\u2640\ufe0f" - MAN_VAMPIRE = "\U0001f9db\u200d\u2642\ufe0f" - MAN_VAMPIRE_LIGHT_SKIN_TONE = "\U0001f9db\U0001f3fb\u200d\u2642\ufe0f" - MAN_VAMPIRE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9db\U0001f3fc\u200d\u2642\ufe0f" - MAN_VAMPIRE_MEDIUM_SKIN_TONE = "\U0001f9db\U0001f3fd\u200d\u2642\ufe0f" - MAN_VAMPIRE_MEDIUM_DARK_SKIN_TONE = "\U0001f9db\U0001f3fe\u200d\u2642\ufe0f" - MAN_VAMPIRE_DARK_SKIN_TONE = "\U0001f9db\U0001f3ff\u200d\u2642\ufe0f" - MERPERSON = "\U0001f9dc" - MERPERSON_LIGHT_SKIN_TONE = "\U0001f9dc\U0001f3fb" - MERPERSON_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9dc\U0001f3fc" - MERPERSON_MEDIUM_SKIN_TONE = "\U0001f9dc\U0001f3fd" - MERPERSON_MEDIUM_DARK_SKIN_TONE = "\U0001f9dc\U0001f3fe" - MERPERSON_DARK_SKIN_TONE = "\U0001f9dc\U0001f3ff" - MERMAID = "\U0001f9dc\u200d\u2640\ufe0f" - MERMAID_LIGHT_SKIN_TONE = "\U0001f9dc\U0001f3fb\u200d\u2640\ufe0f" - MERMAID_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9dc\U0001f3fc\u200d\u2640\ufe0f" - MERMAID_MEDIUM_SKIN_TONE = "\U0001f9dc\U0001f3fd\u200d\u2640\ufe0f" - MERMAID_MEDIUM_DARK_SKIN_TONE = "\U0001f9dc\U0001f3fe\u200d\u2640\ufe0f" - MERMAID_DARK_SKIN_TONE = "\U0001f9dc\U0001f3ff\u200d\u2640\ufe0f" - MERMAN = "\U0001f9dc\u200d\u2642\ufe0f" - MERMAN_LIGHT_SKIN_TONE = "\U0001f9dc\U0001f3fb\u200d\u2642\ufe0f" - MERMAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9dc\U0001f3fc\u200d\u2642\ufe0f" - MERMAN_MEDIUM_SKIN_TONE = "\U0001f9dc\U0001f3fd\u200d\u2642\ufe0f" - MERMAN_MEDIUM_DARK_SKIN_TONE = "\U0001f9dc\U0001f3fe\u200d\u2642\ufe0f" - MERMAN_DARK_SKIN_TONE = "\U0001f9dc\U0001f3ff\u200d\u2642\ufe0f" - ELF = "\U0001f9dd" - ELF_LIGHT_SKIN_TONE = "\U0001f9dd\U0001f3fb" - ELF_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9dd\U0001f3fc" - ELF_MEDIUM_SKIN_TONE = "\U0001f9dd\U0001f3fd" - ELF_MEDIUM_DARK_SKIN_TONE = "\U0001f9dd\U0001f3fe" - ELF_DARK_SKIN_TONE = "\U0001f9dd\U0001f3ff" - WOMAN_ELF = "\U0001f9dd\u200d\u2640\ufe0f" - WOMAN_ELF_LIGHT_SKIN_TONE = "\U0001f9dd\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_ELF_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9dd\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_ELF_MEDIUM_SKIN_TONE = "\U0001f9dd\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_ELF_MEDIUM_DARK_SKIN_TONE = "\U0001f9dd\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_ELF_DARK_SKIN_TONE = "\U0001f9dd\U0001f3ff\u200d\u2640\ufe0f" - MAN_ELF = "\U0001f9dd\u200d\u2642\ufe0f" - MAN_ELF_LIGHT_SKIN_TONE = "\U0001f9dd\U0001f3fb\u200d\u2642\ufe0f" - MAN_ELF_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9dd\U0001f3fc\u200d\u2642\ufe0f" - MAN_ELF_MEDIUM_SKIN_TONE = "\U0001f9dd\U0001f3fd\u200d\u2642\ufe0f" - MAN_ELF_MEDIUM_DARK_SKIN_TONE = "\U0001f9dd\U0001f3fe\u200d\u2642\ufe0f" - MAN_ELF_DARK_SKIN_TONE = "\U0001f9dd\U0001f3ff\u200d\u2642\ufe0f" - GENIE = "\U0001f9de" - WOMAN_GENIE = "\U0001f9de\u200d\u2640\ufe0f" - MAN_GENIE = "\U0001f9de\u200d\u2642\ufe0f" - ZOMBIE = "\U0001f9df" - ZOMBIE_LIGHT_SKIN_TONE = "\U0001f9df\U0001f3fb" - ZOMBIE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9df\U0001f3fc" - ZOMBIE_MEDIUM_SKIN_TONE = "\U0001f9df\U0001f3fd" - ZOMBIE_MEDIUM_DARK_SKIN_TONE = "\U0001f9df\U0001f3fe" - ZOMBIE_DARK_SKIN_TONE = "\U0001f9df\U0001f3ff" - WOMAN_ZOMBIE = "\U0001f9df\u200d\u2640\ufe0f" - MAN_ZOMBIE = "\U0001f9df\u200d\u2642\ufe0f" - PERSON_FROWNING = "\U0001f64d" - PERSON_FROWNING_LIGHT_SKIN_TONE = "\U0001f64d\U0001f3fb" - PERSON_FROWNING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f64d\U0001f3fc" - PERSON_FROWNING_MEDIUM_SKIN_TONE = "\U0001f64d\U0001f3fd" - PERSON_FROWNING_MEDIUM_DARK_SKIN_TONE = "\U0001f64d\U0001f3fe" - PERSON_FROWNING_DARK_SKIN_TONE = "\U0001f64d\U0001f3ff" - MAN_FROWNING = "\U0001f64d\u200d\u2642\ufe0f" - MAN_FROWNING_LIGHT_SKIN_TONE = "\U0001f64d\U0001f3fb\u200d\u2642\ufe0f" - MAN_FROWNING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f64d\U0001f3fc\u200d\u2642\ufe0f" - MAN_FROWNING_MEDIUM_SKIN_TONE = "\U0001f64d\U0001f3fd\u200d\u2642\ufe0f" - MAN_FROWNING_MEDIUM_DARK_SKIN_TONE = "\U0001f64d\U0001f3fe\u200d\u2642\ufe0f" - MAN_FROWNING_DARK_SKIN_TONE = "\U0001f64d\U0001f3ff\u200d\u2642\ufe0f" - WOMAN_FROWNING = "\U0001f64d\u200d\u2640\ufe0f" - WOMAN_FROWNING_LIGHT_SKIN_TONE = "\U0001f64d\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_FROWNING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f64d\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_FROWNING_MEDIUM_SKIN_TONE = "\U0001f64d\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_FROWNING_MEDIUM_DARK_SKIN_TONE = "\U0001f64d\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_FROWNING_DARK_SKIN_TONE = "\U0001f64d\U0001f3ff\u200d\u2640\ufe0f" - PERSON_POUTING = "\U0001f64e" - PERSON_POUTING_LIGHT_SKIN_TONE = "\U0001f64e\U0001f3fb" - PERSON_POUTING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f64e\U0001f3fc" - PERSON_POUTING_MEDIUM_SKIN_TONE = "\U0001f64e\U0001f3fd" - PERSON_POUTING_MEDIUM_DARK_SKIN_TONE = "\U0001f64e\U0001f3fe" - PERSON_POUTING_DARK_SKIN_TONE = "\U0001f64e\U0001f3ff" - MAN_POUTING = "\U0001f64e\u200d\u2642\ufe0f" - MAN_POUTING_LIGHT_SKIN_TONE = "\U0001f64e\U0001f3fb\u200d\u2642\ufe0f" - MAN_POUTING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f64e\U0001f3fc\u200d\u2642\ufe0f" - MAN_POUTING_MEDIUM_SKIN_TONE = "\U0001f64e\U0001f3fd\u200d\u2642\ufe0f" - MAN_POUTING_MEDIUM_DARK_SKIN_TONE = "\U0001f64e\U0001f3fe\u200d\u2642\ufe0f" - MAN_POUTING_DARK_SKIN_TONE = "\U0001f64e\U0001f3ff\u200d\u2642\ufe0f" - WOMAN_POUTING = "\U0001f64e\u200d\u2640\ufe0f" - WOMAN_POUTING_LIGHT_SKIN_TONE = "\U0001f64e\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_POUTING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f64e\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_POUTING_MEDIUM_SKIN_TONE = "\U0001f64e\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_POUTING_MEDIUM_DARK_SKIN_TONE = "\U0001f64e\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_POUTING_DARK_SKIN_TONE = "\U0001f64e\U0001f3ff\u200d\u2640\ufe0f" - PERSON_GESTURING_NO = "\U0001f645" - PERSON_GESTURING_NO_LIGHT_SKIN_TONE = "\U0001f645\U0001f3fb" - PERSON_GESTURING_NO_MEDIUM_LIGHT_SKIN_TONE = "\U0001f645\U0001f3fc" - PERSON_GESTURING_NO_MEDIUM_SKIN_TONE = "\U0001f645\U0001f3fd" - PERSON_GESTURING_NO_MEDIUM_DARK_SKIN_TONE = "\U0001f645\U0001f3fe" - PERSON_GESTURING_NO_DARK_SKIN_TONE = "\U0001f645\U0001f3ff" - MAN_GESTURING_NO = "\U0001f645\u200d\u2642\ufe0f" - MAN_GESTURING_NO_LIGHT_SKIN_TONE = "\U0001f645\U0001f3fb\u200d\u2642\ufe0f" - MAN_GESTURING_NO_MEDIUM_LIGHT_SKIN_TONE = "\U0001f645\U0001f3fc\u200d\u2642\ufe0f" - HANDSHAKE_TYPE_1_2 = "\U0001f91d\U0001f3fb" - HANDSHAKE_TYPE_3 = "\U0001f91d\U0001f3fc" - MAN_GESTURING_NO_MEDIUM_SKIN_TONE = "\U0001f645\U0001f3fd\u200d\u2642\ufe0f" - HANDSHAKE_TYPE_4 = "\U0001f91d\U0001f3fd" - HANDSHAKE_TYPE_5 = "\U0001f91d\U0001f3fe" - MAN_GESTURING_NO_MEDIUM_DARK_SKIN_TONE = "\U0001f645\U0001f3fe\u200d\u2642\ufe0f" - HANDSHAKE_TYPE_6 = "\U0001f91d\U0001f3ff" - MAN_GESTURING_NO_DARK_SKIN_TONE = "\U0001f645\U0001f3ff\u200d\u2642\ufe0f" - WOMAN_GESTURING_NO = "\U0001f645\u200d\u2640\ufe0f" - WOMAN_GESTURING_NO_LIGHT_SKIN_TONE = "\U0001f645\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_GESTURING_NO_MEDIUM_LIGHT_SKIN_TONE = "\U0001f645\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_GESTURING_NO_MEDIUM_SKIN_TONE = "\U0001f645\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_GESTURING_NO_MEDIUM_DARK_SKIN_TONE = "\U0001f645\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_GESTURING_NO_DARK_SKIN_TONE = "\U0001f645\U0001f3ff\u200d\u2640\ufe0f" - PERSON_GESTURING_OK = "\U0001f646" - PERSON_GESTURING_OK_LIGHT_SKIN_TONE = "\U0001f646\U0001f3fb" - PERSON_GESTURING_OK_MEDIUM_LIGHT_SKIN_TONE = "\U0001f646\U0001f3fc" - PERSON_GESTURING_OK_MEDIUM_SKIN_TONE = "\U0001f646\U0001f3fd" - PERSON_GESTURING_OK_MEDIUM_DARK_SKIN_TONE = "\U0001f646\U0001f3fe" - PERSON_GESTURING_OK_DARK_SKIN_TONE = "\U0001f646\U0001f3ff" - MAN_GESTURING_OK = "\U0001f646\u200d\u2642\ufe0f" - MAN_GESTURING_OK_LIGHT_SKIN_TONE = "\U0001f646\U0001f3fb\u200d\u2642\ufe0f" - MAN_GESTURING_OK_MEDIUM_LIGHT_SKIN_TONE = "\U0001f646\U0001f3fc\u200d\u2642\ufe0f" - MAN_GESTURING_OK_MEDIUM_SKIN_TONE = "\U0001f646\U0001f3fd\u200d\u2642\ufe0f" - MAN_GESTURING_OK_MEDIUM_DARK_SKIN_TONE = "\U0001f646\U0001f3fe\u200d\u2642\ufe0f" - MAN_GESTURING_OK_DARK_SKIN_TONE = "\U0001f646\U0001f3ff\u200d\u2642\ufe0f" - WOMAN_GESTURING_OK = "\U0001f646\u200d\u2640\ufe0f" - WOMAN_GESTURING_OK_LIGHT_SKIN_TONE = "\U0001f646\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_GESTURING_OK_MEDIUM_LIGHT_SKIN_TONE = "\U0001f646\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_GESTURING_OK_MEDIUM_SKIN_TONE = "\U0001f646\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_GESTURING_OK_MEDIUM_DARK_SKIN_TONE = "\U0001f646\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_GESTURING_OK_DARK_SKIN_TONE = "\U0001f646\U0001f3ff\u200d\u2640\ufe0f" - PERSON_TIPPING_HAND = "\U0001f481" - PERSON_TIPPING_HAND_LIGHT_SKIN_TONE = "\U0001f481\U0001f3fb" - PERSON_TIPPING_HAND_MEDIUM_LIGHT_SKIN_TONE = "\U0001f481\U0001f3fc" - PERSON_TIPPING_HAND_MEDIUM_SKIN_TONE = "\U0001f481\U0001f3fd" - PERSON_TIPPING_HAND_MEDIUM_DARK_SKIN_TONE = "\U0001f481\U0001f3fe" - PERSON_TIPPING_HAND_DARK_SKIN_TONE = "\U0001f481\U0001f3ff" - MAN_TIPPING_HAND = "\U0001f481\u200d\u2642\ufe0f" - MAN_TIPPING_HAND_LIGHT_SKIN_TONE = "\U0001f481\U0001f3fb\u200d\u2642\ufe0f" - MAN_TIPPING_HAND_MEDIUM_LIGHT_SKIN_TONE = "\U0001f481\U0001f3fc\u200d\u2642\ufe0f" - MAN_TIPPING_HAND_MEDIUM_SKIN_TONE = "\U0001f481\U0001f3fd\u200d\u2642\ufe0f" - MAN_TIPPING_HAND_MEDIUM_DARK_SKIN_TONE = "\U0001f481\U0001f3fe\u200d\u2642\ufe0f" - MAN_TIPPING_HAND_DARK_SKIN_TONE = "\U0001f481\U0001f3ff\u200d\u2642\ufe0f" - WOMAN_TIPPING_HAND = "\U0001f481\u200d\u2640\ufe0f" - WOMAN_TIPPING_HAND_LIGHT_SKIN_TONE = "\U0001f481\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_TIPPING_HAND_MEDIUM_LIGHT_SKIN_TONE = "\U0001f481\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_TIPPING_HAND_MEDIUM_SKIN_TONE = "\U0001f481\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_TIPPING_HAND_MEDIUM_DARK_SKIN_TONE = "\U0001f481\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_TIPPING_HAND_DARK_SKIN_TONE = "\U0001f481\U0001f3ff\u200d\u2640\ufe0f" - PERSON_RAISING_HAND = "\U0001f64b" - PERSON_RAISING_HAND_LIGHT_SKIN_TONE = "\U0001f64b\U0001f3fb" - PERSON_RAISING_HAND_MEDIUM_LIGHT_SKIN_TONE = "\U0001f64b\U0001f3fc" - PERSON_RAISING_HAND_MEDIUM_SKIN_TONE = "\U0001f64b\U0001f3fd" - PERSON_RAISING_HAND_MEDIUM_DARK_SKIN_TONE = "\U0001f64b\U0001f3fe" - PERSON_RAISING_HAND_DARK_SKIN_TONE = "\U0001f64b\U0001f3ff" - MAN_RAISING_HAND = "\U0001f64b\u200d\u2642\ufe0f" - MAN_RAISING_HAND_LIGHT_SKIN_TONE = "\U0001f64b\U0001f3fb\u200d\u2642\ufe0f" - MAN_RAISING_HAND_MEDIUM_LIGHT_SKIN_TONE = "\U0001f64b\U0001f3fc\u200d\u2642\ufe0f" - MAN_RAISING_HAND_MEDIUM_SKIN_TONE = "\U0001f64b\U0001f3fd\u200d\u2642\ufe0f" - MAN_RAISING_HAND_MEDIUM_DARK_SKIN_TONE = "\U0001f64b\U0001f3fe\u200d\u2642\ufe0f" - MAN_RAISING_HAND_DARK_SKIN_TONE = "\U0001f64b\U0001f3ff\u200d\u2642\ufe0f" - WOMAN_RAISING_HAND = "\U0001f64b\u200d\u2640\ufe0f" - WOMAN_RAISING_HAND_LIGHT_SKIN_TONE = "\U0001f64b\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_RAISING_HAND_MEDIUM_LIGHT_SKIN_TONE = "\U0001f64b\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_RAISING_HAND_MEDIUM_SKIN_TONE = "\U0001f64b\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_RAISING_HAND_MEDIUM_DARK_SKIN_TONE = "\U0001f64b\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_RAISING_HAND_DARK_SKIN_TONE = "\U0001f64b\U0001f3ff\u200d\u2640\ufe0f" - PERSON_BOWING = "\U0001f647" - PERSON_BOWING_LIGHT_SKIN_TONE = "\U0001f647\U0001f3fb" - PERSON_BOWING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f647\U0001f3fc" - PERSON_BOWING_MEDIUM_SKIN_TONE = "\U0001f647\U0001f3fd" - PERSON_BOWING_MEDIUM_DARK_SKIN_TONE = "\U0001f647\U0001f3fe" - PERSON_BOWING_DARK_SKIN_TONE = "\U0001f647\U0001f3ff" - MAN_BOWING = "\U0001f647\u200d\u2642\ufe0f" - MAN_BOWING_LIGHT_SKIN_TONE = "\U0001f647\U0001f3fb\u200d\u2642\ufe0f" - MAN_BOWING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f647\U0001f3fc\u200d\u2642\ufe0f" - MAN_BOWING_MEDIUM_SKIN_TONE = "\U0001f647\U0001f3fd\u200d\u2642\ufe0f" - MAN_BOWING_MEDIUM_DARK_SKIN_TONE = "\U0001f647\U0001f3fe\u200d\u2642\ufe0f" - MAN_BOWING_DARK_SKIN_TONE = "\U0001f647\U0001f3ff\u200d\u2642\ufe0f" - WOMAN_BOWING = "\U0001f647\u200d\u2640\ufe0f" - WOMAN_BOWING_LIGHT_SKIN_TONE = "\U0001f647\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_BOWING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f647\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_BOWING_MEDIUM_SKIN_TONE = "\U0001f647\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_BOWING_MEDIUM_DARK_SKIN_TONE = "\U0001f647\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_BOWING_DARK_SKIN_TONE = "\U0001f647\U0001f3ff\u200d\u2640\ufe0f" - PERSON_FACEPALMING = "\U0001f926" - PERSON_FACEPALMING_LIGHT_SKIN_TONE = "\U0001f926\U0001f3fb" - PERSON_FACEPALMING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f926\U0001f3fc" - PERSON_FACEPALMING_MEDIUM_SKIN_TONE = "\U0001f926\U0001f3fd" - PERSON_FACEPALMING_MEDIUM_DARK_SKIN_TONE = "\U0001f926\U0001f3fe" - PERSON_FACEPALMING_DARK_SKIN_TONE = "\U0001f926\U0001f3ff" - MAN_FACEPALMING = "\U0001f926\u200d\u2642\ufe0f" - MAN_FACEPALMING_LIGHT_SKIN_TONE = "\U0001f926\U0001f3fb\u200d\u2642\ufe0f" - MAN_FACEPALMING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f926\U0001f3fc\u200d\u2642\ufe0f" - MAN_FACEPALMING_MEDIUM_SKIN_TONE = "\U0001f926\U0001f3fd\u200d\u2642\ufe0f" - MAN_FACEPALMING_MEDIUM_DARK_SKIN_TONE = "\U0001f926\U0001f3fe\u200d\u2642\ufe0f" - MAN_FACEPALMING_DARK_SKIN_TONE = "\U0001f926\U0001f3ff\u200d\u2642\ufe0f" - WOMAN_FACEPALMING = "\U0001f926\u200d\u2640\ufe0f" - WOMAN_FACEPALMING_LIGHT_SKIN_TONE = "\U0001f926\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_FACEPALMING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f926\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_FACEPALMING_MEDIUM_SKIN_TONE = "\U0001f926\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_FACEPALMING_MEDIUM_DARK_SKIN_TONE = "\U0001f926\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_FACEPALMING_DARK_SKIN_TONE = "\U0001f926\U0001f3ff\u200d\u2640\ufe0f" - PERSON_SHRUGGING = "\U0001f937" - PERSON_SHRUGGING_LIGHT_SKIN_TONE = "\U0001f937\U0001f3fb" - PERSON_SHRUGGING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f937\U0001f3fc" - PERSON_SHRUGGING_MEDIUM_SKIN_TONE = "\U0001f937\U0001f3fd" - PERSON_SHRUGGING_MEDIUM_DARK_SKIN_TONE = "\U0001f937\U0001f3fe" - PERSON_SHRUGGING_DARK_SKIN_TONE = "\U0001f937\U0001f3ff" - MAN_SHRUGGING = "\U0001f937\u200d\u2642\ufe0f" - MAN_SHRUGGING_LIGHT_SKIN_TONE = "\U0001f937\U0001f3fb\u200d\u2642\ufe0f" - MAN_SHRUGGING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f937\U0001f3fc\u200d\u2642\ufe0f" - MAN_SHRUGGING_MEDIUM_SKIN_TONE = "\U0001f937\U0001f3fd\u200d\u2642\ufe0f" - MAN_SHRUGGING_MEDIUM_DARK_SKIN_TONE = "\U0001f937\U0001f3fe\u200d\u2642\ufe0f" - MAN_SHRUGGING_DARK_SKIN_TONE = "\U0001f937\U0001f3ff\u200d\u2642\ufe0f" - WOMAN_SHRUGGING = "\U0001f937\u200d\u2640\ufe0f" - WOMAN_SHRUGGING_LIGHT_SKIN_TONE = "\U0001f937\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_SHRUGGING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f937\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_SHRUGGING_MEDIUM_SKIN_TONE = "\U0001f937\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_SHRUGGING_MEDIUM_DARK_SKIN_TONE = "\U0001f937\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_SHRUGGING_DARK_SKIN_TONE = "\U0001f937\U0001f3ff\u200d\u2640\ufe0f" - PERSON_GETTING_MASSAGE = "\U0001f486" - PERSON_GETTING_MASSAGE_LIGHT_SKIN_TONE = "\U0001f486\U0001f3fb" - PERSON_GETTING_MASSAGE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f486\U0001f3fc" - PERSON_GETTING_MASSAGE_MEDIUM_SKIN_TONE = "\U0001f486\U0001f3fd" - PERSON_GETTING_MASSAGE_MEDIUM_DARK_SKIN_TONE = "\U0001f486\U0001f3fe" - PERSON_GETTING_MASSAGE_DARK_SKIN_TONE = "\U0001f486\U0001f3ff" - MAN_GETTING_MASSAGE = "\U0001f486\u200d\u2642\ufe0f" - MAN_GETTING_MASSAGE_LIGHT_SKIN_TONE = "\U0001f486\U0001f3fb\u200d\u2642\ufe0f" - MAN_GETTING_MASSAGE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f486\U0001f3fc\u200d\u2642\ufe0f" - MAN_GETTING_MASSAGE_MEDIUM_SKIN_TONE = "\U0001f486\U0001f3fd\u200d\u2642\ufe0f" - MAN_GETTING_MASSAGE_MEDIUM_DARK_SKIN_TONE = "\U0001f486\U0001f3fe\u200d\u2642\ufe0f" - MAN_GETTING_MASSAGE_DARK_SKIN_TONE = "\U0001f486\U0001f3ff\u200d\u2642\ufe0f" - WOMAN_GETTING_MASSAGE = "\U0001f486\u200d\u2640\ufe0f" - WOMAN_GETTING_MASSAGE_LIGHT_SKIN_TONE = "\U0001f486\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_GETTING_MASSAGE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f486\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_GETTING_MASSAGE_MEDIUM_SKIN_TONE = "\U0001f486\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_GETTING_MASSAGE_MEDIUM_DARK_SKIN_TONE = "\U0001f486\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_GETTING_MASSAGE_DARK_SKIN_TONE = "\U0001f486\U0001f3ff\u200d\u2640\ufe0f" - PERSON_GETTING_HAIRCUT = "\U0001f487" - PERSON_GETTING_HAIRCUT_LIGHT_SKIN_TONE = "\U0001f487\U0001f3fb" - PERSON_GETTING_HAIRCUT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f487\U0001f3fc" - PERSON_GETTING_HAIRCUT_MEDIUM_SKIN_TONE = "\U0001f487\U0001f3fd" - PERSON_GETTING_HAIRCUT_MEDIUM_DARK_SKIN_TONE = "\U0001f487\U0001f3fe" - PERSON_GETTING_HAIRCUT_DARK_SKIN_TONE = "\U0001f487\U0001f3ff" - MAN_GETTING_HAIRCUT = "\U0001f487\u200d\u2642\ufe0f" - MAN_GETTING_HAIRCUT_LIGHT_SKIN_TONE = "\U0001f487\U0001f3fb\u200d\u2642\ufe0f" - MAN_GETTING_HAIRCUT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f487\U0001f3fc\u200d\u2642\ufe0f" - MAN_GETTING_HAIRCUT_MEDIUM_SKIN_TONE = "\U0001f487\U0001f3fd\u200d\u2642\ufe0f" - MAN_GETTING_HAIRCUT_MEDIUM_DARK_SKIN_TONE = "\U0001f487\U0001f3fe\u200d\u2642\ufe0f" - MAN_GETTING_HAIRCUT_DARK_SKIN_TONE = "\U0001f487\U0001f3ff\u200d\u2642\ufe0f" - WOMAN_GETTING_HAIRCUT = "\U0001f487\u200d\u2640\ufe0f" - WOMAN_GETTING_HAIRCUT_LIGHT_SKIN_TONE = "\U0001f487\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_GETTING_HAIRCUT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f487\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_GETTING_HAIRCUT_MEDIUM_SKIN_TONE = "\U0001f487\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_GETTING_HAIRCUT_MEDIUM_DARK_SKIN_TONE = "\U0001f487\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_GETTING_HAIRCUT_DARK_SKIN_TONE = "\U0001f487\U0001f3ff\u200d\u2640\ufe0f" - PERSON_WALKING = "\U0001f6b6" - PERSON_WALKING_LIGHT_SKIN_TONE = "\U0001f6b6\U0001f3fb" - PERSON_WALKING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f6b6\U0001f3fc" - PERSON_WALKING_MEDIUM_SKIN_TONE = "\U0001f6b6\U0001f3fd" - PERSON_WALKING_MEDIUM_DARK_SKIN_TONE = "\U0001f6b6\U0001f3fe" - PERSON_WALKING_DARK_SKIN_TONE = "\U0001f6b6\U0001f3ff" - MAN_WALKING = "\U0001f6b6\u200d\u2642\ufe0f" - MAN_WALKING_LIGHT_SKIN_TONE = "\U0001f6b6\U0001f3fb\u200d\u2642\ufe0f" - MAN_WALKING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f6b6\U0001f3fc\u200d\u2642\ufe0f" - MAN_WALKING_MEDIUM_SKIN_TONE = "\U0001f6b6\U0001f3fd\u200d\u2642\ufe0f" - MAN_WALKING_MEDIUM_DARK_SKIN_TONE = "\U0001f6b6\U0001f3fe\u200d\u2642\ufe0f" - MAN_WALKING_DARK_SKIN_TONE = "\U0001f6b6\U0001f3ff\u200d\u2642\ufe0f" - WOMAN_WALKING = "\U0001f6b6\u200d\u2640\ufe0f" - WOMAN_WALKING_LIGHT_SKIN_TONE = "\U0001f6b6\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_WALKING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f6b6\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_WALKING_MEDIUM_SKIN_TONE = "\U0001f6b6\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_WALKING_MEDIUM_DARK_SKIN_TONE = "\U0001f6b6\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_WALKING_DARK_SKIN_TONE = "\U0001f6b6\U0001f3ff\u200d\u2640\ufe0f" - PERSON_RUNNING = "\U0001f3c3" - PERSON_RUNNING_LIGHT_SKIN_TONE = "\U0001f3c3\U0001f3fb" - PERSON_RUNNING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f3c3\U0001f3fc" - PERSON_RUNNING_MEDIUM_SKIN_TONE = "\U0001f3c3\U0001f3fd" - PERSON_RUNNING_MEDIUM_DARK_SKIN_TONE = "\U0001f3c3\U0001f3fe" - PERSON_RUNNING_DARK_SKIN_TONE = "\U0001f3c3\U0001f3ff" - MAN_RUNNING = "\U0001f3c3\u200d\u2642\ufe0f" - MAN_RUNNING_LIGHT_SKIN_TONE = "\U0001f3c3\U0001f3fb\u200d\u2642\ufe0f" - MAN_RUNNING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f3c3\U0001f3fc\u200d\u2642\ufe0f" - MAN_RUNNING_MEDIUM_SKIN_TONE = "\U0001f3c3\U0001f3fd\u200d\u2642\ufe0f" - MAN_RUNNING_MEDIUM_DARK_SKIN_TONE = "\U0001f3c3\U0001f3fe\u200d\u2642\ufe0f" - MAN_RUNNING_DARK_SKIN_TONE = "\U0001f3c3\U0001f3ff\u200d\u2642\ufe0f" - WOMAN_RUNNING = "\U0001f3c3\u200d\u2640\ufe0f" - WOMAN_RUNNING_LIGHT_SKIN_TONE = "\U0001f3c3\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_RUNNING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f3c3\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_RUNNING_MEDIUM_SKIN_TONE = "\U0001f3c3\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_RUNNING_MEDIUM_DARK_SKIN_TONE = "\U0001f3c3\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_RUNNING_DARK_SKIN_TONE = "\U0001f3c3\U0001f3ff\u200d\u2640\ufe0f" - WOMAN_DANCING = "\U0001f483" - WOMAN_DANCING_LIGHT_SKIN_TONE = "\U0001f483\U0001f3fb" - WOMAN_DANCING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f483\U0001f3fc" - WOMAN_DANCING_MEDIUM_SKIN_TONE = "\U0001f483\U0001f3fd" - WOMAN_DANCING_MEDIUM_DARK_SKIN_TONE = "\U0001f483\U0001f3fe" - WOMAN_DANCING_DARK_SKIN_TONE = "\U0001f483\U0001f3ff" - MAN_DANCING = "\U0001f57a" - MAN_DANCING_LIGHT_SKIN_TONE = "\U0001f57a\U0001f3fb" - MAN_DANCING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f57a\U0001f3fc" - MAN_DANCING_MEDIUM_SKIN_TONE = "\U0001f57a\U0001f3fd" - MAN_DANCING_MEDIUM_DARK_SKIN_TONE = "\U0001f57a\U0001f3fe" - MAN_DANCING_DARK_SKIN_TONE = "\U0001f57a\U0001f3ff" - PEOPLE_WITH_BUNNY_EARS = "\U0001f46f" - MEN_WITH_BUNNY_EARS = "\U0001f46f\u200d\u2642\ufe0f" - WOMEN_WITH_BUNNY_EARS = "\U0001f46f\u200d\u2640\ufe0f" - PERSON_IN_STEAMY_ROOM = "\U0001f9d6" - PERSON_IN_STEAMY_ROOM_LIGHT_SKIN_TONE = "\U0001f9d6\U0001f3fb" - PERSON_IN_STEAMY_ROOM_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d6\U0001f3fc" - PERSON_IN_STEAMY_ROOM_MEDIUM_SKIN_TONE = "\U0001f9d6\U0001f3fd" - PERSON_IN_STEAMY_ROOM_MEDIUM_DARK_SKIN_TONE = "\U0001f9d6\U0001f3fe" - PERSON_IN_STEAMY_ROOM_DARK_SKIN_TONE = "\U0001f9d6\U0001f3ff" - WOMAN_IN_STEAMY_ROOM = "\U0001f9d6\u200d\u2640\ufe0f" - WOMAN_IN_STEAMY_ROOM_LIGHT_SKIN_TONE = "\U0001f9d6\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_IN_STEAMY_ROOM_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d6\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_IN_STEAMY_ROOM_MEDIUM_SKIN_TONE = "\U0001f9d6\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_IN_STEAMY_ROOM_MEDIUM_DARK_SKIN_TONE = "\U0001f9d6\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_IN_STEAMY_ROOM_DARK_SKIN_TONE = "\U0001f9d6\U0001f3ff\u200d\u2640\ufe0f" - MAN_IN_STEAMY_ROOM = "\U0001f9d6\u200d\u2642\ufe0f" - MAN_IN_STEAMY_ROOM_LIGHT_SKIN_TONE = "\U0001f9d6\U0001f3fb\u200d\u2642\ufe0f" - MAN_IN_STEAMY_ROOM_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d6\U0001f3fc\u200d\u2642\ufe0f" - MAN_IN_STEAMY_ROOM_MEDIUM_SKIN_TONE = "\U0001f9d6\U0001f3fd\u200d\u2642\ufe0f" - MAN_IN_STEAMY_ROOM_MEDIUM_DARK_SKIN_TONE = "\U0001f9d6\U0001f3fe\u200d\u2642\ufe0f" - MAN_IN_STEAMY_ROOM_DARK_SKIN_TONE = "\U0001f9d6\U0001f3ff\u200d\u2642\ufe0f" - PERSON_CLIMBING = "\U0001f9d7" - PERSON_CLIMBING_LIGHT_SKIN_TONE = "\U0001f9d7\U0001f3fb" - PERSON_CLIMBING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d7\U0001f3fc" - PERSON_CLIMBING_MEDIUM_SKIN_TONE = "\U0001f9d7\U0001f3fd" - PERSON_CLIMBING_MEDIUM_DARK_SKIN_TONE = "\U0001f9d7\U0001f3fe" - PERSON_CLIMBING_DARK_SKIN_TONE = "\U0001f9d7\U0001f3ff" - WOMAN_CLIMBING = "\U0001f9d7\u200d\u2640\ufe0f" - WOMAN_CLIMBING_LIGHT_SKIN_TONE = "\U0001f9d7\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_CLIMBING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d7\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_CLIMBING_MEDIUM_SKIN_TONE = "\U0001f9d7\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_CLIMBING_MEDIUM_DARK_SKIN_TONE = "\U0001f9d7\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_CLIMBING_DARK_SKIN_TONE = "\U0001f9d7\U0001f3ff\u200d\u2640\ufe0f" - MAN_CLIMBING = "\U0001f9d7\u200d\u2642\ufe0f" - MAN_CLIMBING_LIGHT_SKIN_TONE = "\U0001f9d7\U0001f3fb\u200d\u2642\ufe0f" - MAN_CLIMBING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d7\U0001f3fc\u200d\u2642\ufe0f" - MAN_CLIMBING_MEDIUM_SKIN_TONE = "\U0001f9d7\U0001f3fd\u200d\u2642\ufe0f" - MAN_CLIMBING_MEDIUM_DARK_SKIN_TONE = "\U0001f9d7\U0001f3fe\u200d\u2642\ufe0f" - MAN_CLIMBING_DARK_SKIN_TONE = "\U0001f9d7\U0001f3ff\u200d\u2642\ufe0f" - PERSON_IN_LOTUS_POSITION = "\U0001f9d8" - PERSON_IN_LOTUS_POSITION_LIGHT_SKIN_TONE = "\U0001f9d8\U0001f3fb" - PERSON_IN_LOTUS_POSITION_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d8\U0001f3fc" - PERSON_IN_LOTUS_POSITION_MEDIUM_SKIN_TONE = "\U0001f9d8\U0001f3fd" - PERSON_IN_LOTUS_POSITION_MEDIUM_DARK_SKIN_TONE = "\U0001f9d8\U0001f3fe" - PERSON_IN_LOTUS_POSITION_DARK_SKIN_TONE = "\U0001f9d8\U0001f3ff" - WOMAN_IN_LOTUS_POSITION = "\U0001f9d8\u200d\u2640\ufe0f" - WOMAN_IN_LOTUS_POSITION_LIGHT_SKIN_TONE = "\U0001f9d8\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_IN_LOTUS_POSITION_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d8\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_IN_LOTUS_POSITION_MEDIUM_SKIN_TONE = "\U0001f9d8\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_IN_LOTUS_POSITION_MEDIUM_DARK_SKIN_TONE = "\U0001f9d8\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_IN_LOTUS_POSITION_DARK_SKIN_TONE = "\U0001f9d8\U0001f3ff\u200d\u2640\ufe0f" - MAN_IN_LOTUS_POSITION = "\U0001f9d8\u200d\u2642\ufe0f" - MAN_IN_LOTUS_POSITION_LIGHT_SKIN_TONE = "\U0001f9d8\U0001f3fb\u200d\u2642\ufe0f" - MAN_IN_LOTUS_POSITION_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d8\U0001f3fc\u200d\u2642\ufe0f" - MAN_IN_LOTUS_POSITION_MEDIUM_SKIN_TONE = "\U0001f9d8\U0001f3fd\u200d\u2642\ufe0f" - MAN_IN_LOTUS_POSITION_MEDIUM_DARK_SKIN_TONE = "\U0001f9d8\U0001f3fe\u200d\u2642\ufe0f" - MAN_IN_LOTUS_POSITION_DARK_SKIN_TONE = "\U0001f9d8\U0001f3ff\u200d\u2642\ufe0f" - PERSON_TAKING_BATH = "\U0001f6c0" - PERSON_TAKING_BATH_LIGHT_SKIN_TONE = "\U0001f6c0\U0001f3fb" - PERSON_TAKING_BATH_MEDIUM_LIGHT_SKIN_TONE = "\U0001f6c0\U0001f3fc" - PERSON_TAKING_BATH_MEDIUM_SKIN_TONE = "\U0001f6c0\U0001f3fd" - PERSON_TAKING_BATH_MEDIUM_DARK_SKIN_TONE = "\U0001f6c0\U0001f3fe" - PERSON_TAKING_BATH_DARK_SKIN_TONE = "\U0001f6c0\U0001f3ff" - PERSON_IN_BED = "\U0001f6cc" - PERSON_IN_BED_LIGHT_SKIN_TONE = "\U0001f6cc\U0001f3fb" - PERSON_IN_BED_MEDIUM_LIGHT_SKIN_TONE = "\U0001f6cc\U0001f3fc" - PERSON_IN_BED_MEDIUM_SKIN_TONE = "\U0001f6cc\U0001f3fd" - PERSON_IN_BED_MEDIUM_DARK_SKIN_TONE = "\U0001f6cc\U0001f3fe" - PERSON_IN_BED_DARK_SKIN_TONE = "\U0001f6cc\U0001f3ff" - MAN_IN_SUIT_LEVITATING = "\U0001f574\ufe0f" - MAN_IN_SUIT_LEVITATING_LIGHT_SKIN_TONE = "\U0001f574\U0001f3fb" - MAN_IN_SUIT_LEVITATING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f574\U0001f3fc" - MAN_IN_SUIT_LEVITATING_MEDIUM_SKIN_TONE = "\U0001f574\U0001f3fd" - MAN_IN_SUIT_LEVITATING_MEDIUM_DARK_SKIN_TONE = "\U0001f574\U0001f3fe" - MAN_IN_SUIT_LEVITATING_DARK_SKIN_TONE = "\U0001f574\U0001f3ff" - SPEAKING_HEAD = "\U0001f5e3\ufe0f" - BUST_IN_SILHOUETTE = "\U0001f464" - BUSTS_IN_SILHOUETTE = "\U0001f465" - PERSON_FENCING = "\U0001f93a" - HORSE_RACING = "\U0001f3c7" - HORSE_RACING_LIGHT_SKIN_TONE = "\U0001f3c7\U0001f3fb" - HORSE_RACING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f3c7\U0001f3fc" - HORSE_RACING_MEDIUM_SKIN_TONE = "\U0001f3c7\U0001f3fd" - HORSE_RACING_MEDIUM_DARK_SKIN_TONE = "\U0001f3c7\U0001f3fe" - HORSE_RACING_DARK_SKIN_TONE = "\U0001f3c7\U0001f3ff" - SKIER = "\u26f7\ufe0f" - SNOWBOARDER = "\U0001f3c2" - SNOWBOARDER_LIGHT_SKIN_TONE = "\U0001f3c2\U0001f3fb" - SNOWBOARDER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f3c2\U0001f3fc" - SNOWBOARDER_MEDIUM_SKIN_TONE = "\U0001f3c2\U0001f3fd" - SNOWBOARDER_MEDIUM_DARK_SKIN_TONE = "\U0001f3c2\U0001f3fe" - SNOWBOARDER_DARK_SKIN_TONE = "\U0001f3c2\U0001f3ff" - PERSON_GOLFING = "\U0001f3cc\ufe0f" - PERSON_GOLFING_LIGHT_SKIN_TONE = "\U0001f3cc\U0001f3fb" - PERSON_GOLFING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f3cc\U0001f3fc" - PERSON_GOLFING_MEDIUM_SKIN_TONE = "\U0001f3cc\U0001f3fd" - PERSON_GOLFING_MEDIUM_DARK_SKIN_TONE = "\U0001f3cc\U0001f3fe" - PERSON_GOLFING_DARK_SKIN_TONE = "\U0001f3cc\U0001f3ff" - MAN_GOLFING = "\U0001f3cc\ufe0f\u200d\u2642\ufe0f" - MAN_GOLFING_LIGHT_SKIN_TONE = "\U0001f3cc\U0001f3fb\u200d\u2642\ufe0f" - MAN_GOLFING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f3cc\U0001f3fc\u200d\u2642\ufe0f" - MAN_GOLFING_MEDIUM_SKIN_TONE = "\U0001f3cc\U0001f3fd\u200d\u2642\ufe0f" - MAN_GOLFING_MEDIUM_DARK_SKIN_TONE = "\U0001f3cc\U0001f3fe\u200d\u2642\ufe0f" - MAN_GOLFING_DARK_SKIN_TONE = "\U0001f3cc\U0001f3ff\u200d\u2642\ufe0f" - WOMAN_GOLFING = "\U0001f3cc\ufe0f\u200d\u2640\ufe0f" - WOMAN_GOLFING_LIGHT_SKIN_TONE = "\U0001f3cc\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_GOLFING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f3cc\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_GOLFING_MEDIUM_SKIN_TONE = "\U0001f3cc\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_GOLFING_MEDIUM_DARK_SKIN_TONE = "\U0001f3cc\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_GOLFING_DARK_SKIN_TONE = "\U0001f3cc\U0001f3ff\u200d\u2640\ufe0f" - PERSON_SURFING = "\U0001f3c4" - PERSON_SURFING_LIGHT_SKIN_TONE = "\U0001f3c4\U0001f3fb" - PERSON_SURFING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f3c4\U0001f3fc" - PERSON_SURFING_MEDIUM_SKIN_TONE = "\U0001f3c4\U0001f3fd" - PERSON_SURFING_MEDIUM_DARK_SKIN_TONE = "\U0001f3c4\U0001f3fe" - PERSON_SURFING_DARK_SKIN_TONE = "\U0001f3c4\U0001f3ff" - MAN_SURFING = "\U0001f3c4\u200d\u2642\ufe0f" - MAN_SURFING_LIGHT_SKIN_TONE = "\U0001f3c4\U0001f3fb\u200d\u2642\ufe0f" - MAN_SURFING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f3c4\U0001f3fc\u200d\u2642\ufe0f" - MAN_SURFING_MEDIUM_SKIN_TONE = "\U0001f3c4\U0001f3fd\u200d\u2642\ufe0f" - MAN_SURFING_MEDIUM_DARK_SKIN_TONE = "\U0001f3c4\U0001f3fe\u200d\u2642\ufe0f" - MAN_SURFING_DARK_SKIN_TONE = "\U0001f3c4\U0001f3ff\u200d\u2642\ufe0f" - WOMAN_SURFING = "\U0001f3c4\u200d\u2640\ufe0f" - WOMAN_SURFING_LIGHT_SKIN_TONE = "\U0001f3c4\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_SURFING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f3c4\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_SURFING_MEDIUM_SKIN_TONE = "\U0001f3c4\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_SURFING_MEDIUM_DARK_SKIN_TONE = "\U0001f3c4\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_SURFING_DARK_SKIN_TONE = "\U0001f3c4\U0001f3ff\u200d\u2640\ufe0f" - PERSON_ROWING_BOAT = "\U0001f6a3" - PERSON_ROWING_BOAT_LIGHT_SKIN_TONE = "\U0001f6a3\U0001f3fb" - PERSON_ROWING_BOAT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f6a3\U0001f3fc" - PERSON_ROWING_BOAT_MEDIUM_SKIN_TONE = "\U0001f6a3\U0001f3fd" - PERSON_ROWING_BOAT_MEDIUM_DARK_SKIN_TONE = "\U0001f6a3\U0001f3fe" - PERSON_ROWING_BOAT_DARK_SKIN_TONE = "\U0001f6a3\U0001f3ff" - MAN_ROWING_BOAT = "\U0001f6a3\u200d\u2642\ufe0f" - MAN_ROWING_BOAT_LIGHT_SKIN_TONE = "\U0001f6a3\U0001f3fb\u200d\u2642\ufe0f" - MAN_ROWING_BOAT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f6a3\U0001f3fc\u200d\u2642\ufe0f" - MAN_ROWING_BOAT_MEDIUM_SKIN_TONE = "\U0001f6a3\U0001f3fd\u200d\u2642\ufe0f" - MAN_ROWING_BOAT_MEDIUM_DARK_SKIN_TONE = "\U0001f6a3\U0001f3fe\u200d\u2642\ufe0f" - MAN_ROWING_BOAT_DARK_SKIN_TONE = "\U0001f6a3\U0001f3ff\u200d\u2642\ufe0f" - WOMAN_ROWING_BOAT = "\U0001f6a3\u200d\u2640\ufe0f" - WOMAN_ROWING_BOAT_LIGHT_SKIN_TONE = "\U0001f6a3\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_ROWING_BOAT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f6a3\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_ROWING_BOAT_MEDIUM_SKIN_TONE = "\U0001f6a3\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_ROWING_BOAT_MEDIUM_DARK_SKIN_TONE = "\U0001f6a3\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_ROWING_BOAT_DARK_SKIN_TONE = "\U0001f6a3\U0001f3ff\u200d\u2640\ufe0f" - PERSON_SWIMMING = "\U0001f3ca" - PERSON_SWIMMING_LIGHT_SKIN_TONE = "\U0001f3ca\U0001f3fb" - PERSON_SWIMMING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f3ca\U0001f3fc" - PERSON_SWIMMING_MEDIUM_SKIN_TONE = "\U0001f3ca\U0001f3fd" - PERSON_SWIMMING_MEDIUM_DARK_SKIN_TONE = "\U0001f3ca\U0001f3fe" - PERSON_SWIMMING_DARK_SKIN_TONE = "\U0001f3ca\U0001f3ff" - MAN_SWIMMING = "\U0001f3ca\u200d\u2642\ufe0f" - MAN_SWIMMING_LIGHT_SKIN_TONE = "\U0001f3ca\U0001f3fb\u200d\u2642\ufe0f" - MAN_SWIMMING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f3ca\U0001f3fc\u200d\u2642\ufe0f" - MAN_SWIMMING_MEDIUM_SKIN_TONE = "\U0001f3ca\U0001f3fd\u200d\u2642\ufe0f" - MAN_SWIMMING_MEDIUM_DARK_SKIN_TONE = "\U0001f3ca\U0001f3fe\u200d\u2642\ufe0f" - MAN_SWIMMING_DARK_SKIN_TONE = "\U0001f3ca\U0001f3ff\u200d\u2642\ufe0f" - WOMAN_SWIMMING = "\U0001f3ca\u200d\u2640\ufe0f" - WOMAN_SWIMMING_LIGHT_SKIN_TONE = "\U0001f3ca\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_SWIMMING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f3ca\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_SWIMMING_MEDIUM_SKIN_TONE = "\U0001f3ca\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_SWIMMING_MEDIUM_DARK_SKIN_TONE = "\U0001f3ca\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_SWIMMING_DARK_SKIN_TONE = "\U0001f3ca\U0001f3ff\u200d\u2640\ufe0f" - PERSON_BOUNCING_BALL = "\u26f9\ufe0f" - PERSON_BOUNCING_BALL_LIGHT_SKIN_TONE = "\u26f9\U0001f3fb" - PERSON_BOUNCING_BALL_MEDIUM_LIGHT_SKIN_TONE = "\u26f9\U0001f3fc" - PERSON_BOUNCING_BALL_MEDIUM_SKIN_TONE = "\u26f9\U0001f3fd" - PERSON_BOUNCING_BALL_MEDIUM_DARK_SKIN_TONE = "\u26f9\U0001f3fe" - PERSON_BOUNCING_BALL_DARK_SKIN_TONE = "\u26f9\U0001f3ff" - MAN_BOUNCING_BALL = "\u26f9\ufe0f\u200d\u2642\ufe0f" - MAN_BOUNCING_BALL_LIGHT_SKIN_TONE = "\u26f9\U0001f3fb\u200d\u2642\ufe0f" - MAN_BOUNCING_BALL_MEDIUM_LIGHT_SKIN_TONE = "\u26f9\U0001f3fc\u200d\u2642\ufe0f" - MAN_BOUNCING_BALL_MEDIUM_SKIN_TONE = "\u26f9\U0001f3fd\u200d\u2642\ufe0f" - MAN_BOUNCING_BALL_MEDIUM_DARK_SKIN_TONE = "\u26f9\U0001f3fe\u200d\u2642\ufe0f" - MAN_BOUNCING_BALL_DARK_SKIN_TONE = "\u26f9\U0001f3ff\u200d\u2642\ufe0f" - WOMAN_BOUNCING_BALL = "\u26f9\ufe0f\u200d\u2640\ufe0f" - WOMAN_BOUNCING_BALL_LIGHT_SKIN_TONE = "\u26f9\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_BOUNCING_BALL_MEDIUM_LIGHT_SKIN_TONE = "\u26f9\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_BOUNCING_BALL_MEDIUM_SKIN_TONE = "\u26f9\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_BOUNCING_BALL_MEDIUM_DARK_SKIN_TONE = "\u26f9\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_BOUNCING_BALL_DARK_SKIN_TONE = "\u26f9\U0001f3ff\u200d\u2640\ufe0f" - PERSON_LIFTING_WEIGHTS = "\U0001f3cb\ufe0f" - PERSON_LIFTING_WEIGHTS_LIGHT_SKIN_TONE = "\U0001f3cb\U0001f3fb" - PERSON_LIFTING_WEIGHTS_MEDIUM_LIGHT_SKIN_TONE = "\U0001f3cb\U0001f3fc" - PERSON_LIFTING_WEIGHTS_MEDIUM_SKIN_TONE = "\U0001f3cb\U0001f3fd" - PERSON_LIFTING_WEIGHTS_MEDIUM_DARK_SKIN_TONE = "\U0001f3cb\U0001f3fe" - PERSON_LIFTING_WEIGHTS_DARK_SKIN_TONE = "\U0001f3cb\U0001f3ff" - MAN_LIFTING_WEIGHTS = "\U0001f3cb\ufe0f\u200d\u2642\ufe0f" - MAN_LIFTING_WEIGHTS_LIGHT_SKIN_TONE = "\U0001f3cb\U0001f3fb\u200d\u2642\ufe0f" - MAN_LIFTING_WEIGHTS_MEDIUM_LIGHT_SKIN_TONE = "\U0001f3cb\U0001f3fc\u200d\u2642\ufe0f" - MAN_LIFTING_WEIGHTS_MEDIUM_SKIN_TONE = "\U0001f3cb\U0001f3fd\u200d\u2642\ufe0f" - MAN_LIFTING_WEIGHTS_MEDIUM_DARK_SKIN_TONE = "\U0001f3cb\U0001f3fe\u200d\u2642\ufe0f" - MAN_LIFTING_WEIGHTS_DARK_SKIN_TONE = "\U0001f3cb\U0001f3ff\u200d\u2642\ufe0f" - WOMAN_LIFTING_WEIGHTS = "\U0001f3cb\ufe0f\u200d\u2640\ufe0f" - WOMAN_LIFTING_WEIGHTS_LIGHT_SKIN_TONE = "\U0001f3cb\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_LIFTING_WEIGHTS_MEDIUM_LIGHT_SKIN_TONE = "\U0001f3cb\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_LIFTING_WEIGHTS_MEDIUM_SKIN_TONE = "\U0001f3cb\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_LIFTING_WEIGHTS_MEDIUM_DARK_SKIN_TONE = "\U0001f3cb\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_LIFTING_WEIGHTS_DARK_SKIN_TONE = "\U0001f3cb\U0001f3ff\u200d\u2640\ufe0f" - PERSON_BIKING = "\U0001f6b4" - PERSON_BIKING_LIGHT_SKIN_TONE = "\U0001f6b4\U0001f3fb" - PERSON_BIKING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f6b4\U0001f3fc" - PERSON_BIKING_MEDIUM_SKIN_TONE = "\U0001f6b4\U0001f3fd" - PERSON_BIKING_MEDIUM_DARK_SKIN_TONE = "\U0001f6b4\U0001f3fe" - PERSON_BIKING_DARK_SKIN_TONE = "\U0001f6b4\U0001f3ff" - MAN_BIKING = "\U0001f6b4\u200d\u2642\ufe0f" - MAN_BIKING_LIGHT_SKIN_TONE = "\U0001f6b4\U0001f3fb\u200d\u2642\ufe0f" - MAN_BIKING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f6b4\U0001f3fc\u200d\u2642\ufe0f" - MAN_BIKING_MEDIUM_SKIN_TONE = "\U0001f6b4\U0001f3fd\u200d\u2642\ufe0f" - MAN_BIKING_MEDIUM_DARK_SKIN_TONE = "\U0001f6b4\U0001f3fe\u200d\u2642\ufe0f" - MAN_BIKING_DARK_SKIN_TONE = "\U0001f6b4\U0001f3ff\u200d\u2642\ufe0f" - WOMAN_BIKING = "\U0001f6b4\u200d\u2640\ufe0f" - WOMAN_BIKING_LIGHT_SKIN_TONE = "\U0001f6b4\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_BIKING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f6b4\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_BIKING_MEDIUM_SKIN_TONE = "\U0001f6b4\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_BIKING_MEDIUM_DARK_SKIN_TONE = "\U0001f6b4\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_BIKING_DARK_SKIN_TONE = "\U0001f6b4\U0001f3ff\u200d\u2640\ufe0f" - PERSON_MOUNTAIN_BIKING = "\U0001f6b5" - PERSON_MOUNTAIN_BIKING_LIGHT_SKIN_TONE = "\U0001f6b5\U0001f3fb" - PERSON_MOUNTAIN_BIKING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f6b5\U0001f3fc" - PERSON_MOUNTAIN_BIKING_MEDIUM_SKIN_TONE = "\U0001f6b5\U0001f3fd" - PERSON_MOUNTAIN_BIKING_MEDIUM_DARK_SKIN_TONE = "\U0001f6b5\U0001f3fe" - PERSON_MOUNTAIN_BIKING_DARK_SKIN_TONE = "\U0001f6b5\U0001f3ff" - MAN_MOUNTAIN_BIKING = "\U0001f6b5\u200d\u2642\ufe0f" - MAN_MOUNTAIN_BIKING_LIGHT_SKIN_TONE = "\U0001f6b5\U0001f3fb\u200d\u2642\ufe0f" - MAN_MOUNTAIN_BIKING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f6b5\U0001f3fc\u200d\u2642\ufe0f" - MAN_MOUNTAIN_BIKING_MEDIUM_SKIN_TONE = "\U0001f6b5\U0001f3fd\u200d\u2642\ufe0f" - MAN_MOUNTAIN_BIKING_MEDIUM_DARK_SKIN_TONE = "\U0001f6b5\U0001f3fe\u200d\u2642\ufe0f" - MAN_MOUNTAIN_BIKING_DARK_SKIN_TONE = "\U0001f6b5\U0001f3ff\u200d\u2642\ufe0f" - WOMAN_MOUNTAIN_BIKING = "\U0001f6b5\u200d\u2640\ufe0f" - WOMAN_MOUNTAIN_BIKING_LIGHT_SKIN_TONE = "\U0001f6b5\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_MOUNTAIN_BIKING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f6b5\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_MOUNTAIN_BIKING_MEDIUM_SKIN_TONE = "\U0001f6b5\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_MOUNTAIN_BIKING_MEDIUM_DARK_SKIN_TONE = "\U0001f6b5\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_MOUNTAIN_BIKING_DARK_SKIN_TONE = "\U0001f6b5\U0001f3ff\u200d\u2640\ufe0f" - RACING_CAR = "\U0001f3ce\ufe0f" - MOTORCYCLE = "\U0001f3cd\ufe0f" - PERSON_CARTWHEELING = "\U0001f938" - PERSON_CARTWHEELING_LIGHT_SKIN_TONE = "\U0001f938\U0001f3fb" - PERSON_CARTWHEELING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f938\U0001f3fc" - PERSON_CARTWHEELING_MEDIUM_SKIN_TONE = "\U0001f938\U0001f3fd" - WRESTLERS_TYPE_1_2 = "\U0001f93c\U0001f3fb" - PERSON_CARTWHEELING_MEDIUM_DARK_SKIN_TONE = "\U0001f938\U0001f3fe" - WRESTLERS_TYPE_3 = "\U0001f93c\U0001f3fc" - PERSON_CARTWHEELING_DARK_SKIN_TONE = "\U0001f938\U0001f3ff" - MAN_CARTWHEELING = "\U0001f938\u200d\u2642\ufe0f" - WRESTLERS_TYPE_4 = "\U0001f93c\U0001f3fd" - WRESTLERS_TYPE_5 = "\U0001f93c\U0001f3fe" - MAN_CARTWHEELING_LIGHT_SKIN_TONE = "\U0001f938\U0001f3fb\u200d\u2642\ufe0f" - WRESTLERS_TYPE_6 = "\U0001f93c\U0001f3ff" - MAN_CARTWHEELING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f938\U0001f3fc\u200d\u2642\ufe0f" - MEN_WRESTLING_TYPE_1_2 = "\U0001f93c\U0001f3fb\u200d\u2642\ufe0f" - MEN_WRESTLING_TYPE_3 = "\U0001f93c\U0001f3fc\u200d\u2642\ufe0f" - MEN_WRESTLING_TYPE_4 = "\U0001f93c\U0001f3fd\u200d\u2642\ufe0f" - MAN_CARTWHEELING_MEDIUM_SKIN_TONE = "\U0001f938\U0001f3fd\u200d\u2642\ufe0f" - MEN_WRESTLING_TYPE_5 = "\U0001f93c\U0001f3fe\u200d\u2642\ufe0f" - MEN_WRESTLING_TYPE_6 = "\U0001f93c\U0001f3ff\u200d\u2642\ufe0f" - MAN_CARTWHEELING_MEDIUM_DARK_SKIN_TONE = "\U0001f938\U0001f3fe\u200d\u2642\ufe0f" - WOMEN_WRESTLING_TYPE_1_2 = "\U0001f93c\U0001f3fb\u200d\u2640\ufe0f" - MAN_CARTWHEELING_DARK_SKIN_TONE = "\U0001f938\U0001f3ff\u200d\u2642\ufe0f" - WOMEN_WRESTLING_TYPE_3 = "\U0001f93c\U0001f3fc\u200d\u2640\ufe0f" - WOMEN_WRESTLING_TYPE_4 = "\U0001f93c\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_CARTWHEELING = "\U0001f938\u200d\u2640\ufe0f" - WOMEN_WRESTLING_TYPE_5 = "\U0001f93c\U0001f3fe\u200d\u2640\ufe0f" - WOMEN_WRESTLING_TYPE_6 = "\U0001f93c\U0001f3ff\u200d\u2640\ufe0f" - WOMAN_CARTWHEELING_LIGHT_SKIN_TONE = "\U0001f938\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_CARTWHEELING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f938\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_CARTWHEELING_MEDIUM_SKIN_TONE = "\U0001f938\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_CARTWHEELING_MEDIUM_DARK_SKIN_TONE = "\U0001f938\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_CARTWHEELING_DARK_SKIN_TONE = "\U0001f938\U0001f3ff\u200d\u2640\ufe0f" - PEOPLE_WRESTLING = "\U0001f93c" - MEN_WRESTLING = "\U0001f93c\u200d\u2642\ufe0f" - WOMEN_WRESTLING = "\U0001f93c\u200d\u2640\ufe0f" - PERSON_PLAYING_WATER_POLO = "\U0001f93d" - PERSON_PLAYING_WATER_POLO_LIGHT_SKIN_TONE = "\U0001f93d\U0001f3fb" - PERSON_PLAYING_WATER_POLO_MEDIUM_LIGHT_SKIN_TONE = "\U0001f93d\U0001f3fc" - PERSON_PLAYING_WATER_POLO_MEDIUM_SKIN_TONE = "\U0001f93d\U0001f3fd" - PERSON_PLAYING_WATER_POLO_MEDIUM_DARK_SKIN_TONE = "\U0001f93d\U0001f3fe" - PERSON_PLAYING_WATER_POLO_DARK_SKIN_TONE = "\U0001f93d\U0001f3ff" - MAN_PLAYING_WATER_POLO = "\U0001f93d\u200d\u2642\ufe0f" - MAN_PLAYING_WATER_POLO_LIGHT_SKIN_TONE = "\U0001f93d\U0001f3fb\u200d\u2642\ufe0f" - MAN_PLAYING_WATER_POLO_MEDIUM_LIGHT_SKIN_TONE = "\U0001f93d\U0001f3fc\u200d\u2642\ufe0f" - MAN_PLAYING_WATER_POLO_MEDIUM_SKIN_TONE = "\U0001f93d\U0001f3fd\u200d\u2642\ufe0f" - MAN_PLAYING_WATER_POLO_MEDIUM_DARK_SKIN_TONE = "\U0001f93d\U0001f3fe\u200d\u2642\ufe0f" - MAN_PLAYING_WATER_POLO_DARK_SKIN_TONE = "\U0001f93d\U0001f3ff\u200d\u2642\ufe0f" - WOMAN_PLAYING_WATER_POLO = "\U0001f93d\u200d\u2640\ufe0f" - WOMAN_PLAYING_WATER_POLO_LIGHT_SKIN_TONE = "\U0001f93d\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_PLAYING_WATER_POLO_MEDIUM_LIGHT_SKIN_TONE = "\U0001f93d\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_PLAYING_WATER_POLO_MEDIUM_SKIN_TONE = "\U0001f93d\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_PLAYING_WATER_POLO_MEDIUM_DARK_SKIN_TONE = "\U0001f93d\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_PLAYING_WATER_POLO_DARK_SKIN_TONE = "\U0001f93d\U0001f3ff\u200d\u2640\ufe0f" - PERSON_PLAYING_HANDBALL = "\U0001f93e" - PERSON_PLAYING_HANDBALL_LIGHT_SKIN_TONE = "\U0001f93e\U0001f3fb" - PERSON_PLAYING_HANDBALL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f93e\U0001f3fc" - PERSON_PLAYING_HANDBALL_MEDIUM_SKIN_TONE = "\U0001f93e\U0001f3fd" - PERSON_PLAYING_HANDBALL_MEDIUM_DARK_SKIN_TONE = "\U0001f93e\U0001f3fe" - PERSON_PLAYING_HANDBALL_DARK_SKIN_TONE = "\U0001f93e\U0001f3ff" - MAN_PLAYING_HANDBALL = "\U0001f93e\u200d\u2642\ufe0f" - MAN_PLAYING_HANDBALL_LIGHT_SKIN_TONE = "\U0001f93e\U0001f3fb\u200d\u2642\ufe0f" - MAN_PLAYING_HANDBALL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f93e\U0001f3fc\u200d\u2642\ufe0f" - MAN_PLAYING_HANDBALL_MEDIUM_SKIN_TONE = "\U0001f93e\U0001f3fd\u200d\u2642\ufe0f" - MAN_PLAYING_HANDBALL_MEDIUM_DARK_SKIN_TONE = "\U0001f93e\U0001f3fe\u200d\u2642\ufe0f" - MAN_PLAYING_HANDBALL_DARK_SKIN_TONE = "\U0001f93e\U0001f3ff\u200d\u2642\ufe0f" - WOMAN_PLAYING_HANDBALL = "\U0001f93e\u200d\u2640\ufe0f" - WOMAN_PLAYING_HANDBALL_LIGHT_SKIN_TONE = "\U0001f93e\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_PLAYING_HANDBALL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f93e\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_PLAYING_HANDBALL_MEDIUM_SKIN_TONE = "\U0001f93e\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_PLAYING_HANDBALL_MEDIUM_DARK_SKIN_TONE = "\U0001f93e\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_PLAYING_HANDBALL_DARK_SKIN_TONE = "\U0001f93e\U0001f3ff\u200d\u2640\ufe0f" - PERSON_JUGGLING = "\U0001f939" - PERSON_JUGGLING_LIGHT_SKIN_TONE = "\U0001f939\U0001f3fb" - PERSON_JUGGLING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f939\U0001f3fc" - PERSON_JUGGLING_MEDIUM_SKIN_TONE = "\U0001f939\U0001f3fd" - PERSON_JUGGLING_MEDIUM_DARK_SKIN_TONE = "\U0001f939\U0001f3fe" - PERSON_JUGGLING_DARK_SKIN_TONE = "\U0001f939\U0001f3ff" - MAN_JUGGLING = "\U0001f939\u200d\u2642\ufe0f" - MAN_JUGGLING_LIGHT_SKIN_TONE = "\U0001f939\U0001f3fb\u200d\u2642\ufe0f" - MAN_JUGGLING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f939\U0001f3fc\u200d\u2642\ufe0f" - MAN_JUGGLING_MEDIUM_SKIN_TONE = "\U0001f939\U0001f3fd\u200d\u2642\ufe0f" - MAN_JUGGLING_MEDIUM_DARK_SKIN_TONE = "\U0001f939\U0001f3fe\u200d\u2642\ufe0f" - MAN_JUGGLING_DARK_SKIN_TONE = "\U0001f939\U0001f3ff\u200d\u2642\ufe0f" - WOMAN_JUGGLING = "\U0001f939\u200d\u2640\ufe0f" - WOMAN_JUGGLING_LIGHT_SKIN_TONE = "\U0001f939\U0001f3fb\u200d\u2640\ufe0f" - WOMAN_JUGGLING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f939\U0001f3fc\u200d\u2640\ufe0f" - WOMAN_JUGGLING_MEDIUM_SKIN_TONE = "\U0001f939\U0001f3fd\u200d\u2640\ufe0f" - WOMAN_JUGGLING_MEDIUM_DARK_SKIN_TONE = "\U0001f939\U0001f3fe\u200d\u2640\ufe0f" - WOMAN_JUGGLING_DARK_SKIN_TONE = "\U0001f939\U0001f3ff\u200d\u2640\ufe0f" - MAN_AND_WOMAN_HOLDING_HANDS = "\U0001f46b" - TWO_MEN_HOLDING_HANDS = "\U0001f46c" - TWO_WOMEN_HOLDING_HANDS = "\U0001f46d" - KISS = "\U0001f48f" - KISS_WOMAN_MAN = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468" - KISS_MAN_MAN = "\U0001f468\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468" - KISS_WOMAN_WOMAN = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469" - COUPLE_WITH_HEART = "\U0001f491" - COUPLE_WITH_HEART_WOMAN_MAN = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f468" - COUPLE_WITH_HEART_MAN_MAN = "\U0001f468\u200d\u2764\ufe0f\u200d\U0001f468" - COUPLE_WITH_HEART_WOMAN_WOMAN = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f469" - FAMILY = "\U0001f46a" - FAMILY_MAN_WOMAN_BOY = "\U0001f468\u200d\U0001f469\u200d\U0001f466" - FAMILY_MAN_WOMAN_GIRL = "\U0001f468\u200d\U0001f469\u200d\U0001f467" - FAMILY_MAN_WOMAN_GIRL_BOY = "\U0001f468\u200d\U0001f469\u200d\U0001f467\u200d\U0001f466" - FAMILY_MAN_WOMAN_BOY_BOY = "\U0001f468\u200d\U0001f469\u200d\U0001f466\u200d\U0001f466" - FAMILY_MAN_WOMAN_GIRL_GIRL = "\U0001f468\u200d\U0001f469\u200d\U0001f467\u200d\U0001f467" - FAMILY_MAN_MAN_BOY = "\U0001f468\u200d\U0001f468\u200d\U0001f466" - FAMILY_MAN_MAN_GIRL = "\U0001f468\u200d\U0001f468\u200d\U0001f467" - FAMILY_MAN_MAN_GIRL_BOY = "\U0001f468\u200d\U0001f468\u200d\U0001f467\u200d\U0001f466" - FAMILY_MAN_MAN_BOY_BOY = "\U0001f468\u200d\U0001f468\u200d\U0001f466\u200d\U0001f466" - FAMILY_MAN_MAN_GIRL_GIRL = "\U0001f468\u200d\U0001f468\u200d\U0001f467\u200d\U0001f467" - FAMILY_WOMAN_WOMAN_BOY = "\U0001f469\u200d\U0001f469\u200d\U0001f466" - FAMILY_WOMAN_WOMAN_GIRL = "\U0001f469\u200d\U0001f469\u200d\U0001f467" - FAMILY_WOMAN_WOMAN_GIRL_BOY = "\U0001f469\u200d\U0001f469\u200d\U0001f467\u200d\U0001f466" - FAMILY_WOMAN_WOMAN_BOY_BOY = "\U0001f469\u200d\U0001f469\u200d\U0001f466\u200d\U0001f466" - FAMILY_WOMAN_WOMAN_GIRL_GIRL = "\U0001f469\u200d\U0001f469\u200d\U0001f467\u200d\U0001f467" - FAMILY_MAN_BOY = "\U0001f468\u200d\U0001f466" - FAMILY_MAN_BOY_BOY = "\U0001f468\u200d\U0001f466\u200d\U0001f466" - FAMILY_MAN_GIRL = "\U0001f468\u200d\U0001f467" - FAMILY_MAN_GIRL_BOY = "\U0001f468\u200d\U0001f467\u200d\U0001f466" - FAMILY_MAN_GIRL_GIRL = "\U0001f468\u200d\U0001f467\u200d\U0001f467" - FAMILY_WOMAN_BOY = "\U0001f469\u200d\U0001f466" - FAMILY_WOMAN_BOY_BOY = "\U0001f469\u200d\U0001f466\u200d\U0001f466" - FAMILY_WOMAN_GIRL = "\U0001f469\u200d\U0001f467" - FAMILY_WOMAN_GIRL_BOY = "\U0001f469\u200d\U0001f467\u200d\U0001f466" - FAMILY_WOMAN_GIRL_GIRL = "\U0001f469\u200d\U0001f467\u200d\U0001f467" - SELFIE = "\U0001f933" - SELFIE_LIGHT_SKIN_TONE = "\U0001f933\U0001f3fb" - SELFIE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f933\U0001f3fc" - SELFIE_MEDIUM_SKIN_TONE = "\U0001f933\U0001f3fd" - SELFIE_MEDIUM_DARK_SKIN_TONE = "\U0001f933\U0001f3fe" - SELFIE_DARK_SKIN_TONE = "\U0001f933\U0001f3ff" - FLEXED_BICEPS = "\U0001f4aa" - FLEXED_BICEPS_LIGHT_SKIN_TONE = "\U0001f4aa\U0001f3fb" - FLEXED_BICEPS_MEDIUM_LIGHT_SKIN_TONE = "\U0001f4aa\U0001f3fc" - FLEXED_BICEPS_MEDIUM_SKIN_TONE = "\U0001f4aa\U0001f3fd" - FLEXED_BICEPS_MEDIUM_DARK_SKIN_TONE = "\U0001f4aa\U0001f3fe" - FLEXED_BICEPS_DARK_SKIN_TONE = "\U0001f4aa\U0001f3ff" - LEG = "\U0001f9b5" - LEG_LIGHT_SKIN_TONE = "\U0001f9b5\U0001f3fb" - LEG_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9b5\U0001f3fc" - LEG_MEDIUM_SKIN_TONE = "\U0001f9b5\U0001f3fd" - LEG_MEDIUM_DARK_SKIN_TONE = "\U0001f9b5\U0001f3fe" - LEG_DARK_SKIN_TONE = "\U0001f9b5\U0001f3ff" - FOOT = "\U0001f9b6" - FOOT_LIGHT_SKIN_TONE = "\U0001f9b6\U0001f3fb" - FOOT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9b6\U0001f3fc" - FOOT_MEDIUM_SKIN_TONE = "\U0001f9b6\U0001f3fd" - FOOT_MEDIUM_DARK_SKIN_TONE = "\U0001f9b6\U0001f3fe" - FOOT_DARK_SKIN_TONE = "\U0001f9b6\U0001f3ff" - BACKHAND_INDEX_POINTING_LEFT = "\U0001f448" - BACKHAND_INDEX_POINTING_LEFT_LIGHT_SKIN_TONE = "\U0001f448\U0001f3fb" - BACKHAND_INDEX_POINTING_LEFT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f448\U0001f3fc" - BACKHAND_INDEX_POINTING_LEFT_MEDIUM_SKIN_TONE = "\U0001f448\U0001f3fd" - BACKHAND_INDEX_POINTING_LEFT_MEDIUM_DARK_SKIN_TONE = "\U0001f448\U0001f3fe" - BACKHAND_INDEX_POINTING_LEFT_DARK_SKIN_TONE = "\U0001f448\U0001f3ff" - BACKHAND_INDEX_POINTING_RIGHT = "\U0001f449" - BACKHAND_INDEX_POINTING_RIGHT_LIGHT_SKIN_TONE = "\U0001f449\U0001f3fb" - BACKHAND_INDEX_POINTING_RIGHT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f449\U0001f3fc" - BACKHAND_INDEX_POINTING_RIGHT_MEDIUM_SKIN_TONE = "\U0001f449\U0001f3fd" - BACKHAND_INDEX_POINTING_RIGHT_MEDIUM_DARK_SKIN_TONE = "\U0001f449\U0001f3fe" - BACKHAND_INDEX_POINTING_RIGHT_DARK_SKIN_TONE = "\U0001f449\U0001f3ff" - INDEX_POINTING_UP = "\u261d\ufe0f" - INDEX_POINTING_UP_LIGHT_SKIN_TONE = "\u261d\U0001f3fb" - INDEX_POINTING_UP_MEDIUM_LIGHT_SKIN_TONE = "\u261d\U0001f3fc" - INDEX_POINTING_UP_MEDIUM_SKIN_TONE = "\u261d\U0001f3fd" - INDEX_POINTING_UP_MEDIUM_DARK_SKIN_TONE = "\u261d\U0001f3fe" - INDEX_POINTING_UP_DARK_SKIN_TONE = "\u261d\U0001f3ff" - BACKHAND_INDEX_POINTING_UP = "\U0001f446" - BACKHAND_INDEX_POINTING_UP_LIGHT_SKIN_TONE = "\U0001f446\U0001f3fb" - BACKHAND_INDEX_POINTING_UP_MEDIUM_LIGHT_SKIN_TONE = "\U0001f446\U0001f3fc" - BACKHAND_INDEX_POINTING_UP_MEDIUM_SKIN_TONE = "\U0001f446\U0001f3fd" - BACKHAND_INDEX_POINTING_UP_MEDIUM_DARK_SKIN_TONE = "\U0001f446\U0001f3fe" - BACKHAND_INDEX_POINTING_UP_DARK_SKIN_TONE = "\U0001f446\U0001f3ff" - MIDDLE_FINGER = "\U0001f595" - MIDDLE_FINGER_LIGHT_SKIN_TONE = "\U0001f595\U0001f3fb" - MIDDLE_FINGER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f595\U0001f3fc" - MIDDLE_FINGER_MEDIUM_SKIN_TONE = "\U0001f595\U0001f3fd" - MIDDLE_FINGER_MEDIUM_DARK_SKIN_TONE = "\U0001f595\U0001f3fe" - MIDDLE_FINGER_DARK_SKIN_TONE = "\U0001f595\U0001f3ff" - BACKHAND_INDEX_POINTING_DOWN = "\U0001f447" - BACKHAND_INDEX_POINTING_DOWN_LIGHT_SKIN_TONE = "\U0001f447\U0001f3fb" - BACKHAND_INDEX_POINTING_DOWN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f447\U0001f3fc" - BACKHAND_INDEX_POINTING_DOWN_MEDIUM_SKIN_TONE = "\U0001f447\U0001f3fd" - BACKHAND_INDEX_POINTING_DOWN_MEDIUM_DARK_SKIN_TONE = "\U0001f447\U0001f3fe" - BACKHAND_INDEX_POINTING_DOWN_DARK_SKIN_TONE = "\U0001f447\U0001f3ff" - VICTORY_HAND = "\u270c\ufe0f" - VICTORY_HAND_LIGHT_SKIN_TONE = "\u270c\U0001f3fb" - VICTORY_HAND_MEDIUM_LIGHT_SKIN_TONE = "\u270c\U0001f3fc" - VICTORY_HAND_MEDIUM_SKIN_TONE = "\u270c\U0001f3fd" - VICTORY_HAND_MEDIUM_DARK_SKIN_TONE = "\u270c\U0001f3fe" - VICTORY_HAND_DARK_SKIN_TONE = "\u270c\U0001f3ff" - CROSSED_FINGERS = "\U0001f91e" - CROSSED_FINGERS_LIGHT_SKIN_TONE = "\U0001f91e\U0001f3fb" - CROSSED_FINGERS_MEDIUM_LIGHT_SKIN_TONE = "\U0001f91e\U0001f3fc" - CROSSED_FINGERS_MEDIUM_SKIN_TONE = "\U0001f91e\U0001f3fd" - CROSSED_FINGERS_MEDIUM_DARK_SKIN_TONE = "\U0001f91e\U0001f3fe" - CROSSED_FINGERS_DARK_SKIN_TONE = "\U0001f91e\U0001f3ff" - VULCAN_SALUTE = "\U0001f596" - VULCAN_SALUTE_LIGHT_SKIN_TONE = "\U0001f596\U0001f3fb" - VULCAN_SALUTE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f596\U0001f3fc" - VULCAN_SALUTE_MEDIUM_SKIN_TONE = "\U0001f596\U0001f3fd" - VULCAN_SALUTE_MEDIUM_DARK_SKIN_TONE = "\U0001f596\U0001f3fe" - VULCAN_SALUTE_DARK_SKIN_TONE = "\U0001f596\U0001f3ff" - SIGN_OF_THE_HORNS = "\U0001f918" - SIGN_OF_THE_HORNS_LIGHT_SKIN_TONE = "\U0001f918\U0001f3fb" - SIGN_OF_THE_HORNS_MEDIUM_LIGHT_SKIN_TONE = "\U0001f918\U0001f3fc" - SIGN_OF_THE_HORNS_MEDIUM_SKIN_TONE = "\U0001f918\U0001f3fd" - SIGN_OF_THE_HORNS_MEDIUM_DARK_SKIN_TONE = "\U0001f918\U0001f3fe" - SIGN_OF_THE_HORNS_DARK_SKIN_TONE = "\U0001f918\U0001f3ff" - CALL_ME_HAND = "\U0001f919" - CALL_ME_HAND_LIGHT_SKIN_TONE = "\U0001f919\U0001f3fb" - CALL_ME_HAND_MEDIUM_LIGHT_SKIN_TONE = "\U0001f919\U0001f3fc" - CALL_ME_HAND_MEDIUM_SKIN_TONE = "\U0001f919\U0001f3fd" - CALL_ME_HAND_MEDIUM_DARK_SKIN_TONE = "\U0001f919\U0001f3fe" - CALL_ME_HAND_DARK_SKIN_TONE = "\U0001f919\U0001f3ff" - HAND_WITH_FINGERS_SPLAYED = "\U0001f590\ufe0f" - HAND_WITH_FINGERS_SPLAYED_LIGHT_SKIN_TONE = "\U0001f590\U0001f3fb" - HAND_WITH_FINGERS_SPLAYED_MEDIUM_LIGHT_SKIN_TONE = "\U0001f590\U0001f3fc" - HAND_WITH_FINGERS_SPLAYED_MEDIUM_SKIN_TONE = "\U0001f590\U0001f3fd" - HAND_WITH_FINGERS_SPLAYED_MEDIUM_DARK_SKIN_TONE = "\U0001f590\U0001f3fe" - HAND_WITH_FINGERS_SPLAYED_DARK_SKIN_TONE = "\U0001f590\U0001f3ff" - RAISED_HAND = "\u270b" - RAISED_HAND_LIGHT_SKIN_TONE = "\u270b\U0001f3fb" - RAISED_HAND_MEDIUM_LIGHT_SKIN_TONE = "\u270b\U0001f3fc" - RAISED_HAND_MEDIUM_SKIN_TONE = "\u270b\U0001f3fd" - RAISED_HAND_MEDIUM_DARK_SKIN_TONE = "\u270b\U0001f3fe" - RAISED_HAND_DARK_SKIN_TONE = "\u270b\U0001f3ff" - OK_HAND = "\U0001f44c" - OK_HAND_LIGHT_SKIN_TONE = "\U0001f44c\U0001f3fb" - OK_HAND_MEDIUM_LIGHT_SKIN_TONE = "\U0001f44c\U0001f3fc" - OK_HAND_MEDIUM_SKIN_TONE = "\U0001f44c\U0001f3fd" - OK_HAND_MEDIUM_DARK_SKIN_TONE = "\U0001f44c\U0001f3fe" - OK_HAND_DARK_SKIN_TONE = "\U0001f44c\U0001f3ff" - THUMBS_UP = "\U0001f44d" - THUMBS_UP_LIGHT_SKIN_TONE = "\U0001f44d\U0001f3fb" - THUMBS_UP_MEDIUM_LIGHT_SKIN_TONE = "\U0001f44d\U0001f3fc" - THUMBS_UP_MEDIUM_SKIN_TONE = "\U0001f44d\U0001f3fd" - THUMBS_UP_MEDIUM_DARK_SKIN_TONE = "\U0001f44d\U0001f3fe" - THUMBS_UP_DARK_SKIN_TONE = "\U0001f44d\U0001f3ff" - THUMBS_DOWN = "\U0001f44e" - THUMBS_DOWN_LIGHT_SKIN_TONE = "\U0001f44e\U0001f3fb" - THUMBS_DOWN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f44e\U0001f3fc" - THUMBS_DOWN_MEDIUM_SKIN_TONE = "\U0001f44e\U0001f3fd" - THUMBS_DOWN_MEDIUM_DARK_SKIN_TONE = "\U0001f44e\U0001f3fe" - THUMBS_DOWN_DARK_SKIN_TONE = "\U0001f44e\U0001f3ff" - RAISED_FIST = "\u270a" - RAISED_FIST_LIGHT_SKIN_TONE = "\u270a\U0001f3fb" - RAISED_FIST_MEDIUM_LIGHT_SKIN_TONE = "\u270a\U0001f3fc" - RAISED_FIST_MEDIUM_SKIN_TONE = "\u270a\U0001f3fd" - RAISED_FIST_MEDIUM_DARK_SKIN_TONE = "\u270a\U0001f3fe" - RAISED_FIST_DARK_SKIN_TONE = "\u270a\U0001f3ff" - ONCOMING_FIST = "\U0001f44a" - ONCOMING_FIST_LIGHT_SKIN_TONE = "\U0001f44a\U0001f3fb" - ONCOMING_FIST_MEDIUM_LIGHT_SKIN_TONE = "\U0001f44a\U0001f3fc" - ONCOMING_FIST_MEDIUM_SKIN_TONE = "\U0001f44a\U0001f3fd" - ONCOMING_FIST_MEDIUM_DARK_SKIN_TONE = "\U0001f44a\U0001f3fe" - ONCOMING_FIST_DARK_SKIN_TONE = "\U0001f44a\U0001f3ff" - LEFT_FACING_FIST = "\U0001f91b" - LEFT_FACING_FIST_LIGHT_SKIN_TONE = "\U0001f91b\U0001f3fb" - LEFT_FACING_FIST_MEDIUM_LIGHT_SKIN_TONE = "\U0001f91b\U0001f3fc" - LEFT_FACING_FIST_MEDIUM_SKIN_TONE = "\U0001f91b\U0001f3fd" - LEFT_FACING_FIST_MEDIUM_DARK_SKIN_TONE = "\U0001f91b\U0001f3fe" - LEFT_FACING_FIST_DARK_SKIN_TONE = "\U0001f91b\U0001f3ff" - RIGHT_FACING_FIST = "\U0001f91c" - RIGHT_FACING_FIST_LIGHT_SKIN_TONE = "\U0001f91c\U0001f3fb" - RIGHT_FACING_FIST_MEDIUM_LIGHT_SKIN_TONE = "\U0001f91c\U0001f3fc" - RIGHT_FACING_FIST_MEDIUM_SKIN_TONE = "\U0001f91c\U0001f3fd" - RIGHT_FACING_FIST_MEDIUM_DARK_SKIN_TONE = "\U0001f91c\U0001f3fe" - RIGHT_FACING_FIST_DARK_SKIN_TONE = "\U0001f91c\U0001f3ff" - RAISED_BACK_OF_HAND = "\U0001f91a" - RAISED_BACK_OF_HAND_LIGHT_SKIN_TONE = "\U0001f91a\U0001f3fb" - RAISED_BACK_OF_HAND_MEDIUM_LIGHT_SKIN_TONE = "\U0001f91a\U0001f3fc" - RAISED_BACK_OF_HAND_MEDIUM_SKIN_TONE = "\U0001f91a\U0001f3fd" - RAISED_BACK_OF_HAND_MEDIUM_DARK_SKIN_TONE = "\U0001f91a\U0001f3fe" - RAISED_BACK_OF_HAND_DARK_SKIN_TONE = "\U0001f91a\U0001f3ff" - WAVING_HAND = "\U0001f44b" - WAVING_HAND_LIGHT_SKIN_TONE = "\U0001f44b\U0001f3fb" - WAVING_HAND_MEDIUM_LIGHT_SKIN_TONE = "\U0001f44b\U0001f3fc" - WAVING_HAND_MEDIUM_SKIN_TONE = "\U0001f44b\U0001f3fd" - WAVING_HAND_MEDIUM_DARK_SKIN_TONE = "\U0001f44b\U0001f3fe" - WAVING_HAND_DARK_SKIN_TONE = "\U0001f44b\U0001f3ff" - LOVE_YOU_GESTURE = "\U0001f91f" - LOVE_YOU_GESTURE_LIGHT_SKIN_TONE = "\U0001f91f\U0001f3fb" - LOVE_YOU_GESTURE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f91f\U0001f3fc" - LOVE_YOU_GESTURE_MEDIUM_SKIN_TONE = "\U0001f91f\U0001f3fd" - LOVE_YOU_GESTURE_MEDIUM_DARK_SKIN_TONE = "\U0001f91f\U0001f3fe" - LOVE_YOU_GESTURE_DARK_SKIN_TONE = "\U0001f91f\U0001f3ff" - WRITING_HAND = "\u270d\ufe0f" - WRITING_HAND_LIGHT_SKIN_TONE = "\u270d\U0001f3fb" - WRITING_HAND_MEDIUM_LIGHT_SKIN_TONE = "\u270d\U0001f3fc" - WRITING_HAND_MEDIUM_SKIN_TONE = "\u270d\U0001f3fd" - WRITING_HAND_MEDIUM_DARK_SKIN_TONE = "\u270d\U0001f3fe" - WRITING_HAND_DARK_SKIN_TONE = "\u270d\U0001f3ff" - CLAPPING_HANDS = "\U0001f44f" - CLAPPING_HANDS_LIGHT_SKIN_TONE = "\U0001f44f\U0001f3fb" - CLAPPING_HANDS_MEDIUM_LIGHT_SKIN_TONE = "\U0001f44f\U0001f3fc" - CLAPPING_HANDS_MEDIUM_SKIN_TONE = "\U0001f44f\U0001f3fd" - CLAPPING_HANDS_MEDIUM_DARK_SKIN_TONE = "\U0001f44f\U0001f3fe" - CLAPPING_HANDS_DARK_SKIN_TONE = "\U0001f44f\U0001f3ff" - OPEN_HANDS = "\U0001f450" - OPEN_HANDS_LIGHT_SKIN_TONE = "\U0001f450\U0001f3fb" - OPEN_HANDS_MEDIUM_LIGHT_SKIN_TONE = "\U0001f450\U0001f3fc" - OPEN_HANDS_MEDIUM_SKIN_TONE = "\U0001f450\U0001f3fd" - OPEN_HANDS_MEDIUM_DARK_SKIN_TONE = "\U0001f450\U0001f3fe" - OPEN_HANDS_DARK_SKIN_TONE = "\U0001f450\U0001f3ff" - RAISING_HANDS = "\U0001f64c" - RAISING_HANDS_LIGHT_SKIN_TONE = "\U0001f64c\U0001f3fb" - RAISING_HANDS_MEDIUM_LIGHT_SKIN_TONE = "\U0001f64c\U0001f3fc" - RAISING_HANDS_MEDIUM_SKIN_TONE = "\U0001f64c\U0001f3fd" - RAISING_HANDS_MEDIUM_DARK_SKIN_TONE = "\U0001f64c\U0001f3fe" - RAISING_HANDS_DARK_SKIN_TONE = "\U0001f64c\U0001f3ff" - PALMS_UP_TOGETHER = "\U0001f932" - PALMS_UP_TOGETHER_LIGHT_SKIN_TONE = "\U0001f932\U0001f3fb" - PALMS_UP_TOGETHER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f932\U0001f3fc" - PALMS_UP_TOGETHER_MEDIUM_SKIN_TONE = "\U0001f932\U0001f3fd" - PALMS_UP_TOGETHER_MEDIUM_DARK_SKIN_TONE = "\U0001f932\U0001f3fe" - PALMS_UP_TOGETHER_DARK_SKIN_TONE = "\U0001f932\U0001f3ff" - FOLDED_HANDS = "\U0001f64f" - FOLDED_HANDS_LIGHT_SKIN_TONE = "\U0001f64f\U0001f3fb" - FOLDED_HANDS_MEDIUM_LIGHT_SKIN_TONE = "\U0001f64f\U0001f3fc" - FOLDED_HANDS_MEDIUM_SKIN_TONE = "\U0001f64f\U0001f3fd" - FOLDED_HANDS_MEDIUM_DARK_SKIN_TONE = "\U0001f64f\U0001f3fe" - FOLDED_HANDS_DARK_SKIN_TONE = "\U0001f64f\U0001f3ff" - HANDSHAKE = "\U0001f91d" - NAIL_POLISH = "\U0001f485" - NAIL_POLISH_LIGHT_SKIN_TONE = "\U0001f485\U0001f3fb" - NAIL_POLISH_MEDIUM_LIGHT_SKIN_TONE = "\U0001f485\U0001f3fc" - NAIL_POLISH_MEDIUM_SKIN_TONE = "\U0001f485\U0001f3fd" - NAIL_POLISH_MEDIUM_DARK_SKIN_TONE = "\U0001f485\U0001f3fe" - NAIL_POLISH_DARK_SKIN_TONE = "\U0001f485\U0001f3ff" - EAR = "\U0001f442" - EAR_LIGHT_SKIN_TONE = "\U0001f442\U0001f3fb" - EAR_MEDIUM_LIGHT_SKIN_TONE = "\U0001f442\U0001f3fc" - EAR_MEDIUM_SKIN_TONE = "\U0001f442\U0001f3fd" - EAR_MEDIUM_DARK_SKIN_TONE = "\U0001f442\U0001f3fe" - EAR_DARK_SKIN_TONE = "\U0001f442\U0001f3ff" - NOSE = "\U0001f443" - NOSE_LIGHT_SKIN_TONE = "\U0001f443\U0001f3fb" - NOSE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f443\U0001f3fc" - NOSE_MEDIUM_SKIN_TONE = "\U0001f443\U0001f3fd" - NOSE_MEDIUM_DARK_SKIN_TONE = "\U0001f443\U0001f3fe" - NOSE_DARK_SKIN_TONE = "\U0001f443\U0001f3ff" - RED_HAIR = "\U0001f9b0" - CURLY_HAIR = "\U0001f9b1" - BALD = "\U0001f9b2" - WHITE_HAIR = "\U0001f9b3" - FOOTPRINTS = "\U0001f463" - EYES = "\U0001f440" - EYE = "\U0001f441\ufe0f" - EYE_IN_SPEECH_BUBBLE = "\U0001f441\u200d\U0001f5e8" - BRAIN = "\U0001f9e0" - BONE = "\U0001f9b4" - TOOTH = "\U0001f9b7" - TONGUE = "\U0001f445" - MOUTH = "\U0001f444" - KISS_MARK = "\U0001f48b" - HEART_WITH_ARROW = "\U0001f498" - RED_HEART = "\u2764\ufe0f" - BEATING_HEART = "\U0001f493" - BROKEN_HEART = "\U0001f494" - TWO_HEARTS = "\U0001f495" - SPARKLING_HEART = "\U0001f496" - GROWING_HEART = "\U0001f497" - BLUE_HEART = "\U0001f499" - GREEN_HEART = "\U0001f49a" - YELLOW_HEART = "\U0001f49b" - ORANGE_HEART = "\U0001f9e1" - PURPLE_HEART = "\U0001f49c" - BLACK_HEART = "\U0001f5a4" - HEART_WITH_RIBBON = "\U0001f49d" - REVOLVING_HEARTS = "\U0001f49e" - HEART_DECORATION = "\U0001f49f" - HEAVY_HEART_EXCLAMATION = "\u2763\ufe0f" - LOVE_LETTER = "\U0001f48c" - ZZZ = "\U0001f4a4" - ANGER_SYMBOL = "\U0001f4a2" - BOMB = "\U0001f4a3" - COLLISION = "\U0001f4a5" - SWEAT_DROPLETS = "\U0001f4a6" - DASHING_AWAY = "\U0001f4a8" - DIZZY = "\U0001f4ab" - SPEECH_BALLOON = "\U0001f4ac" - LEFT_SPEECH_BUBBLE = "\U0001f5e8\ufe0f" - RIGHT_ANGER_BUBBLE = "\U0001f5ef\ufe0f" - THOUGHT_BALLOON = "\U0001f4ad" - HOLE = "\U0001f573\ufe0f" - GLASSES = "\U0001f453" - SUNGLASSES = "\U0001f576\ufe0f" - GOGGLES = "\U0001f97d" - LAB_COAT = "\U0001f97c" - NECKTIE = "\U0001f454" - T_SHIRT = "\U0001f455" - JEANS = "\U0001f456" - SCARF = "\U0001f9e3" - GLOVES = "\U0001f9e4" - COAT = "\U0001f9e5" - SOCKS = "\U0001f9e6" - DRESS = "\U0001f457" - KIMONO = "\U0001f458" - BIKINI = "\U0001f459" - WOMAN_S_CLOTHES = "\U0001f45a" - PURSE = "\U0001f45b" - HANDBAG = "\U0001f45c" - CLUTCH_BAG = "\U0001f45d" - SHOPPING_BAGS = "\U0001f6cd\ufe0f" - BACKPACK = "\U0001f392" - MAN_S_SHOE = "\U0001f45e" - RUNNING_SHOE = "\U0001f45f" - HIKING_BOOT = "\U0001f97e" - FLAT_SHOE = "\U0001f97f" - HIGH_HEELED_SHOE = "\U0001f460" - WOMAN_S_SANDAL = "\U0001f461" - WOMAN_S_BOOT = "\U0001f462" - CROWN = "\U0001f451" - WOMAN_S_HAT = "\U0001f452" - TOP_HAT = "\U0001f3a9" - GRADUATION_CAP = "\U0001f393" - BILLED_CAP = "\U0001f9e2" - RESCUE_WORKER_S_HELMET = "\u26d1\ufe0f" - PRAYER_BEADS = "\U0001f4ff" - LIPSTICK = "\U0001f484" - RING = "\U0001f48d" - GEM_STONE = "\U0001f48e" - MONKEY_FACE = "\U0001f435" - MONKEY = "\U0001f412" - GORILLA = "\U0001f98d" - DOG_FACE = "\U0001f436" - DOG = "\U0001f415" - POODLE = "\U0001f429" - WOLF_FACE = "\U0001f43a" - FOX_FACE = "\U0001f98a" - RACCOON = "\U0001f99d" - CAT_FACE = "\U0001f431" - CAT = "\U0001f408" - LION_FACE = "\U0001f981" - TIGER_FACE = "\U0001f42f" - TIGER = "\U0001f405" - LEOPARD = "\U0001f406" - HORSE_FACE = "\U0001f434" - HORSE = "\U0001f40e" - UNICORN_FACE = "\U0001f984" - ZEBRA = "\U0001f993" - DEER = "\U0001f98c" - COW_FACE = "\U0001f42e" - OX = "\U0001f402" - WATER_BUFFALO = "\U0001f403" - COW = "\U0001f404" - PIG_FACE = "\U0001f437" - PIG = "\U0001f416" - BOAR = "\U0001f417" - PIG_NOSE = "\U0001f43d" - RAM = "\U0001f40f" - EWE = "\U0001f411" - GOAT = "\U0001f410" - CAMEL = "\U0001f42a" - TWO_HUMP_CAMEL = "\U0001f42b" - LLAMA = "\U0001f999" - GIRAFFE = "\U0001f992" - ELEPHANT = "\U0001f418" - RHINOCEROS = "\U0001f98f" - HIPPOPOTAMUS = "\U0001f99b" - MOUSE_FACE = "\U0001f42d" - MOUSE = "\U0001f401" - RAT = "\U0001f400" - HAMSTER_FACE = "\U0001f439" - RABBIT_FACE = "\U0001f430" - RABBIT = "\U0001f407" - CHIPMUNK = "\U0001f43f\ufe0f" - HEDGEHOG = "\U0001f994" - BAT = "\U0001f987" - BEAR_FACE = "\U0001f43b" - KOALA = "\U0001f428" - PANDA_FACE = "\U0001f43c" - KANGAROO = "\U0001f998" - BADGER = "\U0001f9a1" - PAW_PRINTS = "\U0001f43e" - TURKEY_2 = "\U0001f983" - CHICKEN = "\U0001f414" - ROOSTER = "\U0001f413" - HATCHING_CHICK = "\U0001f423" - BABY_CHICK = "\U0001f424" - FRONT_FACING_BABY_CHICK = "\U0001f425" - BIRD = "\U0001f426" - PENGUIN = "\U0001f427" - DOVE = "\U0001f54a\ufe0f" - EAGLE = "\U0001f985" - DUCK = "\U0001f986" - SWAN = "\U0001f9a2" - OWL = "\U0001f989" - PEACOCK = "\U0001f99a" - PARROT = "\U0001f99c" - FROG_FACE = "\U0001f438" - CROCODILE = "\U0001f40a" - TURTLE = "\U0001f422" - LIZARD = "\U0001f98e" - SNAKE = "\U0001f40d" - DRAGON_FACE = "\U0001f432" - DRAGON = "\U0001f409" - SAUROPOD = "\U0001f995" - T_REX = "\U0001f996" - SPOUTING_WHALE = "\U0001f433" - WHALE = "\U0001f40b" - DOLPHIN = "\U0001f42c" - FISH = "\U0001f41f" - TROPICAL_FISH = "\U0001f420" - BLOWFISH = "\U0001f421" - SHARK = "\U0001f988" - OCTOPUS = "\U0001f419" - SPIRAL_SHELL = "\U0001f41a" - CRAB = "\U0001f980" - LOBSTER = "\U0001f99e" - SHRIMP = "\U0001f990" - SQUID = "\U0001f991" - SNAIL = "\U0001f40c" - BUTTERFLY = "\U0001f98b" - BUG = "\U0001f41b" - ANT = "\U0001f41c" - HONEYBEE = "\U0001f41d" - LADY_BEETLE = "\U0001f41e" - CRICKET = "\U0001f997" - SPIDER = "\U0001f577\ufe0f" - SPIDER_WEB = "\U0001f578\ufe0f" - SCORPION = "\U0001f982" - MOSQUITO = "\U0001f99f" - MICROBE = "\U0001f9a0" - BOUQUET = "\U0001f490" - CHERRY_BLOSSOM = "\U0001f338" - WHITE_FLOWER = "\U0001f4ae" - ROSETTE = "\U0001f3f5\ufe0f" - ROSE = "\U0001f339" - WILTED_FLOWER = "\U0001f940" - HIBISCUS = "\U0001f33a" - SUNFLOWER = "\U0001f33b" - BLOSSOM = "\U0001f33c" - TULIP = "\U0001f337" - SEEDLING = "\U0001f331" - EVERGREEN_TREE = "\U0001f332" - DECIDUOUS_TREE = "\U0001f333" - PALM_TREE = "\U0001f334" - CACTUS = "\U0001f335" - SHEAF_OF_RICE = "\U0001f33e" - HERB = "\U0001f33f" - SHAMROCK = "\u2618\ufe0f" - FOUR_LEAF_CLOVER = "\U0001f340" - MAPLE_LEAF = "\U0001f341" - FALLEN_LEAF = "\U0001f342" - LEAF_FLUTTERING_IN_WIND = "\U0001f343" - GRAPES = "\U0001f347" - MELON = "\U0001f348" - WATERMELON = "\U0001f349" - TANGERINE = "\U0001f34a" - LEMON = "\U0001f34b" - BANANA = "\U0001f34c" - PINEAPPLE = "\U0001f34d" - MANGO = "\U0001f96d" - RED_APPLE = "\U0001f34e" - GREEN_APPLE = "\U0001f34f" - PEAR = "\U0001f350" - PEACH = "\U0001f351" - CHERRIES = "\U0001f352" - STRAWBERRY = "\U0001f353" - KIWI_FRUIT = "\U0001f95d" - TOMATO = "\U0001f345" - COCONUT = "\U0001f965" - AVOCADO = "\U0001f951" - EGGPLANT = "\U0001f346" - POTATO = "\U0001f954" - CARROT = "\U0001f955" - EAR_OF_CORN = "\U0001f33d" - HOT_PEPPER = "\U0001f336\ufe0f" - CUCUMBER = "\U0001f952" - LEAFY_GREEN = "\U0001f96c" - BROCCOLI = "\U0001f966" - MUSHROOM = "\U0001f344" - PEANUTS = "\U0001f95c" - CHESTNUT = "\U0001f330" - BREAD = "\U0001f35e" - CROISSANT = "\U0001f950" - BAGUETTE_BREAD = "\U0001f956" - PRETZEL = "\U0001f968" - BAGEL = "\U0001f96f" - PANCAKES = "\U0001f95e" - CHEESE_WEDGE = "\U0001f9c0" - MEAT_ON_BONE = "\U0001f356" - POULTRY_LEG = "\U0001f357" - CUT_OF_MEAT = "\U0001f969" - BACON = "\U0001f953" - HAMBURGER = "\U0001f354" - FRENCH_FRIES = "\U0001f35f" - PIZZA = "\U0001f355" - HOT_DOG = "\U0001f32d" - SANDWICH = "\U0001f96a" - TACO = "\U0001f32e" - BURRITO = "\U0001f32f" - STUFFED_FLATBREAD = "\U0001f959" - EGG = "\U0001f95a" - COOKING = "\U0001f373" - SHALLOW_PAN_OF_FOOD = "\U0001f958" - POT_OF_FOOD = "\U0001f372" - BOWL_WITH_SPOON = "\U0001f963" - GREEN_SALAD = "\U0001f957" - POPCORN = "\U0001f37f" - SALT = "\U0001f9c2" - CANNED_FOOD = "\U0001f96b" - BENTO_BOX = "\U0001f371" - RICE_CRACKER = "\U0001f358" - RICE_BALL = "\U0001f359" - COOKED_RICE = "\U0001f35a" - CURRY_RICE = "\U0001f35b" - STEAMING_BOWL = "\U0001f35c" - SPAGHETTI = "\U0001f35d" - ROASTED_SWEET_POTATO = "\U0001f360" - ODEN = "\U0001f362" - SUSHI = "\U0001f363" - FRIED_SHRIMP = "\U0001f364" - FISH_CAKE_WITH_SWIRL = "\U0001f365" - MOON_CAKE = "\U0001f96e" - DANGO = "\U0001f361" - DUMPLING = "\U0001f95f" - FORTUNE_COOKIE = "\U0001f960" - TAKEOUT_BOX = "\U0001f961" - SOFT_ICE_CREAM = "\U0001f366" - SHAVED_ICE = "\U0001f367" - ICE_CREAM = "\U0001f368" - DOUGHNUT = "\U0001f369" - COOKIE = "\U0001f36a" - BIRTHDAY_CAKE = "\U0001f382" - SHORTCAKE = "\U0001f370" - CUPCAKE = "\U0001f9c1" - PIE = "\U0001f967" - CHOCOLATE_BAR = "\U0001f36b" - CANDY = "\U0001f36c" - LOLLIPOP = "\U0001f36d" - CUSTARD = "\U0001f36e" - HONEY_POT = "\U0001f36f" - BABY_BOTTLE = "\U0001f37c" - GLASS_OF_MILK = "\U0001f95b" - HOT_BEVERAGE = "\u2615" - TEACUP_WITHOUT_HANDLE = "\U0001f375" - SAKE = "\U0001f376" - BOTTLE_WITH_POPPING_CORK = "\U0001f37e" - WINE_GLASS = "\U0001f377" - COCKTAIL_GLASS = "\U0001f378" - TROPICAL_DRINK = "\U0001f379" - BEER_MUG = "\U0001f37a" - CLINKING_BEER_MUGS = "\U0001f37b" - CLINKING_GLASSES = "\U0001f942" - TUMBLER_GLASS = "\U0001f943" - CUP_WITH_STRAW = "\U0001f964" - CHOPSTICKS = "\U0001f962" - FORK_AND_KNIFE_WITH_PLATE = "\U0001f37d\ufe0f" - FORK_AND_KNIFE = "\U0001f374" - SPOON = "\U0001f944" - KITCHEN_KNIFE = "\U0001f52a" - AMPHORA = "\U0001f3fa" - GLOBE_SHOWING_EUROPE_AFRICA = "\U0001f30d" - GLOBE_SHOWING_AMERICAS = "\U0001f30e" - GLOBE_SHOWING_ASIA_AUSTRALIA = "\U0001f30f" - GLOBE_WITH_MERIDIANS = "\U0001f310" - WORLD_MAP = "\U0001f5fa\ufe0f" - MAP_OF_JAPAN = "\U0001f5fe" - COMPASS = "\U0001f9ed" - SNOW_CAPPED_MOUNTAIN = "\U0001f3d4\ufe0f" - MOUNTAIN = "\u26f0\ufe0f" - VOLCANO = "\U0001f30b" - MOUNT_FUJI = "\U0001f5fb" - CAMPING = "\U0001f3d5\ufe0f" - BEACH_WITH_UMBRELLA = "\U0001f3d6\ufe0f" - DESERT = "\U0001f3dc\ufe0f" - DESERT_ISLAND = "\U0001f3dd\ufe0f" - NATIONAL_PARK = "\U0001f3de\ufe0f" - STADIUM = "\U0001f3df\ufe0f" - CLASSICAL_BUILDING = "\U0001f3db\ufe0f" - BUILDING_CONSTRUCTION = "\U0001f3d7\ufe0f" - HOUSES = "\U0001f3d8\ufe0f" - DERELICT_HOUSE = "\U0001f3da\ufe0f" - HOUSE = "\U0001f3e0" - HOUSE_WITH_GARDEN = "\U0001f3e1" - BRICK = "\U0001f9f1" - OFFICE_BUILDING = "\U0001f3e2" - JAPANESE_POST_OFFICE = "\U0001f3e3" - POST_OFFICE = "\U0001f3e4" - HOSPITAL = "\U0001f3e5" - BANK = "\U0001f3e6" - HOTEL = "\U0001f3e8" - LOVE_HOTEL = "\U0001f3e9" - CONVENIENCE_STORE = "\U0001f3ea" - SCHOOL = "\U0001f3eb" - DEPARTMENT_STORE = "\U0001f3ec" - FACTORY = "\U0001f3ed" - JAPANESE_CASTLE = "\U0001f3ef" - CASTLE = "\U0001f3f0" - WEDDING = "\U0001f492" - TOKYO_TOWER = "\U0001f5fc" - STATUE_OF_LIBERTY = "\U0001f5fd" - CHURCH = "\u26ea" - MOSQUE = "\U0001f54c" - SYNAGOGUE = "\U0001f54d" - SHINTO_SHRINE = "\u26e9\ufe0f" - KAABA = "\U0001f54b" - FOUNTAIN = "\u26f2" - TENT = "\u26fa" - FOGGY = "\U0001f301" - NIGHT_WITH_STARS = "\U0001f303" - CITYSCAPE = "\U0001f3d9\ufe0f" - SUNRISE_OVER_MOUNTAINS = "\U0001f304" - SUNRISE = "\U0001f305" - CITYSCAPE_AT_DUSK = "\U0001f306" - SUNSET = "\U0001f307" - BRIDGE_AT_NIGHT = "\U0001f309" - HOT_SPRINGS = "\u2668\ufe0f" - MILKY_WAY = "\U0001f30c" - CAROUSEL_HORSE = "\U0001f3a0" - FERRIS_WHEEL = "\U0001f3a1" - ROLLER_COASTER = "\U0001f3a2" - BARBER_POLE = "\U0001f488" - CIRCUS_TENT = "\U0001f3aa" - LOCOMOTIVE = "\U0001f682" - RAILWAY_CAR = "\U0001f683" - HIGH_SPEED_TRAIN = "\U0001f684" - BULLET_TRAIN = "\U0001f685" - TRAIN = "\U0001f686" - METRO = "\U0001f687" - LIGHT_RAIL = "\U0001f688" - STATION = "\U0001f689" - TRAM = "\U0001f68a" - MONORAIL = "\U0001f69d" - MOUNTAIN_RAILWAY = "\U0001f69e" - TRAM_CAR = "\U0001f68b" - BUS = "\U0001f68c" - ONCOMING_BUS = "\U0001f68d" - TROLLEYBUS = "\U0001f68e" - MINIBUS = "\U0001f690" - AMBULANCE = "\U0001f691" - FIRE_ENGINE = "\U0001f692" - POLICE_CAR = "\U0001f693" - ONCOMING_POLICE_CAR = "\U0001f694" - TAXI = "\U0001f695" - ONCOMING_TAXI = "\U0001f696" - AUTOMOBILE = "\U0001f697" - ONCOMING_AUTOMOBILE = "\U0001f698" - SPORT_UTILITY_VEHICLE = "\U0001f699" - DELIVERY_TRUCK = "\U0001f69a" - ARTICULATED_LORRY = "\U0001f69b" - TRACTOR = "\U0001f69c" - BICYCLE = "\U0001f6b2" - KICK_SCOOTER = "\U0001f6f4" - SKATEBOARD = "\U0001f6f9" - MOTOR_SCOOTER = "\U0001f6f5" - BUS_STOP = "\U0001f68f" - MOTORWAY = "\U0001f6e3\ufe0f" - RAILWAY_TRACK = "\U0001f6e4\ufe0f" - OIL_DRUM = "\U0001f6e2\ufe0f" - FUEL_PUMP = "\u26fd" - POLICE_CAR_LIGHT = "\U0001f6a8" - HORIZONTAL_TRAFFIC_LIGHT = "\U0001f6a5" - VERTICAL_TRAFFIC_LIGHT = "\U0001f6a6" - STOP_SIGN = "\U0001f6d1" - CONSTRUCTION = "\U0001f6a7" - ANCHOR = "\u2693" - SAILBOAT = "\u26f5" - CANOE = "\U0001f6f6" - SPEEDBOAT = "\U0001f6a4" - PASSENGER_SHIP = "\U0001f6f3\ufe0f" - FERRY = "\u26f4\ufe0f" - MOTOR_BOAT = "\U0001f6e5\ufe0f" - SHIP = "\U0001f6a2" - AIRPLANE = "\u2708\ufe0f" - SMALL_AIRPLANE = "\U0001f6e9\ufe0f" - AIRPLANE_DEPARTURE = "\U0001f6eb" - AIRPLANE_ARRIVAL = "\U0001f6ec" - SEAT = "\U0001f4ba" - HELICOPTER = "\U0001f681" - SUSPENSION_RAILWAY = "\U0001f69f" - MOUNTAIN_CABLEWAY = "\U0001f6a0" - AERIAL_TRAMWAY = "\U0001f6a1" - SATELLITE = "\U0001f6f0\ufe0f" - ROCKET = "\U0001f680" - FLYING_SAUCER = "\U0001f6f8" - BELLHOP_BELL = "\U0001f6ce\ufe0f" - LUGGAGE = "\U0001f9f3" - HOURGLASS_DONE = "\u231b" - HOURGLASS_NOT_DONE = "\u23f3" - WATCH = "\u231a" - ALARM_CLOCK = "\u23f0" - STOPWATCH = "\u23f1\ufe0f" - TIMER_CLOCK = "\u23f2\ufe0f" - MANTELPIECE_CLOCK = "\U0001f570\ufe0f" - TWELVE_O_CLOCK = "\U0001f55b" - TWELVE_THIRTY = "\U0001f567" - ONE_O_CLOCK = "\U0001f550" - ONE_THIRTY = "\U0001f55c" - TWO_O_CLOCK = "\U0001f551" - TWO_THIRTY = "\U0001f55d" - THREE_O_CLOCK = "\U0001f552" - THREE_THIRTY = "\U0001f55e" - FOUR_O_CLOCK = "\U0001f553" - FOUR_THIRTY = "\U0001f55f" - FIVE_O_CLOCK = "\U0001f554" - FIVE_THIRTY = "\U0001f560" - SIX_O_CLOCK = "\U0001f555" - SIX_THIRTY = "\U0001f561" - SEVEN_O_CLOCK = "\U0001f556" - SEVEN_THIRTY = "\U0001f562" - EIGHT_O_CLOCK = "\U0001f557" - EIGHT_THIRTY = "\U0001f563" - NINE_O_CLOCK = "\U0001f558" - NINE_THIRTY = "\U0001f564" - TEN_O_CLOCK = "\U0001f559" - TEN_THIRTY = "\U0001f565" - ELEVEN_O_CLOCK = "\U0001f55a" - ELEVEN_THIRTY = "\U0001f566" - NEW_MOON = "\U0001f311" - WAXING_CRESCENT_MOON = "\U0001f312" - FIRST_QUARTER_MOON = "\U0001f313" - WAXING_GIBBOUS_MOON = "\U0001f314" - FULL_MOON = "\U0001f315" - WANING_GIBBOUS_MOON = "\U0001f316" - LAST_QUARTER_MOON = "\U0001f317" - WANING_CRESCENT_MOON = "\U0001f318" - CRESCENT_MOON = "\U0001f319" - NEW_MOON_FACE = "\U0001f31a" - FIRST_QUARTER_MOON_FACE = "\U0001f31b" - LAST_QUARTER_MOON_FACE = "\U0001f31c" - THERMOMETER = "\U0001f321\ufe0f" - SUN = "\u2600\ufe0f" - FULL_MOON_FACE = "\U0001f31d" - SUN_WITH_FACE = "\U0001f31e" - STAR = "\u2b50" - GLOWING_STAR = "\U0001f31f" - SHOOTING_STAR = "\U0001f320" - CLOUD = "\u2601\ufe0f" - SUN_BEHIND_CLOUD = "\u26c5" - CLOUD_WITH_LIGHTNING_AND_RAIN = "\u26c8\ufe0f" - SUN_BEHIND_SMALL_CLOUD = "\U0001f324\ufe0f" - SUN_BEHIND_LARGE_CLOUD = "\U0001f325\ufe0f" - SUN_BEHIND_RAIN_CLOUD = "\U0001f326\ufe0f" - CLOUD_WITH_RAIN = "\U0001f327\ufe0f" - CLOUD_WITH_SNOW = "\U0001f328\ufe0f" - CLOUD_WITH_LIGHTNING = "\U0001f329\ufe0f" - TORNADO = "\U0001f32a\ufe0f" - FOG = "\U0001f32b\ufe0f" - WIND_FACE = "\U0001f32c\ufe0f" - CYCLONE = "\U0001f300" - RAINBOW = "\U0001f308" - CLOSED_UMBRELLA = "\U0001f302" - UMBRELLA = "\u2602\ufe0f" - UMBRELLA_WITH_RAIN_DROPS = "\u2614" - UMBRELLA_ON_GROUND = "\u26f1\ufe0f" - HIGH_VOLTAGE = "\u26a1" - SNOWFLAKE = "\u2744\ufe0f" - SNOWMAN = "\u2603\ufe0f" - SNOWMAN_WITHOUT_SNOW = "\u26c4" - COMET = "\u2604\ufe0f" - FIRE = "\U0001f525" - DROPLET = "\U0001f4a7" - WATER_WAVE = "\U0001f30a" - JACK_O_LANTERN = "\U0001f383" - CHRISTMAS_TREE = "\U0001f384" - FIREWORKS = "\U0001f386" - SPARKLER = "\U0001f387" - FIRECRACKER = "\U0001f9e8" - SPARKLES = "\u2728" - BALLOON = "\U0001f388" - PARTY_POPPER = "\U0001f389" - CONFETTI_BALL = "\U0001f38a" - TANABATA_TREE = "\U0001f38b" - PINE_DECORATION = "\U0001f38d" - JAPANESE_DOLLS = "\U0001f38e" - CARP_STREAMER = "\U0001f38f" - WIND_CHIME = "\U0001f390" - MOON_VIEWING_CEREMONY = "\U0001f391" - RED_ENVELOPE = "\U0001f9e7" - RIBBON = "\U0001f380" - WRAPPED_GIFT = "\U0001f381" - REMINDER_RIBBON = "\U0001f397\ufe0f" - ADMISSION_TICKETS = "\U0001f39f\ufe0f" - TICKET = "\U0001f3ab" - MILITARY_MEDAL = "\U0001f396\ufe0f" - TROPHY = "\U0001f3c6" - SPORTS_MEDAL = "\U0001f3c5" - FIRST_PLACE_MEDAL = "\U0001f947" - SECOND_PLACE_MEDAL = "\U0001f948" - THIRD_PLACE_MEDAL = "\U0001f949" - SOCCER_BALL = "\u26bd" - BASEBALL = "\u26be" - SOFTBALL = "\U0001f94e" - BASKETBALL = "\U0001f3c0" - VOLLEYBALL = "\U0001f3d0" - AMERICAN_FOOTBALL = "\U0001f3c8" - RUGBY_FOOTBALL = "\U0001f3c9" - TENNIS = "\U0001f3be" - FLYING_DISC = "\U0001f94f" - BOWLING = "\U0001f3b3" - CRICKET_GAME = "\U0001f3cf" - FIELD_HOCKEY = "\U0001f3d1" - ICE_HOCKEY = "\U0001f3d2" - LACROSSE = "\U0001f94d" - PING_PONG = "\U0001f3d3" - BADMINTON = "\U0001f3f8" - BOXING_GLOVE = "\U0001f94a" - MARTIAL_ARTS_UNIFORM = "\U0001f94b" - GOAL_NET = "\U0001f945" - FLAG_IN_HOLE = "\u26f3" - ICE_SKATE = "\u26f8\ufe0f" - FISHING_POLE = "\U0001f3a3" - RUNNING_SHIRT = "\U0001f3bd" - SKIS = "\U0001f3bf" - SLED = "\U0001f6f7" - CURLING_STONE = "\U0001f94c" - DIRECT_HIT = "\U0001f3af" - POOL_8_BALL = "\U0001f3b1" - CRYSTAL_BALL = "\U0001f52e" - NAZAR_AMULET = "\U0001f9ff" - VIDEO_GAME = "\U0001f3ae" - JOYSTICK = "\U0001f579\ufe0f" - SLOT_MACHINE = "\U0001f3b0" - GAME_DIE = "\U0001f3b2" - JIGSAW = "\U0001f9e9" - TEDDY_BEAR = "\U0001f9f8" - SPADE_SUIT = "\u2660\ufe0f" - HEART_SUIT = "\u2665\ufe0f" - DIAMOND_SUIT = "\u2666\ufe0f" - CLUB_SUIT = "\u2663\ufe0f" - CHESS_PAWN = "\u265f\ufe0f" - JOKER = "\U0001f0cf" - MAHJONG_RED_DRAGON = "\U0001f004" - FLOWER_PLAYING_CARDS = "\U0001f3b4" - PERFORMING_ARTS = "\U0001f3ad" - FRAMED_PICTURE = "\U0001f5bc\ufe0f" - ARTIST_PALETTE = "\U0001f3a8" - MUTED_SPEAKER = "\U0001f507" - SPEAKER_LOW_VOLUME = "\U0001f508" - SPEAKER_MEDIUM_VOLUME = "\U0001f509" - SPEAKER_HIGH_VOLUME = "\U0001f50a" - LOUDSPEAKER = "\U0001f4e2" - MEGAPHONE = "\U0001f4e3" - POSTAL_HORN = "\U0001f4ef" - BELL = "\U0001f514" - BELL_WITH_SLASH = "\U0001f515" - MUSICAL_SCORE = "\U0001f3bc" - MUSICAL_NOTE = "\U0001f3b5" - MUSICAL_NOTES = "\U0001f3b6" - STUDIO_MICROPHONE = "\U0001f399\ufe0f" - LEVEL_SLIDER = "\U0001f39a\ufe0f" - CONTROL_KNOBS = "\U0001f39b\ufe0f" - MICROPHONE = "\U0001f3a4" - HEADPHONE = "\U0001f3a7" - RADIO = "\U0001f4fb" - SAXOPHONE = "\U0001f3b7" - GUITAR = "\U0001f3b8" - MUSICAL_KEYBOARD = "\U0001f3b9" - TRUMPET = "\U0001f3ba" - VIOLIN = "\U0001f3bb" - DRUM = "\U0001f941" - MOBILE_PHONE = "\U0001f4f1" - MOBILE_PHONE_WITH_ARROW = "\U0001f4f2" - TELEPHONE = "\u260e\ufe0f" - TELEPHONE_RECEIVER = "\U0001f4de" - PAGER = "\U0001f4df" - FAX_MACHINE = "\U0001f4e0" - BATTERY = "\U0001f50b" - ELECTRIC_PLUG = "\U0001f50c" - LAPTOP_COMPUTER = "\U0001f4bb" - DESKTOP_COMPUTER = "\U0001f5a5\ufe0f" - PRINTER = "\U0001f5a8\ufe0f" - KEYBOARD = "\u2328\ufe0f" - COMPUTER_MOUSE = "\U0001f5b1\ufe0f" - TRACKBALL = "\U0001f5b2\ufe0f" - COMPUTER_DISK = "\U0001f4bd" - FLOPPY_DISK = "\U0001f4be" - OPTICAL_DISK = "\U0001f4bf" - DVD = "\U0001f4c0" - ABACUS = "\U0001f9ee" - MOVIE_CAMERA = "\U0001f3a5" - FILM_FRAMES = "\U0001f39e\ufe0f" - FILM_PROJECTOR = "\U0001f4fd\ufe0f" - CLAPPER_BOARD = "\U0001f3ac" - TELEVISION = "\U0001f4fa" - CAMERA = "\U0001f4f7" - CAMERA_WITH_FLASH = "\U0001f4f8" - VIDEO_CAMERA = "\U0001f4f9" - VIDEOCASSETTE = "\U0001f4fc" - MAGNIFYING_GLASS_TILTED_LEFT = "\U0001f50d" - MAGNIFYING_GLASS_TILTED_RIGHT = "\U0001f50e" - CANDLE = "\U0001f56f\ufe0f" - LIGHT_BULB = "\U0001f4a1" - FLASHLIGHT = "\U0001f526" - RED_PAPER_LANTERN = "\U0001f3ee" - NOTEBOOK_WITH_DECORATIVE_COVER = "\U0001f4d4" - CLOSED_BOOK = "\U0001f4d5" - OPEN_BOOK = "\U0001f4d6" - GREEN_BOOK = "\U0001f4d7" - BLUE_BOOK = "\U0001f4d8" - ORANGE_BOOK = "\U0001f4d9" - BOOKS = "\U0001f4da" - NOTEBOOK = "\U0001f4d3" - LEDGER = "\U0001f4d2" - PAGE_WITH_CURL = "\U0001f4c3" - SCROLL = "\U0001f4dc" - PAGE_FACING_UP = "\U0001f4c4" - NEWSPAPER = "\U0001f4f0" - ROLLED_UP_NEWSPAPER = "\U0001f5de\ufe0f" - BOOKMARK_TABS = "\U0001f4d1" - BOOKMARK = "\U0001f516" - LABEL = "\U0001f3f7\ufe0f" - MONEY_BAG = "\U0001f4b0" - YEN_BANKNOTE = "\U0001f4b4" - DOLLAR_BANKNOTE = "\U0001f4b5" - EURO_BANKNOTE = "\U0001f4b6" - POUND_BANKNOTE = "\U0001f4b7" - MONEY_WITH_WINGS = "\U0001f4b8" - CREDIT_CARD = "\U0001f4b3" - RECEIPT = "\U0001f9fe" - CHART_INCREASING_WITH_YEN = "\U0001f4b9" - CURRENCY_EXCHANGE = "\U0001f4b1" - HEAVY_DOLLAR_SIGN = "\U0001f4b2" - ENVELOPE = "\u2709\ufe0f" - E_MAIL = "\U0001f4e7" - INCOMING_ENVELOPE = "\U0001f4e8" - ENVELOPE_WITH_ARROW = "\U0001f4e9" - OUTBOX_TRAY = "\U0001f4e4" - INBOX_TRAY = "\U0001f4e5" - PACKAGE = "\U0001f4e6" - CLOSED_MAILBOX_WITH_RAISED_FLAG = "\U0001f4eb" - CLOSED_MAILBOX_WITH_LOWERED_FLAG = "\U0001f4ea" - OPEN_MAILBOX_WITH_RAISED_FLAG = "\U0001f4ec" - OPEN_MAILBOX_WITH_LOWERED_FLAG = "\U0001f4ed" - POSTBOX = "\U0001f4ee" - BALLOT_BOX_WITH_BALLOT = "\U0001f5f3\ufe0f" - PENCIL = "\u270f\ufe0f" - BLACK_NIB = "\u2712\ufe0f" - FOUNTAIN_PEN = "\U0001f58b\ufe0f" - PEN = "\U0001f58a\ufe0f" - PAINTBRUSH = "\U0001f58c\ufe0f" - CRAYON = "\U0001f58d\ufe0f" - MEMO = "\U0001f4dd" - BRIEFCASE = "\U0001f4bc" - FILE_FOLDER = "\U0001f4c1" - OPEN_FILE_FOLDER = "\U0001f4c2" - CARD_INDEX_DIVIDERS = "\U0001f5c2\ufe0f" - CALENDAR = "\U0001f4c5" - TEAR_OFF_CALENDAR = "\U0001f4c6" - SPIRAL_NOTEPAD = "\U0001f5d2\ufe0f" - SPIRAL_CALENDAR = "\U0001f5d3\ufe0f" - CARD_INDEX = "\U0001f4c7" - CHART_INCREASING = "\U0001f4c8" - CHART_DECREASING = "\U0001f4c9" - BAR_CHART = "\U0001f4ca" - CLIPBOARD = "\U0001f4cb" - PUSHPIN = "\U0001f4cc" - ROUND_PUSHPIN = "\U0001f4cd" - PAPERCLIP = "\U0001f4ce" - LINKED_PAPERCLIPS = "\U0001f587\ufe0f" - STRAIGHT_RULER = "\U0001f4cf" - TRIANGULAR_RULER = "\U0001f4d0" - SCISSORS = "\u2702\ufe0f" - CARD_FILE_BOX = "\U0001f5c3\ufe0f" - FILE_CABINET = "\U0001f5c4\ufe0f" - WASTEBASKET = "\U0001f5d1\ufe0f" - LOCKED = "\U0001f512" - UNLOCKED = "\U0001f513" - LOCKED_WITH_PEN = "\U0001f50f" - LOCKED_WITH_KEY = "\U0001f510" - KEY = "\U0001f511" - OLD_KEY = "\U0001f5dd\ufe0f" - HAMMER = "\U0001f528" - PICK = "\u26cf\ufe0f" - HAMMER_AND_PICK = "\u2692\ufe0f" - HAMMER_AND_WRENCH = "\U0001f6e0\ufe0f" - DAGGER = "\U0001f5e1\ufe0f" - CROSSED_SWORDS = "\u2694\ufe0f" - PISTOL = "\U0001f52b" - BOW_AND_ARROW = "\U0001f3f9" - SHIELD = "\U0001f6e1\ufe0f" - WRENCH = "\U0001f527" - NUT_AND_BOLT = "\U0001f529" - GEAR = "\u2699\ufe0f" - CLAMP = "\U0001f5dc\ufe0f" - BALANCE_SCALE = "\u2696\ufe0f" - LINK = "\U0001f517" - CHAINS = "\u26d3\ufe0f" - TOOLBOX = "\U0001f9f0" - MAGNET = "\U0001f9f2" - ALEMBIC = "\u2697\ufe0f" - TEST_TUBE = "\U0001f9ea" - PETRI_DISH = "\U0001f9eb" - DNA = "\U0001f9ec" - FIRE_EXTINGUISHER = "\U0001f9ef" - MICROSCOPE = "\U0001f52c" - TELESCOPE = "\U0001f52d" - SATELLITE_ANTENNA = "\U0001f4e1" - SYRINGE = "\U0001f489" - PILL = "\U0001f48a" - DOOR = "\U0001f6aa" - BED = "\U0001f6cf\ufe0f" - COUCH_AND_LAMP = "\U0001f6cb\ufe0f" - TOILET = "\U0001f6bd" - SHOWER = "\U0001f6bf" - BATHTUB = "\U0001f6c1" - LOTION_BOTTLE = "\U0001f9f4" - THREAD = "\U0001f9f5" - YARN = "\U0001f9f6" - SAFETY_PIN = "\U0001f9f7" - BROOM = "\U0001f9f9" - BASKET = "\U0001f9fa" - ROLL_OF_PAPER = "\U0001f9fb" - SOAP = "\U0001f9fc" - SPONGE = "\U0001f9fd" - SHOPPING_CART = "\U0001f6d2" - CIGARETTE = "\U0001f6ac" - COFFIN = "\u26b0\ufe0f" - FUNERAL_URN = "\u26b1\ufe0f" - MOAI = "\U0001f5ff" - ATM_SIGN = "\U0001f3e7" - LITTER_IN_BIN_SIGN = "\U0001f6ae" - POTABLE_WATER = "\U0001f6b0" - WHEELCHAIR_SYMBOL = "\u267f" - MEN_S_ROOM = "\U0001f6b9" - WOMEN_S_ROOM = "\U0001f6ba" - RESTROOM = "\U0001f6bb" - BABY_SYMBOL = "\U0001f6bc" - WATER_CLOSET = "\U0001f6be" - PASSPORT_CONTROL = "\U0001f6c2" - CUSTOMS = "\U0001f6c3" - BAGGAGE_CLAIM = "\U0001f6c4" - LEFT_LUGGAGE = "\U0001f6c5" - WARNING = "\u26a0\ufe0f" - CHILDREN_CROSSING = "\U0001f6b8" - NO_ENTRY = "\u26d4" - PROHIBITED = "\U0001f6ab" - NO_BICYCLES = "\U0001f6b3" - NO_SMOKING = "\U0001f6ad" - NO_LITTERING = "\U0001f6af" - NON_POTABLE_WATER = "\U0001f6b1" - NO_PEDESTRIANS = "\U0001f6b7" - NO_MOBILE_PHONES = "\U0001f4f5" - NO_ONE_UNDER_EIGHTEEN = "\U0001f51e" - RADIOACTIVE = "\u2622\ufe0f" - BIOHAZARD = "\u2623\ufe0f" - UP_ARROW = "\u2b06\ufe0f" - UP_RIGHT_ARROW = "\u2197\ufe0f" - RIGHT_ARROW = "\u27a1\ufe0f" - DOWN_RIGHT_ARROW = "\u2198\ufe0f" - DOWN_ARROW = "\u2b07\ufe0f" - DOWN_LEFT_ARROW = "\u2199\ufe0f" - LEFT_ARROW = "\u2b05\ufe0f" - UP_LEFT_ARROW = "\u2196\ufe0f" - UP_DOWN_ARROW = "\u2195\ufe0f" - LEFT_RIGHT_ARROW = "\u2194\ufe0f" - RIGHT_ARROW_CURVING_LEFT = "\u21a9\ufe0f" - LEFT_ARROW_CURVING_RIGHT = "\u21aa\ufe0f" - RIGHT_ARROW_CURVING_UP = "\u2934\ufe0f" - RIGHT_ARROW_CURVING_DOWN = "\u2935\ufe0f" - CLOCKWISE_VERTICAL_ARROWS = "\U0001f503" - COUNTERCLOCKWISE_ARROWS_BUTTON = "\U0001f504" - BACK_ARROW = "\U0001f519" - END_ARROW = "\U0001f51a" - ON_ARROW = "\U0001f51b" - SOON_ARROW = "\U0001f51c" - TOP_ARROW = "\U0001f51d" - PLACE_OF_WORSHIP = "\U0001f6d0" - ATOM_SYMBOL = "\u269b\ufe0f" - INFINITY = "\u267e\ufe0f" - OM = "\U0001f549\ufe0f" - STAR_OF_DAVID = "\u2721\ufe0f" - WHEEL_OF_DHARMA = "\u2638\ufe0f" - YIN_YANG = "\u262f\ufe0f" - LATIN_CROSS = "\u271d\ufe0f" - ORTHODOX_CROSS = "\u2626\ufe0f" - STAR_AND_CRESCENT = "\u262a\ufe0f" - PEACE_SYMBOL = "\u262e\ufe0f" - MENORAH = "\U0001f54e" - DOTTED_SIX_POINTED_STAR = "\U0001f52f" - ARIES = "\u2648" - TAURUS = "\u2649" - GEMINI = "\u264a" - CANCER = "\u264b" - LEO = "\u264c" - VIRGO = "\u264d" - LIBRA = "\u264e" - SCORPIO = "\u264f" - SAGITTARIUS = "\u2650" - CAPRICORN = "\u2651" - AQUARIUS = "\u2652" - PISCES = "\u2653" - OPHIUCHUS = "\u26ce" - SHUFFLE_TRACKS_BUTTON = "\U0001f500" - REPEAT_BUTTON = "\U0001f501" - REPEAT_SINGLE_BUTTON = "\U0001f502" - PLAY_BUTTON = "\u25b6\ufe0f" - FAST_FORWARD_BUTTON = "\u23e9" - NEXT_TRACK_BUTTON = "\u23ed\ufe0f" - PLAY_OR_PAUSE_BUTTON = "\u23ef\ufe0f" - REVERSE_BUTTON = "\u25c0\ufe0f" - FAST_REVERSE_BUTTON = "\u23ea" - LAST_TRACK_BUTTON = "\u23ee\ufe0f" - UPWARDS_BUTTON = "\U0001f53c" - FAST_UP_BUTTON = "\u23eb" - DOWNWARDS_BUTTON = "\U0001f53d" - FAST_DOWN_BUTTON = "\u23ec" - PAUSE_BUTTON = "\u23f8\ufe0f" - STOP_BUTTON = "\u23f9\ufe0f" - RECORD_BUTTON = "\u23fa\ufe0f" - EJECT_BUTTON = "\u23cf\ufe0f" - CINEMA = "\U0001f3a6" - DIM_BUTTON = "\U0001f505" - BRIGHT_BUTTON = "\U0001f506" - ANTENNA_BARS = "\U0001f4f6" - VIBRATION_MODE = "\U0001f4f3" - MOBILE_PHONE_OFF = "\U0001f4f4" - FEMALE_SIGN = "\u2640\ufe0f" - MALE_SIGN = "\u2642\ufe0f" - MEDICAL_SYMBOL = "\u2695\ufe0f" - RECYCLING_SYMBOL = "\u267b\ufe0f" - FLEUR_DE_LIS = "\u269c\ufe0f" - TRIDENT_EMBLEM = "\U0001f531" - NAME_BADGE = "\U0001f4db" - JAPANESE_SYMBOL_FOR_BEGINNER = "\U0001f530" - HEAVY_LARGE_CIRCLE = "\u2b55" - WHITE_HEAVY_CHECK_MARK = "\u2705" - BALLOT_BOX_WITH_CHECK = "\u2611\ufe0f" - HEAVY_CHECK_MARK = "\u2714\ufe0f" - HEAVY_MULTIPLICATION_X = "\u2716\ufe0f" - CROSS_MARK = "\u274c" - CROSS_MARK_BUTTON = "\u274e" - HEAVY_PLUS_SIGN = "\u2795" - HEAVY_MINUS_SIGN = "\u2796" - HEAVY_DIVISION_SIGN = "\u2797" - CURLY_LOOP = "\u27b0" - DOUBLE_CURLY_LOOP = "\u27bf" - PART_ALTERNATION_MARK = "\u303d\ufe0f" - EIGHT_SPOKED_ASTERISK = "\u2733\ufe0f" - EIGHT_POINTED_STAR = "\u2734\ufe0f" - SPARKLE = "\u2747\ufe0f" - DOUBLE_EXCLAMATION_MARK = "\u203c\ufe0f" - EXCLAMATION_QUESTION_MARK = "\u2049\ufe0f" - QUESTION_MARK = "\u2753" - WHITE_QUESTION_MARK = "\u2754" - WHITE_EXCLAMATION_MARK = "\u2755" - EXCLAMATION_MARK = "\u2757" - WAVY_DASH = "\u3030\ufe0f" - COPYRIGHT = "\xa9\ufe0f" - REGISTERED = "\xae\ufe0f" - TRADE_MARK = "\u2122\ufe0f" - KEYCAP_NUMBER_SIGN = "#\ufe0f\u20e3" - KEYCAP_ASTERISK = "*\ufe0f\u20e3" - KEYCAP_DIGIT_ZERO = "0\ufe0f\u20e3" - KEYCAP_DIGIT_ONE = "1\ufe0f\u20e3" - KEYCAP_DIGIT_TWO = "2\ufe0f\u20e3" - KEYCAP_DIGIT_THREE = "3\ufe0f\u20e3" - KEYCAP_DIGIT_FOUR = "4\ufe0f\u20e3" - KEYCAP_DIGIT_FIVE = "5\ufe0f\u20e3" - KEYCAP_DIGIT_SIX = "6\ufe0f\u20e3" - KEYCAP_DIGIT_SEVEN = "7\ufe0f\u20e3" - KEYCAP_DIGIT_EIGHT = "8\ufe0f\u20e3" - KEYCAP_DIGIT_NINE = "9\ufe0f\u20e3" - KEYCAP_10 = "\U0001f51f" - HUNDRED_POINTS = "\U0001f4af" - INPUT_LATIN_UPPERCASE = "\U0001f520" - INPUT_LATIN_LOWERCASE = "\U0001f521" - INPUT_NUMBERS = "\U0001f522" - INPUT_SYMBOLS = "\U0001f523" - INPUT_LATIN_LETTERS = "\U0001f524" - A_BUTTON_BLOOD_TYPE = "\U0001f170\ufe0f" - AB_BUTTON_BLOOD_TYPE = "\U0001f18e" - B_BUTTON_BLOOD_TYPE = "\U0001f171\ufe0f" - CL_BUTTON = "\U0001f191" - COOL_BUTTON = "\U0001f192" - FREE_BUTTON = "\U0001f193" - INFORMATION = "\u2139\ufe0f" - ID_BUTTON = "\U0001f194" - CIRCLED_M = "\u24c2\ufe0f" - NEW_BUTTON = "\U0001f195" - NG_BUTTON = "\U0001f196" - O_BUTTON_BLOOD_TYPE = "\U0001f17e\ufe0f" - OK_BUTTON = "\U0001f197" - P_BUTTON = "\U0001f17f\ufe0f" - SOS_BUTTON = "\U0001f198" - UP_BUTTON = "\U0001f199" - VS_BUTTON = "\U0001f19a" - JAPANESE_HERE_BUTTON = "\U0001f201" - JAPANESE_SERVICE_CHARGE_BUTTON = "\U0001f202\ufe0f" - JAPANESE_MONTHLY_AMOUNT_BUTTON = "\U0001f237\ufe0f" - JAPANESE_NOT_FREE_OF_CHARGE_BUTTON = "\U0001f236" - JAPANESE_RESERVED_BUTTON = "\U0001f22f" - JAPANESE_BARGAIN_BUTTON = "\U0001f250" - JAPANESE_DISCOUNT_BUTTON = "\U0001f239" - JAPANESE_FREE_OF_CHARGE_BUTTON = "\U0001f21a" - JAPANESE_PROHIBITED_BUTTON = "\U0001f232" - JAPANESE_ACCEPTABLE_BUTTON = "\U0001f251" - JAPANESE_APPLICATION_BUTTON = "\U0001f238" - JAPANESE_PASSING_GRADE_BUTTON = "\U0001f234" - JAPANESE_VACANCY_BUTTON = "\U0001f233" - JAPANESE_CONGRATULATIONS_BUTTON = "\u3297\ufe0f" - JAPANESE_SECRET_BUTTON = "\u3299\ufe0f" - JAPANESE_OPEN_FOR_BUSINESS_BUTTON = "\U0001f23a" - JAPANESE_NO_VACANCY_BUTTON = "\U0001f235" - BLACK_SMALL_SQUARE = "\u25aa\ufe0f" - WHITE_SMALL_SQUARE = "\u25ab\ufe0f" - WHITE_MEDIUM_SQUARE = "\u25fb\ufe0f" - BLACK_MEDIUM_SQUARE = "\u25fc\ufe0f" - WHITE_MEDIUM_SMALL_SQUARE = "\u25fd" - BLACK_MEDIUM_SMALL_SQUARE = "\u25fe" - BLACK_LARGE_SQUARE = "\u2b1b" - WHITE_LARGE_SQUARE = "\u2b1c" - LARGE_ORANGE_DIAMOND = "\U0001f536" - LARGE_BLUE_DIAMOND = "\U0001f537" - SMALL_ORANGE_DIAMOND = "\U0001f538" - SMALL_BLUE_DIAMOND = "\U0001f539" - RED_TRIANGLE_POINTED_UP = "\U0001f53a" - RED_TRIANGLE_POINTED_DOWN = "\U0001f53b" - DIAMOND_WITH_A_DOT = "\U0001f4a0" - RADIO_BUTTON = "\U0001f518" - BLACK_SQUARE_BUTTON = "\U0001f532" - WHITE_SQUARE_BUTTON = "\U0001f533" - WHITE_CIRCLE = "\u26aa" - BLACK_CIRCLE = "\u26ab" - RED_CIRCLE = "\U0001f534" - BLUE_CIRCLE = "\U0001f535" - CHEQUERED_FLAG = "\U0001f3c1" - TRIANGULAR_FLAG = "\U0001f6a9" - CROSSED_FLAGS = "\U0001f38c" - BLACK_FLAG = "\U0001f3f4" - WHITE_FLAG = "\U0001f3f3\ufe0f" - RAINBOW_FLAG = "\U0001f3f3\ufe0f\u200d\U0001f308" - PIRATE_FLAG = "\U0001f3f4\u200d\u2620\ufe0f" - ASCENSION_ISLAND = "\U0001f1e6\U0001f1e8" - ANDORRA = "\U0001f1e6\U0001f1e9" - UNITED_ARAB_EMIRATES = "\U0001f1e6\U0001f1ea" - AFGHANISTAN = "\U0001f1e6\U0001f1eb" - ANTIGUA_AND_BARBUDA = "\U0001f1e6\U0001f1ec" - ANGUILLA = "\U0001f1e6\U0001f1ee" - ALBANIA = "\U0001f1e6\U0001f1f1" - ARMENIA = "\U0001f1e6\U0001f1f2" - ANGOLA = "\U0001f1e6\U0001f1f4" - ANTARCTICA = "\U0001f1e6\U0001f1f6" - ARGENTINA = "\U0001f1e6\U0001f1f7" - AMERICAN_SAMOA = "\U0001f1e6\U0001f1f8" - AUSTRIA = "\U0001f1e6\U0001f1f9" - AUSTRALIA = "\U0001f1e6\U0001f1fa" - ARUBA = "\U0001f1e6\U0001f1fc" - ALAND_ISLANDS = "\U0001f1e6\U0001f1fd" - AZERBAIJAN = "\U0001f1e6\U0001f1ff" - BOSNIA_AND_HERZEGOVINA = "\U0001f1e7\U0001f1e6" - BARBADOS = "\U0001f1e7\U0001f1e7" - BANGLADESH = "\U0001f1e7\U0001f1e9" - BELGIUM = "\U0001f1e7\U0001f1ea" - BURKINA_FASO = "\U0001f1e7\U0001f1eb" - BULGARIA = "\U0001f1e7\U0001f1ec" - BAHRAIN = "\U0001f1e7\U0001f1ed" - BURUNDI = "\U0001f1e7\U0001f1ee" - BENIN = "\U0001f1e7\U0001f1ef" - ST_BARTHELEMY = "\U0001f1e7\U0001f1f1" - BERMUDA = "\U0001f1e7\U0001f1f2" - BRUNEI = "\U0001f1e7\U0001f1f3" - BOLIVIA = "\U0001f1e7\U0001f1f4" - CARIBBEAN_NETHERLANDS = "\U0001f1e7\U0001f1f6" - BRAZIL = "\U0001f1e7\U0001f1f7" - BAHAMAS = "\U0001f1e7\U0001f1f8" - BHUTAN = "\U0001f1e7\U0001f1f9" - BOUVET_ISLAND = "\U0001f1e7\U0001f1fb" - BOTSWANA = "\U0001f1e7\U0001f1fc" - BELARUS = "\U0001f1e7\U0001f1fe" - BELIZE = "\U0001f1e7\U0001f1ff" - CANADA = "\U0001f1e8\U0001f1e6" - COCOS_KEELING_ISLANDS = "\U0001f1e8\U0001f1e8" - CONGO_KINSHASA = "\U0001f1e8\U0001f1e9" - CENTRAL_AFRICAN_REPUBLIC = "\U0001f1e8\U0001f1eb" - CONGO_BRAZZAVILLE = "\U0001f1e8\U0001f1ec" - SWITZERLAND = "\U0001f1e8\U0001f1ed" - COTE_D_IVOIRE = "\U0001f1e8\U0001f1ee" - COOK_ISLANDS = "\U0001f1e8\U0001f1f0" - CHILE = "\U0001f1e8\U0001f1f1" - CAMEROON = "\U0001f1e8\U0001f1f2" - CHINA = "\U0001f1e8\U0001f1f3" - COLOMBIA = "\U0001f1e8\U0001f1f4" - CLIPPERTON_ISLAND = "\U0001f1e8\U0001f1f5" - COSTA_RICA = "\U0001f1e8\U0001f1f7" - CUBA = "\U0001f1e8\U0001f1fa" - CAPE_VERDE = "\U0001f1e8\U0001f1fb" - CURACAO = "\U0001f1e8\U0001f1fc" - CHRISTMAS_ISLAND = "\U0001f1e8\U0001f1fd" - CYPRUS = "\U0001f1e8\U0001f1fe" - CZECHIA = "\U0001f1e8\U0001f1ff" - GERMANY = "\U0001f1e9\U0001f1ea" - DIEGO_GARCIA = "\U0001f1e9\U0001f1ec" - DJIBOUTI = "\U0001f1e9\U0001f1ef" - DENMARK = "\U0001f1e9\U0001f1f0" - DOMINICA = "\U0001f1e9\U0001f1f2" - DOMINICAN_REPUBLIC = "\U0001f1e9\U0001f1f4" - ALGERIA = "\U0001f1e9\U0001f1ff" - CEUTA_AND_MELILLA = "\U0001f1ea\U0001f1e6" - ECUADOR = "\U0001f1ea\U0001f1e8" - ESTONIA = "\U0001f1ea\U0001f1ea" - EGYPT = "\U0001f1ea\U0001f1ec" - WESTERN_SAHARA = "\U0001f1ea\U0001f1ed" - ERITREA = "\U0001f1ea\U0001f1f7" - SPAIN = "\U0001f1ea\U0001f1f8" - ETHIOPIA = "\U0001f1ea\U0001f1f9" - EUROPEAN_UNION = "\U0001f1ea\U0001f1fa" - FINLAND = "\U0001f1eb\U0001f1ee" - FIJI = "\U0001f1eb\U0001f1ef" - FALKLAND_ISLANDS = "\U0001f1eb\U0001f1f0" - MICRONESIA = "\U0001f1eb\U0001f1f2" - FAROE_ISLANDS = "\U0001f1eb\U0001f1f4" - FRANCE = "\U0001f1eb\U0001f1f7" - GABON = "\U0001f1ec\U0001f1e6" - UNITED_KINGDOM = "\U0001f1ec\U0001f1e7" - GRENADA = "\U0001f1ec\U0001f1e9" - GEORGIA = "\U0001f1ec\U0001f1ea" - FRENCH_GUIANA = "\U0001f1ec\U0001f1eb" - GUERNSEY = "\U0001f1ec\U0001f1ec" - GHANA = "\U0001f1ec\U0001f1ed" - GIBRALTAR = "\U0001f1ec\U0001f1ee" - GREENLAND = "\U0001f1ec\U0001f1f1" - GAMBIA = "\U0001f1ec\U0001f1f2" - GUINEA = "\U0001f1ec\U0001f1f3" - GUADELOUPE = "\U0001f1ec\U0001f1f5" - EQUATORIAL_GUINEA = "\U0001f1ec\U0001f1f6" - GREECE = "\U0001f1ec\U0001f1f7" - SOUTH_GEORGIA_AND_SOUTH_SANDWICH_ISLANDS = "\U0001f1ec\U0001f1f8" - GUATEMALA = "\U0001f1ec\U0001f1f9" - GUAM = "\U0001f1ec\U0001f1fa" - GUINEA_BISSAU = "\U0001f1ec\U0001f1fc" - GUYANA = "\U0001f1ec\U0001f1fe" - HONG_KONG_SAR_CHINA = "\U0001f1ed\U0001f1f0" - HEARD_AND_MCDONALD_ISLANDS = "\U0001f1ed\U0001f1f2" - HONDURAS = "\U0001f1ed\U0001f1f3" - CROATIA = "\U0001f1ed\U0001f1f7" - HAITI = "\U0001f1ed\U0001f1f9" - HUNGARY = "\U0001f1ed\U0001f1fa" - CANARY_ISLANDS = "\U0001f1ee\U0001f1e8" - INDONESIA = "\U0001f1ee\U0001f1e9" - IRELAND = "\U0001f1ee\U0001f1ea" - ISRAEL = "\U0001f1ee\U0001f1f1" - ISLE_OF_MAN = "\U0001f1ee\U0001f1f2" - INDIA = "\U0001f1ee\U0001f1f3" - BRITISH_INDIAN_OCEAN_TERRITORY = "\U0001f1ee\U0001f1f4" - IRAQ = "\U0001f1ee\U0001f1f6" - IRAN = "\U0001f1ee\U0001f1f7" - ICELAND = "\U0001f1ee\U0001f1f8" - ITALY = "\U0001f1ee\U0001f1f9" - JERSEY = "\U0001f1ef\U0001f1ea" - JAMAICA = "\U0001f1ef\U0001f1f2" - JORDAN = "\U0001f1ef\U0001f1f4" - JAPAN = "\U0001f1ef\U0001f1f5" - KENYA = "\U0001f1f0\U0001f1ea" - KYRGYZSTAN = "\U0001f1f0\U0001f1ec" - CAMBODIA = "\U0001f1f0\U0001f1ed" - KIRIBATI = "\U0001f1f0\U0001f1ee" - COMOROS = "\U0001f1f0\U0001f1f2" - ST_KITTS_AND_NEVIS = "\U0001f1f0\U0001f1f3" - NORTH_KOREA = "\U0001f1f0\U0001f1f5" - SOUTH_KOREA = "\U0001f1f0\U0001f1f7" - KUWAIT = "\U0001f1f0\U0001f1fc" - CAYMAN_ISLANDS = "\U0001f1f0\U0001f1fe" - KAZAKHSTAN = "\U0001f1f0\U0001f1ff" - LAOS = "\U0001f1f1\U0001f1e6" - LEBANON = "\U0001f1f1\U0001f1e7" - ST_LUCIA = "\U0001f1f1\U0001f1e8" - LIECHTENSTEIN = "\U0001f1f1\U0001f1ee" - SRI_LANKA = "\U0001f1f1\U0001f1f0" - LIBERIA = "\U0001f1f1\U0001f1f7" - LESOTHO = "\U0001f1f1\U0001f1f8" - LITHUANIA = "\U0001f1f1\U0001f1f9" - LUXEMBOURG = "\U0001f1f1\U0001f1fa" - LATVIA = "\U0001f1f1\U0001f1fb" - LIBYA = "\U0001f1f1\U0001f1fe" - MOROCCO = "\U0001f1f2\U0001f1e6" - MONACO = "\U0001f1f2\U0001f1e8" - MOLDOVA = "\U0001f1f2\U0001f1e9" - MONTENEGRO = "\U0001f1f2\U0001f1ea" - ST_MARTIN = "\U0001f1f2\U0001f1eb" - MADAGASCAR = "\U0001f1f2\U0001f1ec" - MARSHALL_ISLANDS = "\U0001f1f2\U0001f1ed" - MACEDONIA = "\U0001f1f2\U0001f1f0" - MALI = "\U0001f1f2\U0001f1f1" - MYANMAR_BURMA = "\U0001f1f2\U0001f1f2" - MONGOLIA = "\U0001f1f2\U0001f1f3" - MACAU_SAR_CHINA = "\U0001f1f2\U0001f1f4" - NORTHERN_MARIANA_ISLANDS = "\U0001f1f2\U0001f1f5" - MARTINIQUE = "\U0001f1f2\U0001f1f6" - MAURITANIA = "\U0001f1f2\U0001f1f7" - MONTSERRAT = "\U0001f1f2\U0001f1f8" - MALTA = "\U0001f1f2\U0001f1f9" - MAURITIUS = "\U0001f1f2\U0001f1fa" - MALDIVES = "\U0001f1f2\U0001f1fb" - MALAWI = "\U0001f1f2\U0001f1fc" - MEXICO = "\U0001f1f2\U0001f1fd" - MALAYSIA = "\U0001f1f2\U0001f1fe" - MOZAMBIQUE = "\U0001f1f2\U0001f1ff" - NAMIBIA = "\U0001f1f3\U0001f1e6" - NEW_CALEDONIA = "\U0001f1f3\U0001f1e8" - NIGER = "\U0001f1f3\U0001f1ea" - NORFOLK_ISLAND = "\U0001f1f3\U0001f1eb" - NIGERIA = "\U0001f1f3\U0001f1ec" - NICARAGUA = "\U0001f1f3\U0001f1ee" - NETHERLANDS = "\U0001f1f3\U0001f1f1" - NORWAY = "\U0001f1f3\U0001f1f4" - NEPAL = "\U0001f1f3\U0001f1f5" - NAURU = "\U0001f1f3\U0001f1f7" - NIUE = "\U0001f1f3\U0001f1fa" - NEW_ZEALAND = "\U0001f1f3\U0001f1ff" - OMAN = "\U0001f1f4\U0001f1f2" - PANAMA = "\U0001f1f5\U0001f1e6" - PERU = "\U0001f1f5\U0001f1ea" - FRENCH_POLYNESIA = "\U0001f1f5\U0001f1eb" - PAPUA_NEW_GUINEA = "\U0001f1f5\U0001f1ec" - PHILIPPINES = "\U0001f1f5\U0001f1ed" - PAKISTAN = "\U0001f1f5\U0001f1f0" - POLAND = "\U0001f1f5\U0001f1f1" - ST_PIERRE_AND_MIQUELON = "\U0001f1f5\U0001f1f2" - PITCAIRN_ISLANDS = "\U0001f1f5\U0001f1f3" - PUERTO_RICO = "\U0001f1f5\U0001f1f7" - PALESTINIAN_TERRITORIES = "\U0001f1f5\U0001f1f8" - PORTUGAL = "\U0001f1f5\U0001f1f9" - PALAU = "\U0001f1f5\U0001f1fc" - PARAGUAY = "\U0001f1f5\U0001f1fe" - QATAR = "\U0001f1f6\U0001f1e6" - REUNION = "\U0001f1f7\U0001f1ea" - ROMANIA = "\U0001f1f7\U0001f1f4" - SERBIA = "\U0001f1f7\U0001f1f8" - RUSSIA = "\U0001f1f7\U0001f1fa" - RWANDA = "\U0001f1f7\U0001f1fc" - SAUDI_ARABIA = "\U0001f1f8\U0001f1e6" - SOLOMON_ISLANDS = "\U0001f1f8\U0001f1e7" - SEYCHELLES = "\U0001f1f8\U0001f1e8" - SUDAN = "\U0001f1f8\U0001f1e9" - SWEDEN = "\U0001f1f8\U0001f1ea" - SINGAPORE = "\U0001f1f8\U0001f1ec" - ST_HELENA = "\U0001f1f8\U0001f1ed" - SLOVENIA = "\U0001f1f8\U0001f1ee" - SVALBARD_AND_JAN_MAYEN = "\U0001f1f8\U0001f1ef" - SLOVAKIA = "\U0001f1f8\U0001f1f0" - SIERRA_LEONE = "\U0001f1f8\U0001f1f1" - SAN_MARINO = "\U0001f1f8\U0001f1f2" - SENEGAL = "\U0001f1f8\U0001f1f3" - SOMALIA = "\U0001f1f8\U0001f1f4" - SURINAME = "\U0001f1f8\U0001f1f7" - SOUTH_SUDAN = "\U0001f1f8\U0001f1f8" - SAO_TOME_AND_PRINCIPE = "\U0001f1f8\U0001f1f9" - EL_SALVADOR = "\U0001f1f8\U0001f1fb" - SINT_MAARTEN = "\U0001f1f8\U0001f1fd" - SYRIA = "\U0001f1f8\U0001f1fe" - SWAZILAND = "\U0001f1f8\U0001f1ff" - TRISTAN_DA_CUNHA = "\U0001f1f9\U0001f1e6" - TURKS_AND_CAICOS_ISLANDS = "\U0001f1f9\U0001f1e8" - CHAD = "\U0001f1f9\U0001f1e9" - FRENCH_SOUTHERN_TERRITORIES = "\U0001f1f9\U0001f1eb" - TOGO = "\U0001f1f9\U0001f1ec" - THAILAND = "\U0001f1f9\U0001f1ed" - TAJIKISTAN = "\U0001f1f9\U0001f1ef" - TOKELAU = "\U0001f1f9\U0001f1f0" - TIMOR_LESTE = "\U0001f1f9\U0001f1f1" - TURKMENISTAN = "\U0001f1f9\U0001f1f2" - TUNISIA = "\U0001f1f9\U0001f1f3" - TONGA = "\U0001f1f9\U0001f1f4" - TURKEY = "\U0001f1f9\U0001f1f7" - TRINIDAD_AND_TOBAGO = "\U0001f1f9\U0001f1f9" - TUVALU = "\U0001f1f9\U0001f1fb" - TAIWAN = "\U0001f1f9\U0001f1fc" - TANZANIA = "\U0001f1f9\U0001f1ff" - UKRAINE = "\U0001f1fa\U0001f1e6" - UGANDA = "\U0001f1fa\U0001f1ec" - U_S_OUTLYING_ISLANDS = "\U0001f1fa\U0001f1f2" - UNITED_NATIONS = "\U0001f1fa\U0001f1f3" - UNITED_STATES = "\U0001f1fa\U0001f1f8" - URUGUAY = "\U0001f1fa\U0001f1fe" - UZBEKISTAN = "\U0001f1fa\U0001f1ff" - VATICAN_CITY = "\U0001f1fb\U0001f1e6" - ST_VINCENT_AND_GRENADINES = "\U0001f1fb\U0001f1e8" - VENEZUELA = "\U0001f1fb\U0001f1ea" - BRITISH_VIRGIN_ISLANDS = "\U0001f1fb\U0001f1ec" - U_S_VIRGIN_ISLANDS = "\U0001f1fb\U0001f1ee" - VIETNAM = "\U0001f1fb\U0001f1f3" - VANUATU = "\U0001f1fb\U0001f1fa" - WALLIS_AND_FUTUNA = "\U0001f1fc\U0001f1eb" - SAMOA = "\U0001f1fc\U0001f1f8" - KOSOVO = "\U0001f1fd\U0001f1f0" - YEMEN = "\U0001f1fe\U0001f1ea" - MAYOTTE = "\U0001f1fe\U0001f1f9" - SOUTH_AFRICA = "\U0001f1ff\U0001f1e6" - ZAMBIA = "\U0001f1ff\U0001f1f2" - ZIMBABWE = "\U0001f1ff\U0001f1fc" - ENGLAND = "\U0001f3f4\U000e0067\U000e0062\U000e0065\U000e006e\U000e0067\U000e007f" - SCOTLAND = "\U0001f3f4\U000e0067\U000e0062\U000e0073\U000e0063\U000e0074\U000e007f" - WALES = "\U0001f3f4\U000e0067\U000e0062\U000e0077\U000e006c\U000e0073\U000e007f" - MODERN_PENTATHLON = "\U0001f93b" - RIFLE = "\U0001f946" - REGIONAL_INDICATOR_SYMBOL_LETTER_A = "\U0001f1e6" - REGIONAL_INDICATOR_SYMBOL_LETTER_B = "\U0001f1e7" - REGIONAL_INDICATOR_SYMBOL_LETTER_C = "\U0001f1e8" - REGIONAL_INDICATOR_SYMBOL_LETTER_D = "\U0001f1e9" - REGIONAL_INDICATOR_SYMBOL_LETTER_E = "\U0001f1ea" - REGIONAL_INDICATOR_SYMBOL_LETTER_F = "\U0001f1eb" - REGIONAL_INDICATOR_SYMBOL_LETTER_G = "\U0001f1ec" - REGIONAL_INDICATOR_SYMBOL_LETTER_H = "\U0001f1ed" - REGIONAL_INDICATOR_SYMBOL_LETTER_I = "\U0001f1ee" - REGIONAL_INDICATOR_SYMBOL_LETTER_J = "\U0001f1ef" - REGIONAL_INDICATOR_SYMBOL_LETTER_K = "\U0001f1f0" - REGIONAL_INDICATOR_SYMBOL_LETTER_L = "\U0001f1f1" - REGIONAL_INDICATOR_SYMBOL_LETTER_M = "\U0001f1f2" - REGIONAL_INDICATOR_SYMBOL_LETTER_N = "\U0001f1f3" - REGIONAL_INDICATOR_SYMBOL_LETTER_O = "\U0001f1f4" - REGIONAL_INDICATOR_SYMBOL_LETTER_P = "\U0001f1f5" - REGIONAL_INDICATOR_SYMBOL_LETTER_Q = "\U0001f1f6" - REGIONAL_INDICATOR_SYMBOL_LETTER_R = "\U0001f1f7" - REGIONAL_INDICATOR_SYMBOL_LETTER_S = "\U0001f1f8" - REGIONAL_INDICATOR_SYMBOL_LETTER_T = "\U0001f1f9" - REGIONAL_INDICATOR_SYMBOL_LETTER_U = "\U0001f1fa" - REGIONAL_INDICATOR_SYMBOL_LETTER_V = "\U0001f1fb" - REGIONAL_INDICATOR_SYMBOL_LETTER_W = "\U0001f1fc" - REGIONAL_INDICATOR_SYMBOL_LETTER_X = "\U0001f1fd" - REGIONAL_INDICATOR_SYMBOL_LETTER_Y = "\U0001f1fe" - REGIONAL_INDICATOR_SYMBOL_LETTER_Z = "\U0001f1ff" - DINO_CAT = "\U0001f431\u200d\U0001f409" - NINJA_CAT = "\U0001f431\u200d\U0001f464" - STUNT_CAT = "\U0001f431\u200d\U0001f3cd" - ASTRO_CAT = "\U0001f431\u200d\U0001f680" - HACKER_CAT = "\U0001f431\u200d\U0001f4bb" - HIPSTER_CAT = "\U0001f431\u200d\U0001f453" - OLYMPIC_RINGS = "\u25ef\u200d\u25ef\u200d\u25ef\u200d\u25ef\u200d\u25ef" - FLAG_FOR_SUKHBAATAR_MN_051 = "\U0001f3f4\U000e006d\U000e006e\U000e0030\U000e0035\U000e0031\U000e007f" - COUPLE_WITH_HEART_WOMAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fb" - FLAG_FOR_ORKHON_MN_035 = "\U0001f3f4\U000e006d\U000e006e\U000e0030\U000e0033\U000e0035\U000e007f" - FLAG_FOR_TIRIS_ZEMMOUR_MR_11 = "\U0001f3f4\U000e006d\U000e0072\U000e0031\U000e0031\U000e007f" - FLAG_FOR_KHOVD_MN_043 = "\U0001f3f4\U000e006d\U000e006e\U000e0030\U000e0034\U000e0033\U000e007f" - FLAG_FOR_ASSABA_MR_03 = "\U0001f3f4\U000e006d\U000e0072\U000e0030\U000e0033\U000e007f" - FLAG_FOR_BRAKNA_MR_05 = "\U0001f3f4\U000e006d\U000e0072\U000e0030\U000e0035\U000e007f" - FLAG_FOR_GORGOL_MR_04 = "\U0001f3f4\U000e006d\U000e0072\U000e0030\U000e0034\U000e007f" - FLAG_FOR_ARKHANGAI_MN_073 = "\U0001f3f4\U000e006d\U000e006e\U000e0030\U000e0037\U000e0033\U000e007f" - FLAG_FOR_AKMOLA_KZ_AKM = "\U0001f3f4\U000e006b\U000e007a\U000e0061\U000e006b\U000e006d\U000e007f" - FLAG_FOR_BIRKIRKARA_MT_04 = "\U0001f3f4\U000e006d\U000e0074\U000e0030\U000e0034\U000e007f" - FLAG_FOR_IKLIN_MT_19 = "\U0001f3f4\U000e006d\U000e0074\U000e0031\U000e0039\U000e007f" - FLAG_FOR_NAXXAR_MT_38 = "\U0001f3f4\U000e006d\U000e0074\U000e0033\U000e0038\U000e007f" - FLAG_FOR_KALKARA_MT_21 = "\U0001f3f4\U000e006d\U000e0074\U000e0032\U000e0031\U000e007f" - FLAG_FOR_FONTANA_MT_10 = "\U0001f3f4\U000e006d\U000e0074\U000e0031\U000e0030\U000e007f" - FLAG_FOR_LIJA_MT_24 = "\U0001f3f4\U000e006d\U000e0074\U000e0032\U000e0034\U000e007f" - FLAG_FOR_FGURA_MT_08 = "\U0001f3f4\U000e006d\U000e0074\U000e0030\U000e0038\U000e007f" - FLAG_FOR_GUDJA_MT_11 = "\U0001f3f4\U000e006d\U000e0074\U000e0031\U000e0031\U000e007f" - FLAG_FOR_NOUAKCHOTT_SUD_MR_15 = "\U0001f3f4\U000e006d\U000e0072\U000e0031\U000e0035\U000e007f" - FLAG_FOR_MUNXAR_MT_36 = "\U0001f3f4\U000e006d\U000e0074\U000e0033\U000e0036\U000e007f" - FLAG_FOR_G_AJNSIELEM_MT_13 = "\U0001f3f4\U000e006d\U000e0074\U000e0031\U000e0033\U000e007f" - FLAG_FOR_BIRGU_MT_03 = "\U0001f3f4\U000e006d\U000e0074\U000e0030\U000e0033\U000e007f" - FLAG_FOR_ATTARD_MT_01 = "\U0001f3f4\U000e006d\U000e0074\U000e0030\U000e0031\U000e007f" - FLAG_FOR_AMRUN_MT_18 = "\U0001f3f4\U000e006d\U000e0074\U000e0031\U000e0038\U000e007f" - FLAG_FOR_G_ARB_MT_14 = "\U0001f3f4\U000e006d\U000e0074\U000e0031\U000e0034\U000e007f" - FLAG_FOR_MARSAXLOKK_MT_28 = "\U0001f3f4\U000e006d\U000e0074\U000e0032\U000e0038\U000e007f" - FLAG_FOR_G_AXAQ_MT_17 = "\U0001f3f4\U000e006d\U000e0074\U000e0031\U000e0037\U000e007f" - FLAG_FOR_MQABBA_MT_33 = "\U0001f3f4\U000e006d\U000e0074\U000e0033\U000e0033\U000e007f" - FLAG_FOR_GZIRA_MT_12 = "\U0001f3f4\U000e006d\U000e0074\U000e0031\U000e0032\U000e007f" - FLAG_FOR_NADUR_MT_37 = "\U0001f3f4\U000e006d\U000e0074\U000e0033\U000e0037\U000e007f" - FLAG_FOR_MOSTA_MT_32 = "\U0001f3f4\U000e006d\U000e0074\U000e0033\U000e0032\U000e007f" - FLAG_FOR_MELLIE_A_MT_30 = "\U0001f3f4\U000e006d\U000e0074\U000e0033\U000e0030\U000e007f" - FLAG_FOR_PAOLA_MT_39 = "\U0001f3f4\U000e006d\U000e0074\U000e0033\U000e0039\U000e007f" - FLAG_FOR_KERCEM_MT_22 = "\U0001f3f4\U000e006d\U000e0074\U000e0032\U000e0032\U000e007f" - FLAG_FOR_KIRKOP_MT_23 = "\U0001f3f4\U000e006d\U000e0074\U000e0032\U000e0033\U000e007f" - FLAG_FOR_MSIDA_MT_34 = "\U0001f3f4\U000e006d\U000e0074\U000e0033\U000e0034\U000e007f" - FLAG_FOR_DINGLI_MT_07 = "\U0001f3f4\U000e006d\U000e0074\U000e0030\U000e0037\U000e007f" - FLAG_FOR_FLORIANA_MT_09 = "\U0001f3f4\U000e006d\U000e0074\U000e0030\U000e0039\U000e007f" - FLAG_FOR_COSPICUA_MT_06 = "\U0001f3f4\U000e006d\U000e0074\U000e0030\U000e0036\U000e007f" - FLAG_FOR_NOUAKCHOTT_NORD_MR_14 = "\U0001f3f4\U000e006d\U000e0072\U000e0031\U000e0034\U000e007f" - FLAG_FOR_G_ARG_UR_MT_15 = "\U0001f3f4\U000e006d\U000e0074\U000e0031\U000e0035\U000e007f" - COUPLE_WITH_HEART_WOMAN_DARK_SKIN_TONE_WOMAN = "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f469" - FLAG_FOR_IMTARFA_MT_35 = "\U0001f3f4\U000e006d\U000e0074\U000e0033\U000e0035\U000e007f" - FLAG_FOR_BIRZEBBUGA_MT_05 = "\U0001f3f4\U000e006d\U000e0074\U000e0030\U000e0035\U000e007f" - FLAG_FOR_BALZAN_MT_02 = "\U0001f3f4\U000e006d\U000e0074\U000e0030\U000e0032\U000e007f" - FLAG_FOR_REDANGE_LU_RD = "\U0001f3f4\U000e006c\U000e0075\U000e0072\U000e0064\U000e007f" - FLAG_FOR_VALLETTA_MT_60 = "\U0001f3f4\U000e006d\U000e0074\U000e0036\U000e0030\U000e007f" - FLAG_FOR_QRENDI_MT_44 = "\U0001f3f4\U000e006d\U000e0074\U000e0034\U000e0034\U000e007f" - FAMILY_WOMAN_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f476\U0001f3ff\u200d\U0001f476\U0001f3ff" - FLAG_FOR_FLACQ_MU_FL = "\U0001f3f4\U000e006d\U000e0075\U000e0066\U000e006c\U000e007f" - FLAG_FOR_SANTA_LUCIJA_MT_53 = "\U0001f3f4\U000e006d\U000e0074\U000e0035\U000e0033\U000e007f" - FLAG_FOR_ZEJTUN_MT_67 = "\U0001f3f4\U000e006d\U000e0074\U000e0036\U000e0037\U000e007f" - FLAG_FOR_FUNAFUTI_TV_FUN = "\U0001f3f4\U000e0074\U000e0076\U000e0066\U000e0075\U000e006e\U000e007f" - FLAG_FOR_PEMBROKE_MT_40 = "\U0001f3f4\U000e006d\U000e0074\U000e0034\U000e0030\U000e007f" - FLAG_FOR_CUREPIPE_MU_CU = "\U0001f3f4\U000e006d\U000e0075\U000e0063\U000e0075\U000e007f" - FLAG_FOR_ZURRIEQ_MT_68 = "\U0001f3f4\U000e006d\U000e0074\U000e0036\U000e0038\U000e007f" - FLAG_FOR_QORMI_MT_43 = "\U0001f3f4\U000e006d\U000e0074\U000e0034\U000e0033\U000e007f" - FLAG_FOR_SAINT_LAWRENCE_MT_50 = "\U0001f3f4\U000e006d\U000e0074\U000e0035\U000e0030\U000e007f" - FLAG_FOR_ZABBAR_MT_64 = "\U0001f3f4\U000e006d\U000e0074\U000e0036\U000e0034\U000e007f" - FLAG_FOR_PAMPLEMOUSSES_MU_PA = "\U0001f3f4\U000e006d\U000e0075\U000e0070\U000e0061\U000e007f" - FLAG_FOR_QALA_MT_42 = "\U0001f3f4\U000e006d\U000e0074\U000e0034\U000e0032\U000e007f" - FLAG_FOR_SLIEMA_MT_56 = "\U0001f3f4\U000e006d\U000e0074\U000e0035\U000e0036\U000e007f" - FLAG_FOR_TARXIEN_MT_59 = "\U0001f3f4\U000e006d\U000e0074\U000e0035\U000e0039\U000e007f" - FLAG_FOR_TA_XBIEX_MT_58 = "\U0001f3f4\U000e006d\U000e0074\U000e0035\U000e0038\U000e007f" - KISS_WOMAN_DARK_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fe" - FLAG_FOR_XAG_RA_MT_61 = "\U0001f3f4\U000e006d\U000e0074\U000e0036\U000e0031\U000e007f" - FLAG_FOR_SWIEQI_MT_57 = "\U0001f3f4\U000e006d\U000e0074\U000e0035\U000e0037\U000e007f" - FLAG_FOR_SAN_GWANN_MT_49 = "\U0001f3f4\U000e006d\U000e0074\U000e0034\U000e0039\U000e007f" - FLAG_FOR_ZEBBUG_MT_66 = "\U0001f3f4\U000e006d\U000e0074\U000e0036\U000e0036\U000e007f" - FLAG_FOR_SIGGIEWI_MT_55 = "\U0001f3f4\U000e006d\U000e0074\U000e0035\U000e0035\U000e007f" - FLAG_FOR_PORT_LOUIS_MU_PU = "\U0001f3f4\U000e006d\U000e0075\U000e0070\U000e0075\U000e007f" - FLAG_FOR_SANTA_VENERA_MT_54 = "\U0001f3f4\U000e006d\U000e0074\U000e0035\U000e0034\U000e007f" - FLAG_FOR_XG_AJRA_MT_63 = "\U0001f3f4\U000e006d\U000e0074\U000e0036\U000e0033\U000e007f" - FLAG_FOR_CENTRAL_MW_C = "\U0001f3f4\U000e006d\U000e0077\U000e0063\U000e007f" - FLAG_FOR_SOUTH_PROVINCE_MV_SU = "\U0001f3f4\U000e006d\U000e0076\U000e0073\U000e0075\U000e007f" - FLAG_FOR_TRIESENBERG_LI_10 = "\U0001f3f4\U000e006c\U000e0069\U000e0031\U000e0030\U000e007f" - FLAG_FOR_VACOAS_PHOENIX_MU_VP = "\U0001f3f4\U000e006d\U000e0075\U000e0076\U000e0070\U000e007f" - FLAG_FOR_ANENII_NOI_MD_AN = "\U0001f3f4\U000e006d\U000e0064\U000e0061\U000e006e\U000e007f" - FLAG_FOR_GUANAJUATO_MX_GUA = "\U0001f3f4\U000e006d\U000e0078\U000e0067\U000e0075\U000e0061\U000e007f" - FLAG_FOR_MALE_MV_MLE = "\U0001f3f4\U000e006d\U000e0076\U000e006d\U000e006c\U000e0065\U000e007f" - FLAG_FOR_OAXACA_MX_OAX = "\U0001f3f4\U000e006d\U000e0078\U000e006f\U000e0061\U000e0078\U000e007f" - FLAG_FOR_CENTRAL_PROVINCE_MV_CE = "\U0001f3f4\U000e006d\U000e0076\U000e0063\U000e0065\U000e007f" - FAMILY_MAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f468\U0001f3fd\u200d\U0001f466\U0001f3fd\u200d\U0001f467\U0001f3fd" - FLAG_FOR_MUNSTER_IE_M = "\U0001f3f4\U000e0069\U000e0065\U000e006d\U000e007f" - FLAG_FOR_MANGYSTAU_KZ_MAN = "\U0001f3f4\U000e006b\U000e007a\U000e006d\U000e0061\U000e006e\U000e007f" - FLAG_FOR_BAJA_CALIFORNIA_SUR_MX_BCS = "\U0001f3f4\U000e006d\U000e0078\U000e0062\U000e0063\U000e0073\U000e007f" - FLAG_FOR_CAMPECHE_MX_CAM = "\U0001f3f4\U000e006d\U000e0078\U000e0063\U000e0061\U000e006d\U000e007f" - FLAG_FOR_KYOTO_JP_26 = "\U0001f3f4\U000e006a\U000e0070\U000e0032\U000e0036\U000e007f" - FLAG_FOR_KOHGILUYEH_AND_BOYER_AHMAD_IR_18 = "\U0001f3f4\U000e0069\U000e0072\U000e0031\U000e0038\U000e007f" - FLAG_FOR_HIDALGO_MX_HID = "\U0001f3f4\U000e006d\U000e0078\U000e0068\U000e0069\U000e0064\U000e007f" - FLAG_FOR_CHLEF_DZ_02 = "\U0001f3f4\U000e0064\U000e007a\U000e0030\U000e0032\U000e007f" - FLAG_FOR_QUATRE_BORNES_MU_QB = "\U0001f3f4\U000e006d\U000e0075\U000e0071\U000e0062\U000e007f" - FLAG_FOR_PLAINES_WILHEMS_MU_PW = "\U0001f3f4\U000e006d\U000e0075\U000e0070\U000e0077\U000e007f" - FLAG_FOR_SAVANNE_MU_SA = "\U0001f3f4\U000e006d\U000e0075\U000e0073\U000e0061\U000e007f" - FLAG_FOR_SOUTHERN_MW_S = "\U0001f3f4\U000e006d\U000e0077\U000e0073\U000e007f" - FLAG_FOR_COAHUILA_MX_COA = "\U0001f3f4\U000e006d\U000e0078\U000e0063\U000e006f\U000e0061\U000e007f" - FLAG_FOR_NEGERI_SEMBILAN_MY_05 = "\U0001f3f4\U000e006d\U000e0079\U000e0030\U000e0035\U000e007f" - FLAG_FOR_SOFALA_MZ_S = "\U0001f3f4\U000e006d\U000e007a\U000e0073\U000e007f" - FLAG_FOR_LABUAN_MY_15 = "\U0001f3f4\U000e006d\U000e0079\U000e0031\U000e0035\U000e007f" - FLAG_FOR_NUEVO_LEON_MX_NLE = "\U0001f3f4\U000e006d\U000e0078\U000e006e\U000e006c\U000e0065\U000e007f" - FLAG_FOR_LA_SOURCE_MC_SO = "\U0001f3f4\U000e006d\U000e0063\U000e0073\U000e006f\U000e007f" - FLAG_FOR_SOUTH_GYEONGSANG_KR_48 = "\U0001f3f4\U000e006b\U000e0072\U000e0034\U000e0038\U000e007f" - FAMILY_MAN_LIGHT_SKIN_TONE_MAN_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f468\U0001f3fb\u200d\U0001f466\U0001f3fb\u200d\U0001f466\U0001f3fb" - FLAG_FOR_GAZA_MZ_G = "\U0001f3f4\U000e006d\U000e007a\U000e0067\U000e007f" - TAG_GRAVE_ACCENT = "\U000e0060" - FLAG_FOR_MALACCA_MY_04 = "\U0001f3f4\U000e006d\U000e0079\U000e0030\U000e0034\U000e007f" - FLAG_FOR_CABO_DELGADO_MZ_P = "\U0001f3f4\U000e006d\U000e007a\U000e0070\U000e007f" - FLAG_FOR_TLAXCALA_MX_TLA = "\U0001f3f4\U000e006d\U000e0078\U000e0074\U000e006c\U000e0061\U000e007f" - FLAG_FOR_APULIA_IT_75 = "\U0001f3f4\U000e0069\U000e0074\U000e0037\U000e0035\U000e007f" - FLAG_FOR_NAMPULA_MZ_N = "\U0001f3f4\U000e006d\U000e007a\U000e006e\U000e007f" - FLAG_FOR_OGOOUE_LOLO_GA_7 = "\U0001f3f4\U000e0067\U000e0061\U000e0037\U000e007f" - FLAG_FOR_ERONGO_NA_ER = "\U0001f3f4\U000e006e\U000e0061\U000e0065\U000e0072\U000e007f" - FLAG_FOR_TETE_MZ_T = "\U0001f3f4\U000e006d\U000e007a\U000e0074\U000e007f" - FLAG_FOR_UPPER_NORTH_PROVINCE_MV_UN = "\U0001f3f4\U000e006d\U000e0076\U000e0075\U000e006e\U000e007f" - FLAG_FOR_MANICA_MZ_B = "\U0001f3f4\U000e006d\U000e007a\U000e0062\U000e007f" - FLAG_FOR_PUTRAJAYA_MY_16 = "\U0001f3f4\U000e006d\U000e0079\U000e0031\U000e0036\U000e007f" - FLAG_FOR_MAPUTO_MZ_MPM = "\U0001f3f4\U000e006d\U000e007a\U000e006d\U000e0070\U000e006d\U000e007f" - FLAG_FOR_HARDAP_NA_HA = "\U0001f3f4\U000e006e\U000e0061\U000e0068\U000e0061\U000e007f" - FLAG_FOR_NIASSA_MZ_A = "\U0001f3f4\U000e006d\U000e007a\U000e0061\U000e007f" - FLAG_FOR_BAGO_MM_02 = "\U0001f3f4\U000e006d\U000e006d\U000e0030\U000e0032\U000e007f" - FLAG_FOR_INHAMBANE_MZ_I = "\U0001f3f4\U000e006d\U000e007a\U000e0069\U000e007f" - FLAG_FOR_ZAMBEZI_NA_CA = "\U0001f3f4\U000e006e\U000e0061\U000e0063\U000e0061\U000e007f" - FLAG_FOR_PERLIS_MY_09 = "\U0001f3f4\U000e006d\U000e0079\U000e0030\U000e0039\U000e007f" - FLAG_FOR_KAVANGO_WEST_NA_KW = "\U0001f3f4\U000e006e\U000e0061\U000e006b\U000e0077\U000e007f" - FLAG_FOR_RIVIERE_DU_REMPART_MU_RR = "\U0001f3f4\U000e006d\U000e0075\U000e0072\U000e0072\U000e007f" - FLAG_FOR_ZACATECAS_MX_ZAC = "\U0001f3f4\U000e006d\U000e0078\U000e007a\U000e0061\U000e0063\U000e007f" - FLAG_FOR_OSHANA_NA_ON = "\U0001f3f4\U000e006e\U000e0061\U000e006f\U000e006e\U000e007f" - FLAG_FOR_DOSSO_NE_3 = "\U0001f3f4\U000e006e\U000e0065\U000e0033\U000e007f" - FLAG_FOR_KHOMAS_NA_KH = "\U0001f3f4\U000e006e\U000e0061\U000e006b\U000e0068\U000e007f" - FLAG_FOR_TILLABERI_NE_6 = "\U0001f3f4\U000e006e\U000e0065\U000e0036\U000e007f" - FLAG_FOR_KANO_NG_KN = "\U0001f3f4\U000e006e\U000e0067\U000e006b\U000e006e\U000e007f" - FLAG_FOR_ANAMBRA_NG_AN = "\U0001f3f4\U000e006e\U000e0067\U000e0061\U000e006e\U000e007f" - FLAG_FOR_NIAMEY_NE_8 = "\U0001f3f4\U000e006e\U000e0065\U000e0038\U000e007f" - FLAG_FOR_KADUNA_NG_KD = "\U0001f3f4\U000e006e\U000e0067\U000e006b\U000e0064\U000e007f" - FLAG_FOR_JIGAWA_NG_JI = "\U0001f3f4\U000e006e\U000e0067\U000e006a\U000e0069\U000e007f" - FLAG_FOR_MARADI_NE_4 = "\U0001f3f4\U000e006e\U000e0065\U000e0034\U000e007f" - FLAG_FOR_OMUSATI_NA_OS = "\U0001f3f4\U000e006e\U000e0061\U000e006f\U000e0073\U000e007f" - FLAG_FOR_AGADEZ_NE_1 = "\U0001f3f4\U000e006e\U000e0065\U000e0031\U000e007f" - FLAG_FOR_KARAS_NA_KA = "\U0001f3f4\U000e006e\U000e0061\U000e006b\U000e0061\U000e007f" - FLAG_FOR_BENUE_NG_BE = "\U0001f3f4\U000e006e\U000e0067\U000e0062\U000e0065\U000e007f" - FLAG_FOR_KUNENE_NA_KU = "\U0001f3f4\U000e006e\U000e0061\U000e006b\U000e0075\U000e007f" - FLAG_FOR_OHANGWENA_NA_OW = "\U0001f3f4\U000e006e\U000e0061\U000e006f\U000e0077\U000e007f" - FLAG_FOR_EKITI_NG_EK = "\U0001f3f4\U000e006e\U000e0067\U000e0065\U000e006b\U000e007f" - FLAG_FOR_SIDI_BEL_ABBES_DZ_22 = "\U0001f3f4\U000e0064\U000e007a\U000e0032\U000e0032\U000e007f" - FLAG_FOR_EBONYI_NG_EB = "\U0001f3f4\U000e006e\U000e0067\U000e0065\U000e0062\U000e007f" - FLAG_FOR_TAHOUA_NE_5 = "\U0001f3f4\U000e006e\U000e0065\U000e0035\U000e007f" - FLAG_FOR_KATSINA_NG_KT = "\U0001f3f4\U000e006e\U000e0067\U000e006b\U000e0074\U000e007f" - FLAG_FOR_BAYELSA_NG_BY = "\U0001f3f4\U000e006e\U000e0067\U000e0062\U000e0079\U000e007f" - FLAG_FOR_ABIA_NG_AB = "\U0001f3f4\U000e006e\U000e0067\U000e0061\U000e0062\U000e007f" - FLAG_FOR_OTJOZONDJUPA_NA_OD = "\U0001f3f4\U000e006e\U000e0061\U000e006f\U000e0064\U000e007f" - FLAG_FOR_ZINDER_NE_7 = "\U0001f3f4\U000e006e\U000e0065\U000e0037\U000e007f" - MAN_WITH_HEADSCARF_LIGHT_SKIN_TONE = "\U0001f9d5\U0001f3fb\u200d\u2642\ufe0f" - FLAG_FOR_DIFFA_NE_2 = "\U0001f3f4\U000e006e\U000e0065\U000e0032\U000e007f" - FLAG_FOR_AKWA_IBOM_NG_AK = "\U0001f3f4\U000e006e\U000e0067\U000e0061\U000e006b\U000e007f" - FLAG_FOR_BOACO_NI_BO = "\U0001f3f4\U000e006e\U000e0069\U000e0062\U000e006f\U000e007f" - FLAG_FOR_OYO_NG_OY = "\U0001f3f4\U000e006e\U000e0067\U000e006f\U000e0079\U000e007f" - FLAG_FOR_CHONTALES_NI_CO = "\U0001f3f4\U000e006e\U000e0069\U000e0063\U000e006f\U000e007f" - FLAG_FOR_NUEVA_SEGOVIA_NI_NS = "\U0001f3f4\U000e006e\U000e0069\U000e006e\U000e0073\U000e007f" - FLAG_FOR_ROTUMA_FJ_R = "\U0001f3f4\U000e0066\U000e006a\U000e0072\U000e007f" - FLAG_FOR_ESTELI_NI_ES = "\U0001f3f4\U000e006e\U000e0069\U000e0065\U000e0073\U000e007f" - FLAG_FOR_MAYOTTE_FR_MAY = "\U0001f3f4\U000e0066\U000e0072\U000e006d\U000e0061\U000e0079\U000e007f" - FLAG_FOR_TARABA_NG_TA = "\U0001f3f4\U000e006e\U000e0067\U000e0074\U000e0061\U000e007f" - FLAG_FOR_CARAZO_NI_CA = "\U0001f3f4\U000e006e\U000e0069\U000e0063\U000e0061\U000e007f" - FLAG_FOR_JINOTEGA_NI_JI = "\U0001f3f4\U000e006e\U000e0069\U000e006a\U000e0069\U000e007f" - FLAG_FOR_GELDERLAND_NL_GE = "\U0001f3f4\U000e006e\U000e006c\U000e0067\U000e0065\U000e007f" - FLAG_FOR_NORTHERN_DISTRICT_IL_Z = "\U0001f3f4\U000e0069\U000e006c\U000e007a\U000e007f" - FLAG_FOR_NASARAWA_NG_NA = "\U0001f3f4\U000e006e\U000e0067\U000e006e\U000e0061\U000e007f" - FLAG_FOR_MANAGUA_NI_MN = "\U0001f3f4\U000e006e\U000e0069\U000e006d\U000e006e\U000e007f" - FLAG_FOR_SANCTI_SPIRITUS_CU_07 = "\U0001f3f4\U000e0063\U000e0075\U000e0030\U000e0037\U000e007f" - FLAG_FOR_ZAMFARA_NG_ZA = "\U0001f3f4\U000e006e\U000e0067\U000e007a\U000e0061\U000e007f" - FLAG_FOR_CHINANDEGA_NI_CI = "\U0001f3f4\U000e006e\U000e0069\U000e0063\U000e0069\U000e007f" - FLAG_FOR_MATAGALPA_NI_MT = "\U0001f3f4\U000e006e\U000e0069\U000e006d\U000e0074\U000e007f" - FLAG_FOR_MADRIZ_NI_MD = "\U0001f3f4\U000e006e\U000e0069\U000e006d\U000e0064\U000e007f" - FLAG_FOR_RIVAS_NI_RI = "\U0001f3f4\U000e006e\U000e0069\U000e0072\U000e0069\U000e007f" - FLAG_FOR_SAINT_JAMES_JM_08 = "\U0001f3f4\U000e006a\U000e006d\U000e0030\U000e0038\U000e007f" - FLAG_FOR_EL_PROGRESO_GT_PR = "\U0001f3f4\U000e0067\U000e0074\U000e0070\U000e0072\U000e007f" - FLAG_FOR_ATLANTICO_NORTE_NI_AN = "\U0001f3f4\U000e006e\U000e0069\U000e0061\U000e006e\U000e007f" - FLAG_FOR_PLATEAU_NG_PL = "\U0001f3f4\U000e006e\U000e0067\U000e0070\U000e006c\U000e007f" - FLAG_FOR_KABARDINO_BALKAR_RU_KB = "\U0001f3f4\U000e0072\U000e0075\U000e006b\U000e0062\U000e007f" - FLAG_FOR_FLEVOLAND_NL_FL = "\U0001f3f4\U000e006e\U000e006c\U000e0066\U000e006c\U000e007f" - FLAG_FOR_KWARA_NG_KW = "\U0001f3f4\U000e006e\U000e0067\U000e006b\U000e0077\U000e007f" - FLAG_FOR_GRANADA_NI_GR = "\U0001f3f4\U000e006e\U000e0069\U000e0067\U000e0072\U000e007f" - FLAG_FOR_SANTIAGO_DE_CUBA_CU_13 = "\U0001f3f4\U000e0063\U000e0075\U000e0031\U000e0033\U000e007f" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f469\U0001f3fc\u200d\U0001f467\U0001f3fc" - FLAG_FOR_VESTFOLD_NO_07 = "\U0001f3f4\U000e006e\U000e006f\U000e0030\U000e0037\U000e007f" - FLAG_FOR_PURWANCHAL_NP_4 = "\U0001f3f4\U000e006e\U000e0070\U000e0034\U000e007f" - FLAG_FOR_STFOLD_NO_01 = "\U0001f3f4\U000e006e\U000e006f\U000e0030\U000e0031\U000e007f" - FLAG_FOR_S_R_TR_NDELAG_NO_16 = "\U0001f3f4\U000e006e\U000e006f\U000e0031\U000e0036\U000e007f" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f468\U0001f3fc\u200d\U0001f467\U0001f3fc" - FLAG_FOR_JAN_MAYEN_NO_22 = "\U0001f3f4\U000e006e\U000e006f\U000e0032\U000e0032\U000e007f" - FLAG_FOR_LARVOTTO_MC_LA = "\U0001f3f4\U000e006d\U000e0063\U000e006c\U000e0061\U000e007f" - FLAG_FOR_CENTRAL_NP_1 = "\U0001f3f4\U000e006e\U000e0070\U000e0031\U000e007f" - FLAG_FOR_TELEMARK_NO_08 = "\U0001f3f4\U000e006e\U000e006f\U000e0030\U000e0038\U000e007f" - FLAG_FOR_MADHYA_PASHCHIMANCHAL_NP_2 = "\U0001f3f4\U000e006e\U000e0070\U000e0032\U000e007f" - COUPLE_WITH_HEART_MAN_MEDIUM_LIGHT_SKIN_TONE_MAN_DARK_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3ff" - FLAG_FOR_AUST_AGDER_NO_09 = "\U0001f3f4\U000e006e\U000e006f\U000e0030\U000e0039\U000e007f" - FLAG_FOR_BALZERS_LI_01 = "\U0001f3f4\U000e006c\U000e0069\U000e0030\U000e0031\U000e007f" - FLAG_FOR_M_RE_OG_ROMSDAL_NO_15 = "\U0001f3f4\U000e006e\U000e006f\U000e0031\U000e0035\U000e007f" - FLAG_FOR_NORD_TR_NDELAG_NO_17 = "\U0001f3f4\U000e006e\U000e006f\U000e0031\U000e0037\U000e007f" - FLAG_FOR_AKERSHUS_NO_02 = "\U0001f3f4\U000e006e\U000e006f\U000e0030\U000e0032\U000e007f" - FLAG_FOR_BUSKERUD_NO_06 = "\U0001f3f4\U000e006e\U000e006f\U000e0030\U000e0036\U000e007f" - FLAG_FOR_BOE_NR_06 = "\U0001f3f4\U000e006e\U000e0072\U000e0030\U000e0036\U000e007f" - FLAG_FOR_NORDLAND_NO_18 = "\U0001f3f4\U000e006e\U000e006f\U000e0031\U000e0038\U000e007f" - FLAG_FOR_PRILEP_MK_62 = "\U0001f3f4\U000e006d\U000e006b\U000e0036\U000e0032\U000e007f" - FLAG_FOR_BAITI_NR_05 = "\U0001f3f4\U000e006e\U000e0072\U000e0030\U000e0035\U000e007f" - FLAG_FOR_ANETAN_NR_03 = "\U0001f3f4\U000e006e\U000e0072\U000e0030\U000e0033\U000e007f" - FLAG_FOR_BUADA_NR_07 = "\U0001f3f4\U000e006e\U000e0072\U000e0030\U000e0037\U000e007f" - FLAG_FOR_NZEREKORE_REGION_GN_N = "\U0001f3f4\U000e0067\U000e006e\U000e006e\U000e007f" - FLAG_FOR_ONDO_NG_ON = "\U0001f3f4\U000e006e\U000e0067\U000e006f\U000e006e\U000e007f" - FLAG_FOR_OPPLAND_NO_05 = "\U0001f3f4\U000e006e\U000e006f\U000e0030\U000e0035\U000e007f" - FLAG_FOR_ANIBARE_NR_04 = "\U0001f3f4\U000e006e\U000e0072\U000e0030\U000e0034\U000e007f" - FLAG_FOR_MUSCAT_OM_MA = "\U0001f3f4\U000e006f\U000e006d\U000e006d\U000e0061\U000e007f" - FLAG_FOR_CHATHAM_ISLANDS_NZ_CIT = "\U0001f3f4\U000e006e\U000e007a\U000e0063\U000e0069\U000e0074\U000e007f" - FLAG_FOR_UNGHENI_MD_UN = "\U0001f3f4\U000e006d\U000e0064\U000e0075\U000e006e\U000e007f" - FLAG_FOR_KANKAN_REGION_GN_K = "\U0001f3f4\U000e0067\U000e006e\U000e006b\U000e007f" - KISS_WOMAN_DARK_SKIN_TONE_MAN_LIGHT_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fb" - FLAG_FOR_DAYKUNDI_AF_DAY = "\U0001f3f4\U000e0061\U000e0066\U000e0064\U000e0061\U000e0079\U000e007f" - FLAG_FOR_MUSANDAM_OM_MU = "\U0001f3f4\U000e006f\U000e006d\U000e006d\U000e0075\U000e007f" - FLAG_FOR_KARAK_JO_KA = "\U0001f3f4\U000e006a\U000e006f\U000e006b\U000e0061\U000e007f" - FLAG_FOR_JANUB_ASH_SHARQIYAH_OM_SJ = "\U0001f3f4\U000e006f\U000e006d\U000e0073\U000e006a\U000e007f" - FLAG_FOR_WAIKATO_NZ_WKO = "\U0001f3f4\U000e006e\U000e007a\U000e0077\U000e006b\U000e006f\U000e007f" - FLAG_FOR_TELSIAI_COUNTY_LT_TE = "\U0001f3f4\U000e006c\U000e0074\U000e0074\U000e0065\U000e007f" - FLAG_FOR_SHAMAL_ASH_SHARQIYAH_OM_SS = "\U0001f3f4\U000e006f\U000e006d\U000e0073\U000e0073\U000e007f" - TAG_LATIN_CAPITAL_LETTER_U = "\U000e0055" - FLAG_FOR_IJUW_NR_10 = "\U0001f3f4\U000e006e\U000e0072\U000e0031\U000e0030\U000e007f" - FLAG_FOR_COLON_PA_3 = "\U0001f3f4\U000e0070\U000e0061\U000e0033\U000e007f" - FLAG_FOR_SHAMAL_AL_BATINAH_OM_BS = "\U0001f3f4\U000e006f\U000e006d\U000e0062\U000e0073\U000e007f" - FLAG_FOR_DHOFAR_OM_ZU = "\U0001f3f4\U000e006f\U000e006d\U000e007a\U000e0075\U000e007f" - FLAG_FOR_AD_DHAHIRAH_OM_ZA = "\U0001f3f4\U000e006f\U000e006d\U000e007a\U000e0061\U000e007f" - FLAG_FOR_GISBORNE_NZ_GIS = "\U0001f3f4\U000e006e\U000e007a\U000e0067\U000e0069\U000e0073\U000e007f" - FLAG_FOR_ALOJA_LV_005 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0030\U000e0035\U000e007f" - FLAG_FOR_TROMS_NO_19 = "\U0001f3f4\U000e006e\U000e006f\U000e0031\U000e0039\U000e007f" - KISS_WOMAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fd" - FLAG_FOR_NIBOK_NR_12 = "\U0001f3f4\U000e006e\U000e0072\U000e0031\U000e0032\U000e007f" - FLAG_FOR_JANUB_AL_BATINAH_OM_BJ = "\U0001f3f4\U000e006f\U000e006d\U000e0062\U000e006a\U000e007f" - FLAG_FOR_TUMBES_PE_TUM = "\U0001f3f4\U000e0070\U000e0065\U000e0074\U000e0075\U000e006d\U000e007f" - FLAG_FOR_EL_CALLAO_PE_CAL = "\U0001f3f4\U000e0070\U000e0065\U000e0063\U000e0061\U000e006c\U000e007f" - FLAG_FOR_PUNO_PE_PUN = "\U0001f3f4\U000e0070\U000e0065\U000e0070\U000e0075\U000e006e\U000e007f" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f469\U0001f3fd\u200d\U0001f467\U0001f3fd\u200d\U0001f476\U0001f3fd" - FLAG_FOR_PASCO_PE_PAS = "\U0001f3f4\U000e0070\U000e0065\U000e0070\U000e0061\U000e0073\U000e007f" - FLAG_FOR_LIMA_PE_LMA = "\U0001f3f4\U000e0070\U000e0065\U000e006c\U000e006d\U000e0061\U000e007f" - FLAG_FOR_MECKLENBURG_VORPOMMERN_DE_MV = "\U0001f3f4\U000e0064\U000e0065\U000e006d\U000e0076\U000e007f" - FLAG_FOR_PIURA_PE_PIU = "\U0001f3f4\U000e0070\U000e0065\U000e0070\U000e0069\U000e0075\U000e007f" - FLAG_FOR_ICA_PE_ICA = "\U0001f3f4\U000e0070\U000e0065\U000e0069\U000e0063\U000e0061\U000e007f" - FLAG_FOR_GITEGA_BI_GI = "\U0001f3f4\U000e0062\U000e0069\U000e0067\U000e0069\U000e007f" - FLAG_FOR_UCAYALI_PE_UCA = "\U0001f3f4\U000e0070\U000e0065\U000e0075\U000e0063\U000e0061\U000e007f" - FLAG_FOR_DENIGOMODU_NR_08 = "\U0001f3f4\U000e006e\U000e0072\U000e0030\U000e0038\U000e007f" - FLAG_FOR_HUANUCO_PE_HUC = "\U0001f3f4\U000e0070\U000e0065\U000e0068\U000e0075\U000e0063\U000e007f" - FLAG_FOR_JUNIN_PE_JUN = "\U0001f3f4\U000e0070\U000e0065\U000e006a\U000e0075\U000e006e\U000e007f" - FLAG_FOR_LA_LIBERTAD_PE_LAL = "\U0001f3f4\U000e0070\U000e0065\U000e006c\U000e0061\U000e006c\U000e007f" - FLAG_FOR_CHIMBU_PG_CPK = "\U0001f3f4\U000e0070\U000e0067\U000e0063\U000e0070\U000e006b\U000e007f" - FLAG_FOR_CAJAMARCA_PE_CAJ = "\U0001f3f4\U000e0070\U000e0065\U000e0063\U000e0061\U000e006a\U000e007f" - FLAG_FOR_BALKAN_TM_B = "\U0001f3f4\U000e0074\U000e006d\U000e0062\U000e007f" - FLAG_FOR_VERAGUAS_PA_9 = "\U0001f3f4\U000e0070\U000e0061\U000e0039\U000e007f" - FLAG_FOR_LAMBAYEQUE_PE_LAM = "\U0001f3f4\U000e0070\U000e0065\U000e006c\U000e0061\U000e006d\U000e007f" - FLAG_FOR_AMAZONAS_PE_AMA = "\U0001f3f4\U000e0070\U000e0065\U000e0061\U000e006d\U000e0061\U000e007f" - FLAG_FOR_NORTH_JEOLLA_KR_45 = "\U0001f3f4\U000e006b\U000e0072\U000e0034\U000e0035\U000e007f" - FLAG_FOR_HUANCAVELICA_PE_HUV = "\U0001f3f4\U000e0070\U000e0065\U000e0068\U000e0075\U000e0076\U000e007f" - FLAG_FOR_DARIEN_PA_5 = "\U0001f3f4\U000e0070\U000e0061\U000e0035\U000e007f" - FLAG_FOR_ROGALAND_NO_11 = "\U0001f3f4\U000e006e\U000e006f\U000e0031\U000e0031\U000e007f" - FLAG_FOR_LORETO_PE_LOR = "\U0001f3f4\U000e0070\U000e0065\U000e006c\U000e006f\U000e0072\U000e007f" - FLAG_FOR_SAN_MARTIN_PE_SAM = "\U0001f3f4\U000e0070\U000e0065\U000e0073\U000e0061\U000e006d\U000e007f" - FLAG_FOR_CHONGQING_CN_50 = "\U0001f3f4\U000e0063\U000e006e\U000e0035\U000e0030\U000e007f" - FLAG_FOR_SANDAUN_PG_SAN = "\U0001f3f4\U000e0070\U000e0067\U000e0073\U000e0061\U000e006e\U000e007f" - FLAG_FOR_CIUDAD_DE_MEXICO_MX_CMX = "\U0001f3f4\U000e006d\U000e0078\U000e0063\U000e006d\U000e0078\U000e007f" - FLAG_FOR_OSUN_NG_OS = "\U0001f3f4\U000e006e\U000e0067\U000e006f\U000e0073\U000e007f" - MAHJONG_TILE_TWO_OF_CIRCLES = "\U0001f01a" - FLAG_FOR_HEDMARK_NO_04 = "\U0001f3f4\U000e006e\U000e006f\U000e0030\U000e0034\U000e007f" - COUPLE_WITH_HEART_MAN_MAN_MEDIUM_SKIN_TONE = "\U0001f468\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fd" - FLAG_FOR_NORTHERN_MINDANAO_PH_10 = "\U0001f3f4\U000e0070\U000e0068\U000e0031\U000e0030\U000e007f" - FLAG_FOR_MADANG_PG_MPM = "\U0001f3f4\U000e0070\U000e0067\U000e006d\U000e0070\U000e006d\U000e007f" - FLAG_FOR_WESTERN_PG_WPD = "\U0001f3f4\U000e0070\U000e0067\U000e0077\U000e0070\U000e0064\U000e007f" - FLAG_FOR_SOUTH_PYONGAN_KP_02 = "\U0001f3f4\U000e006b\U000e0070\U000e0030\U000e0032\U000e007f" - FLAG_FOR_EASTERN_VISAYAS_PH_08 = "\U0001f3f4\U000e0070\U000e0068\U000e0030\U000e0038\U000e007f" - FLAG_FOR_WESTERN_FJ_W = "\U0001f3f4\U000e0066\U000e006a\U000e0077\U000e007f" - FLAG_FOR_SOCCSKSARGEN_PH_12 = "\U0001f3f4\U000e0070\U000e0068\U000e0031\U000e0032\U000e007f" - FLAG_FOR_MOROBE_PG_MPL = "\U0001f3f4\U000e0070\U000e0067\U000e006d\U000e0070\U000e006c\U000e007f" - FLAG_FOR_MIMAROPA_PH_41 = "\U0001f3f4\U000e0070\U000e0068\U000e0034\U000e0031\U000e007f" - COUPLE_WITH_HEART_WOMAN_LIGHT_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fe" - FLAG_FOR_HELA_PG_HLA = "\U0001f3f4\U000e0070\U000e0067\U000e0068\U000e006c\U000e0061\U000e007f" - FLAG_FOR_CALABARZON_PH_40 = "\U0001f3f4\U000e0070\U000e0068\U000e0034\U000e0030\U000e007f" - FLAG_FOR_ZAMBOANGA_PENINSULA_PH_09 = "\U0001f3f4\U000e0070\U000e0068\U000e0030\U000e0039\U000e007f" - FLAG_FOR_HERRERA_PA_6 = "\U0001f3f4\U000e0070\U000e0061\U000e0036\U000e007f" - COUPLE_WITH_HEART_MAN_LIGHT_SKIN_TONE_MAN_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fb" - FLAG_FOR_EDO_NG_ED = "\U0001f3f4\U000e006e\U000e0067\U000e0065\U000e0064\U000e007f" - FLAG_FOR_MILNE_BAY_PG_MBA = "\U0001f3f4\U000e0070\U000e0067\U000e006d\U000e0062\U000e0061\U000e007f" - FAMILY_WOMAN_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f467\U0001f3fb" - FLAG_FOR_WESTERN_VISAYAS_PH_06 = "\U0001f3f4\U000e0070\U000e0068\U000e0030\U000e0036\U000e007f" - FLAG_FOR_ANABAR_NR_02 = "\U0001f3f4\U000e006e\U000e0072\U000e0030\U000e0032\U000e007f" - FLAG_FOR_CARAGA_PH_13 = "\U0001f3f4\U000e0070\U000e0068\U000e0031\U000e0033\U000e007f" - FLAG_FOR_KOGI_NG_KO = "\U0001f3f4\U000e006e\U000e0067\U000e006b\U000e006f\U000e007f" - FLAG_FOR_GUANACASTE_CR_G = "\U0001f3f4\U000e0063\U000e0072\U000e0067\U000e007f" - FLAG_FOR_COROZAL_BZ_CZL = "\U0001f3f4\U000e0062\U000e007a\U000e0063\U000e007a\U000e006c\U000e007f" - FLAG_FOR_ALSUNGA_LV_006 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0030\U000e0036\U000e007f" - FLAG_FOR_TULKARM_PS_TKM = "\U0001f3f4\U000e0070\U000e0073\U000e0074\U000e006b\U000e006d\U000e007f" - FLAG_FOR_OPOLE_PL_OP = "\U0001f3f4\U000e0070\U000e006c\U000e006f\U000e0070\U000e007f" - FLAG_FOR_KHYBER_PAKHTUNKHWA_PK_KP = "\U0001f3f4\U000e0070\U000e006b\U000e006b\U000e0070\U000e007f" - FLAG_FOR_WEST_POMERANIA_PL_ZP = "\U0001f3f4\U000e0070\U000e006c\U000e007a\U000e0070\U000e007f" - FLAG_FOR_KURDAMIR_AZ_KUR = "\U0001f3f4\U000e0061\U000e007a\U000e006b\U000e0075\U000e0072\U000e007f" - FLAG_FOR_LUBUSZ_PL_LB = "\U0001f3f4\U000e0070\U000e006c\U000e006c\U000e0062\U000e007f" - FLAG_FOR_HEBRON_PS_HBN = "\U0001f3f4\U000e0070\U000e0073\U000e0068\U000e0062\U000e006e\U000e007f" - FLAG_FOR_RAFAH_PS_RFH = "\U0001f3f4\U000e0070\U000e0073\U000e0072\U000e0066\U000e0068\U000e007f" - FLAG_FOR_WARMIAN_MASURIA_PL_WN = "\U0001f3f4\U000e0070\U000e006c\U000e0077\U000e006e\U000e007f" - FLAG_FOR_PUNJAB_PK_PB = "\U0001f3f4\U000e0070\U000e006b\U000e0070\U000e0062\U000e007f" - FLAG_FOR_AVEIRO_PT_01 = "\U0001f3f4\U000e0070\U000e0074\U000e0030\U000e0031\U000e007f" - FLAG_FOR_MAZOVIA_PL_MZ = "\U0001f3f4\U000e0070\U000e006c\U000e006d\U000e007a\U000e007f" - FLAG_FOR_LUBLIN_PL_LU = "\U0001f3f4\U000e0070\U000e006c\U000e006c\U000e0075\U000e007f" - FLAG_FOR_NABLUS_PS_NBS = "\U0001f3f4\U000e0070\U000e0073\U000e006e\U000e0062\U000e0073\U000e007f" - FLAG_FOR_QALQILYA_PS_QQA = "\U0001f3f4\U000e0070\U000e0073\U000e0071\U000e0071\U000e0061\U000e007f" - FLAG_FOR_KUYAVIAN_POMERANIA_PL_KP = "\U0001f3f4\U000e0070\U000e006c\U000e006b\U000e0070\U000e007f" - FLAG_FOR_SINDH_PK_SD = "\U0001f3f4\U000e0070\U000e006b\U000e0073\U000e0064\U000e007f" - FLAG_FOR_PODLASKIE_PL_PD = "\U0001f3f4\U000e0070\U000e006c\U000e0070\U000e0064\U000e007f" - FLAG_FOR_DAVAO_PH_11 = "\U0001f3f4\U000e0070\U000e0068\U000e0031\U000e0031\U000e007f" - FLAG_FOR_SUBCARPATHIA_PL_PK = "\U0001f3f4\U000e0070\U000e006c\U000e0070\U000e006b\U000e007f" - FLAG_FOR_ODZ_PL_LD = "\U0001f3f4\U000e0070\U000e006c\U000e006c\U000e0064\U000e007f" - FLAG_FOR_SALFIT_PS_SLT = "\U0001f3f4\U000e0070\U000e0073\U000e0073\U000e006c\U000e0074\U000e007f" - FLAG_FOR_JENIN_PS_JEN = "\U0001f3f4\U000e0070\U000e0073\U000e006a\U000e0065\U000e006e\U000e007f" - FLAG_FOR_SILESIA_PL_SL = "\U0001f3f4\U000e0070\U000e006c\U000e0073\U000e006c\U000e007f" - FLAG_FOR_KHAN_YUNIS_PS_KYS = "\U0001f3f4\U000e0070\U000e0073\U000e006b\U000e0079\U000e0073\U000e007f" - TAG_RIGHT_SQUARE_BRACKET = "\U000e005d" - FLAG_FOR_MOSCOW_PROVINCE_RU_MOS = "\U0001f3f4\U000e0072\U000e0075\U000e006d\U000e006f\U000e0073\U000e007f" - FLAG_FOR_TUSCANY_IT_52 = "\U0001f3f4\U000e0069\U000e0074\U000e0035\U000e0032\U000e007f" - FLAG_FOR_BICOL_PH_05 = "\U0001f3f4\U000e0070\U000e0068\U000e0030\U000e0035\U000e007f" - TAG_LATIN_CAPITAL_LETTER_K = "\U000e004b" - FLAG_FOR_LOWER_SILESIAN_PL_DS = "\U0001f3f4\U000e0070\U000e006c\U000e0064\U000e0073\U000e007f" - FLAG_FOR_CAGAYAN_VALLEY_PH_02 = "\U0001f3f4\U000e0070\U000e0068\U000e0030\U000e0032\U000e007f" - FLAG_FOR_RIO_DE_JANEIRO_BR_RJ = "\U0001f3f4\U000e0062\U000e0072\U000e0072\U000e006a\U000e007f" - FLAG_FOR_EVORA_PT_07 = "\U0001f3f4\U000e0070\U000e0074\U000e0030\U000e0037\U000e007f" - FLAG_FOR_NGARAARD_PW_214 = "\U0001f3f4\U000e0070\U000e0077\U000e0032\U000e0031\U000e0034\U000e007f" - FLAG_FOR_BRAGANCA_PT_04 = "\U0001f3f4\U000e0070\U000e0074\U000e0030\U000e0034\U000e007f" - FLAG_FOR_SHIDA_KARTLI_GE_SK = "\U0001f3f4\U000e0067\U000e0065\U000e0073\U000e006b\U000e007f" - FLAG_FOR_MENENG_NR_11 = "\U0001f3f4\U000e006e\U000e0072\U000e0031\U000e0031\U000e007f" - FLAG_FOR_AIMELIIK_PW_002 = "\U0001f3f4\U000e0070\U000e0077\U000e0030\U000e0030\U000e0032\U000e007f" - FLAG_FOR_AMAMBAY_PY_13 = "\U0001f3f4\U000e0070\U000e0079\U000e0031\U000e0033\U000e007f" - FLAG_FOR_HATOHOBEI_PW_050 = "\U0001f3f4\U000e0070\U000e0077\U000e0030\U000e0035\U000e0030\U000e007f" - FLAG_FOR_CENTRAL_PY_11 = "\U0001f3f4\U000e0070\U000e0079\U000e0031\U000e0031\U000e007f" - FLAG_FOR_COIMBRA_PT_06 = "\U0001f3f4\U000e0070\U000e0074\U000e0030\U000e0036\U000e007f" - FLAG_FOR_CENTRAL_PG_CPM = "\U0001f3f4\U000e0070\U000e0067\U000e0063\U000e0070\U000e006d\U000e007f" - FLAG_FOR_KOROR_PW_150 = "\U0001f3f4\U000e0070\U000e0077\U000e0031\U000e0035\U000e0030\U000e007f" - FLAG_FOR_COCLE_PA_2 = "\U0001f3f4\U000e0070\U000e0061\U000e0032\U000e007f" - FLAG_FOR_NEEMBUCU_PY_12 = "\U0001f3f4\U000e0070\U000e0079\U000e0031\U000e0032\U000e007f" - FLAG_FOR_AIRAI_PW_004 = "\U0001f3f4\U000e0070\U000e0077\U000e0030\U000e0030\U000e0034\U000e007f" - FLAG_FOR_SANTAREM_PT_14 = "\U0001f3f4\U000e0070\U000e0074\U000e0031\U000e0034\U000e007f" - FLAG_FOR_NGATPANG_PW_224 = "\U0001f3f4\U000e0070\U000e0077\U000e0032\U000e0032\U000e0034\U000e007f" - FLAG_FOR_PORTALEGRE_PT_12 = "\U0001f3f4\U000e0070\U000e0074\U000e0031\U000e0032\U000e007f" - FLAG_FOR_LEIRIA_PT_10 = "\U0001f3f4\U000e0070\U000e0074\U000e0031\U000e0030\U000e007f" - FLAG_FOR_NGARDMAU_PW_222 = "\U0001f3f4\U000e0070\U000e0077\U000e0032\U000e0032\U000e0032\U000e007f" - FLAG_FOR_NGCHESAR_PW_226 = "\U0001f3f4\U000e0070\U000e0077\U000e0032\U000e0032\U000e0036\U000e007f" - FLAG_FOR_NORTH_HWANGHAE_KP_06 = "\U0001f3f4\U000e006b\U000e0070\U000e0030\U000e0036\U000e007f" - COUPLE_WITH_HEART_WOMAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fd" - FLAG_FOR_WESTERN_NP_3 = "\U0001f3f4\U000e006e\U000e0070\U000e0033\U000e007f" - FLAG_FOR_VIANA_DO_CASTELO_PT_16 = "\U0001f3f4\U000e0070\U000e0074\U000e0031\U000e0036\U000e007f" - FLAG_FOR_CANINDEYU_PY_14 = "\U0001f3f4\U000e0070\U000e0079\U000e0031\U000e0034\U000e007f" - FLAG_FOR_CORDILLERA_PY_3 = "\U0001f3f4\U000e0070\U000e0079\U000e0033\U000e007f" - FLAG_FOR_CAAZAPA_PY_6 = "\U0001f3f4\U000e0070\U000e0079\U000e0036\U000e007f" - FLAG_FOR_COVASNA_RO_CV = "\U0001f3f4\U000e0072\U000e006f\U000e0063\U000e0076\U000e007f" - FLAG_FOR_GIURGIU_RO_GR = "\U0001f3f4\U000e0072\U000e006f\U000e0067\U000e0072\U000e007f" - FLAG_FOR_DAMBOVITA_RO_DB = "\U0001f3f4\U000e0072\U000e006f\U000e0064\U000e0062\U000e007f" - FLAG_FOR_ITAPUA_PY_7 = "\U0001f3f4\U000e0070\U000e0079\U000e0037\U000e007f" - FLAG_FOR_CAAGUAZU_PY_5 = "\U0001f3f4\U000e0070\U000e0079\U000e0035\U000e007f" - FLAG_FOR_SAN_PEDRO_PY_2 = "\U0001f3f4\U000e0070\U000e0079\U000e0032\U000e007f" - FLAG_FOR_NGEREMLENGUI_PW_227 = "\U0001f3f4\U000e0070\U000e0077\U000e0032\U000e0032\U000e0037\U000e007f" - FLAG_FOR_BIHOR_RO_BH = "\U0001f3f4\U000e0072\U000e006f\U000e0062\U000e0068\U000e007f" - FLAG_FOR_DOLJ_RO_DJ = "\U0001f3f4\U000e0072\U000e006f\U000e0064\U000e006a\U000e007f" - FLAG_FOR_ARGES_RO_AG = "\U0001f3f4\U000e0072\U000e006f\U000e0061\U000e0067\U000e007f" - FLAG_FOR_AL_KHOR_QA_KH = "\U0001f3f4\U000e0071\U000e0061\U000e006b\U000e0068\U000e007f" - FLAG_FOR_GILGIT_BALTISTAN_PK_GB = "\U0001f3f4\U000e0070\U000e006b\U000e0067\U000e0062\U000e007f" - FLAG_FOR_GUAIRA_PY_4 = "\U0001f3f4\U000e0070\U000e0079\U000e0034\U000e007f" - FLAG_FOR_PARAGUARI_PY_9 = "\U0001f3f4\U000e0070\U000e0079\U000e0039\U000e007f" - FLAG_FOR_CAPITAL_IS_1 = "\U0001f3f4\U000e0069\U000e0073\U000e0031\U000e007f" - FLAG_FOR_GALATI_RO_GL = "\U0001f3f4\U000e0072\U000e006f\U000e0067\U000e006c\U000e007f" - FLAG_FOR_ARAD_RO_AR = "\U0001f3f4\U000e0072\U000e006f\U000e0061\U000e0072\U000e007f" - FLAG_FOR_BRAILA_RO_BR = "\U0001f3f4\U000e0072\U000e006f\U000e0062\U000e0072\U000e007f" - FLAG_FOR_TIVAT_ME_19 = "\U0001f3f4\U000e006d\U000e0065\U000e0031\U000e0039\U000e007f" - FLAG_FOR_HARGHITA_RO_HR = "\U0001f3f4\U000e0072\U000e006f\U000e0068\U000e0072\U000e007f" - FLAG_FOR_MISIONES_PY_8 = "\U0001f3f4\U000e0070\U000e0079\U000e0038\U000e007f" - FLAG_FOR_AL_DAAYEN_QA_ZA = "\U0001f3f4\U000e0071\U000e0061\U000e007a\U000e0061\U000e007f" - FLAG_FOR_DIKHIL_DJ_DI = "\U0001f3f4\U000e0064\U000e006a\U000e0064\U000e0069\U000e007f" - FLAG_FOR_OLT_RO_OT = "\U0001f3f4\U000e0072\U000e006f\U000e006f\U000e0074\U000e007f" - FLAG_FOR_BRANICEVO_RS_11 = "\U0001f3f4\U000e0072\U000e0073\U000e0031\U000e0031\U000e007f" - FLAG_FOR_RASINA_RS_19 = "\U0001f3f4\U000e0072\U000e0073\U000e0031\U000e0039\U000e007f" - FLAG_FOR_SUCEAVA_RO_SV = "\U0001f3f4\U000e0072\U000e006f\U000e0073\U000e0076\U000e007f" - FLAG_FOR_ZAJECAR_RS_15 = "\U0001f3f4\U000e0072\U000e0073\U000e0031\U000e0035\U000e007f" - WHITE_HEART_SUIT = "\u2661" - FLAG_FOR_JABLANICA_RS_23 = "\U0001f3f4\U000e0072\U000e0073\U000e0032\U000e0033\U000e007f" - FLAG_FOR_SUMADIJA_RS_12 = "\U0001f3f4\U000e0072\U000e0073\U000e0031\U000e0032\U000e007f" - FLAG_FOR_PIROT_RS_22 = "\U0001f3f4\U000e0072\U000e0073\U000e0032\U000e0032\U000e007f" - FLAG_FOR_MURES_RO_MS = "\U0001f3f4\U000e0072\U000e006f\U000e006d\U000e0073\U000e007f" - FLAG_FOR_TELEORMAN_RO_TR = "\U0001f3f4\U000e0072\U000e006f\U000e0074\U000e0072\U000e007f" - FLAG_FOR_MACVA_RS_08 = "\U0001f3f4\U000e0072\U000e0073\U000e0030\U000e0038\U000e007f" - FLAG_FOR_MORAVICA_RS_17 = "\U0001f3f4\U000e0072\U000e0073\U000e0031\U000e0037\U000e007f" - FLAG_FOR_ILFOV_RO_IF = "\U0001f3f4\U000e0072\U000e006f\U000e0069\U000e0066\U000e007f" - FLAG_FOR_PRAHOVA_RO_PH = "\U0001f3f4\U000e0072\U000e006f\U000e0070\U000e0068\U000e007f" - FLAG_FOR_LOS_LAGOS_CL_LL = "\U0001f3f4\U000e0063\U000e006c\U000e006c\U000e006c\U000e007f" - FLAG_FOR_TULCEA_RO_TL = "\U0001f3f4\U000e0072\U000e006f\U000e0074\U000e006c\U000e007f" - FLAG_FOR_IALOMITA_RO_IL = "\U0001f3f4\U000e0072\U000e006f\U000e0069\U000e006c\U000e007f" - FLAG_FOR_MOULINS_MC_MU = "\U0001f3f4\U000e006d\U000e0063\U000e006d\U000e0075\U000e007f" - FLAG_FOR_KOLUBARA_RS_09 = "\U0001f3f4\U000e0072\U000e0073\U000e0030\U000e0039\U000e007f" - FLAG_FOR_ZLATIBOR_RS_16 = "\U0001f3f4\U000e0072\U000e0073\U000e0031\U000e0036\U000e007f" - FLAG_FOR_SALAJ_RO_SJ = "\U0001f3f4\U000e0072\U000e006f\U000e0073\U000e006a\U000e007f" - FLAG_FOR_VASLUI_RO_VS = "\U0001f3f4\U000e0072\U000e006f\U000e0076\U000e0073\U000e007f" - FLAG_FOR_POMORAVLJE_RS_13 = "\U0001f3f4\U000e0072\U000e0073\U000e0031\U000e0033\U000e007f" - FLAG_FOR_NISAVA_RS_20 = "\U0001f3f4\U000e0072\U000e0073\U000e0032\U000e0030\U000e007f" - FLAG_FOR_MEHEDINTI_RO_MH = "\U0001f3f4\U000e0072\U000e006f\U000e006d\U000e0068\U000e007f" - FLAG_FOR_RASKA_RS_18 = "\U0001f3f4\U000e0072\U000e0073\U000e0031\U000e0038\U000e007f" - FLAG_FOR_PCINJA_RS_24 = "\U0001f3f4\U000e0072\U000e0073\U000e0032\U000e0034\U000e007f" - FLAG_FOR_BEOGRAD_RS_00 = "\U0001f3f4\U000e0072\U000e0073\U000e0030\U000e0030\U000e007f" - FLAG_FOR_TACNA_PE_TAC = "\U0001f3f4\U000e0070\U000e0065\U000e0074\U000e0061\U000e0063\U000e007f" - FLAG_FOR_BELGOROD_RU_BEL = "\U0001f3f4\U000e0072\U000e0075\U000e0062\U000e0065\U000e006c\U000e007f" - FLAG_FOR_KURGAN_RU_KGN = "\U0001f3f4\U000e0072\U000e0075\U000e006b\U000e0067\U000e006e\U000e007f" - FLAG_FOR_CHELYABINSK_RU_CHE = "\U0001f3f4\U000e0072\U000e0075\U000e0063\U000e0068\U000e0065\U000e007f" - FLAG_FOR_KIROV_RU_KIR = "\U0001f3f4\U000e0072\U000e0075\U000e006b\U000e0069\U000e0072\U000e007f" - KISS_MAN_LIGHT_SKIN_TONE_MAN_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fb" - FLAG_FOR_UTTAR_PRADESH_IN_UP = "\U0001f3f4\U000e0069\U000e006e\U000e0075\U000e0070\U000e007f" - FLAG_FOR_KOMI_RU_KO = "\U0001f3f4\U000e0072\U000e0075\U000e006b\U000e006f\U000e007f" - FLAG_FOR_IVANOVO_RU_IVA = "\U0001f3f4\U000e0072\U000e0075\U000e0069\U000e0076\U000e0061\U000e007f" - FLAG_FOR_ALBA_RO_AB = "\U0001f3f4\U000e0072\U000e006f\U000e0061\U000e0062\U000e007f" - FLAG_FOR_KHANTY_MANSI_RU_KHM = "\U0001f3f4\U000e0072\U000e0075\U000e006b\U000e0068\U000e006d\U000e007f" - FLAG_FOR_KALUGA_RU_KLU = "\U0001f3f4\U000e0072\U000e0075\U000e006b\U000e006c\U000e0075\U000e007f" - FLAG_FOR_PECS_HU_PS = "\U0001f3f4\U000e0068\U000e0075\U000e0070\U000e0073\U000e007f" - FLAG_FOR_MARI_EL_RU_ME = "\U0001f3f4\U000e0072\U000e0075\U000e006d\U000e0065\U000e007f" - FLAG_FOR_IRKUTSK_RU_IRK = "\U0001f3f4\U000e0072\U000e0075\U000e0069\U000e0072\U000e006b\U000e007f" - FLAG_FOR_PLAV_ME_13 = "\U0001f3f4\U000e006d\U000e0065\U000e0031\U000e0033\U000e007f" - FLAG_FOR_GRAND_KRU_LR_GK = "\U0001f3f4\U000e006c\U000e0072\U000e0067\U000e006b\U000e007f" - FLAG_FOR_SIEM_REAP_KH_17 = "\U0001f3f4\U000e006b\U000e0068\U000e0031\U000e0037\U000e007f" - FLAG_FOR_KAMCHATKA_KRAI_RU_KAM = "\U0001f3f4\U000e0072\U000e0075\U000e006b\U000e0061\U000e006d\U000e007f" - FLAG_FOR_ALTAI_RU_AL = "\U0001f3f4\U000e0072\U000e0075\U000e0061\U000e006c\U000e007f" - FLAG_FOR_MORDOVIA_RU_MO = "\U0001f3f4\U000e0072\U000e0075\U000e006d\U000e006f\U000e007f" - FLAG_FOR_ASTRAKHAN_RU_AST = "\U0001f3f4\U000e0072\U000e0075\U000e0061\U000e0073\U000e0074\U000e007f" - FLAG_FOR_KARACHAY_CHERKESS_RU_KC = "\U0001f3f4\U000e0072\U000e0075\U000e006b\U000e0063\U000e007f" - FLAG_FOR_KEMEROVO_RU_KEM = "\U0001f3f4\U000e0072\U000e0075\U000e006b\U000e0065\U000e006d\U000e007f" - FLAG_FOR_ENGA_PG_EPW = "\U0001f3f4\U000e0070\U000e0067\U000e0065\U000e0070\U000e0077\U000e007f" - FLAG_FOR_VOLOGDA_RU_VLG = "\U0001f3f4\U000e0072\U000e0075\U000e0076\U000e006c\U000e0067\U000e007f" - FLAG_FOR_TOMSK_RU_TOM = "\U0001f3f4\U000e0072\U000e0075\U000e0074\U000e006f\U000e006d\U000e007f" - FLAG_FOR_VLADIMIR_RU_VLA = "\U0001f3f4\U000e0072\U000e0075\U000e0076\U000e006c\U000e0061\U000e007f" - FLAG_FOR_CONCEPCION_PY_1 = "\U0001f3f4\U000e0070\U000e0079\U000e0031\U000e007f" - FLAG_FOR_BAUCHI_NG_BA = "\U0001f3f4\U000e006e\U000e0067\U000e0062\U000e0061\U000e007f" - FLAG_FOR_NIZHNY_NOVGOROD_RU_NIZ = "\U0001f3f4\U000e0072\U000e0075\U000e006e\U000e0069\U000e007a\U000e007f" - FLAG_FOR_ORENBURG_RU_ORE = "\U0001f3f4\U000e0072\U000e0075\U000e006f\U000e0072\U000e0065\U000e007f" - FLAG_FOR_NOVOSIBIRSK_RU_NVS = "\U0001f3f4\U000e0072\U000e0075\U000e006e\U000e0076\U000e0073\U000e007f" - FLAG_FOR_SAINT_JOHN_GD_04 = "\U0001f3f4\U000e0067\U000e0064\U000e0030\U000e0034\U000e007f" - FLAG_FOR_TVER_RU_TVE = "\U0001f3f4\U000e0072\U000e0075\U000e0074\U000e0076\U000e0065\U000e007f" - FLAG_FOR_NOVGOROD_RU_NGR = "\U0001f3f4\U000e0072\U000e0075\U000e006e\U000e0067\U000e0072\U000e007f" - FLAG_FOR_TULA_RU_TUL = "\U0001f3f4\U000e0072\U000e0075\U000e0074\U000e0075\U000e006c\U000e007f" - FLAG_FOR_CASTELO_BRANCO_PT_05 = "\U0001f3f4\U000e0070\U000e0074\U000e0030\U000e0035\U000e007f" - FLAG_FOR_PRESIDENTE_HAYES_PY_15 = "\U0001f3f4\U000e0070\U000e0079\U000e0031\U000e0035\U000e007f" - FLAG_FOR_SMOLENSK_RU_SMO = "\U0001f3f4\U000e0072\U000e0075\U000e0073\U000e006d\U000e006f\U000e007f" - FLAG_FOR_TYUMEN_RU_TYU = "\U0001f3f4\U000e0072\U000e0075\U000e0074\U000e0079\U000e0075\U000e007f" - FLAG_FOR_SVERDLOVSK_RU_SVE = "\U0001f3f4\U000e0072\U000e0075\U000e0073\U000e0076\U000e0065\U000e007f" - FLAG_FOR_DAMAN_AND_DIU_IN_DD = "\U0001f3f4\U000e0069\U000e006e\U000e0064\U000e0064\U000e007f" - FLAG_FOR_SARATOV_RU_SAR = "\U0001f3f4\U000e0072\U000e0075\U000e0073\U000e0061\U000e0072\U000e007f" - FLAG_FOR_NENETS_RU_NEN = "\U0001f3f4\U000e0072\U000e0075\U000e006e\U000e0065\U000e006e\U000e007f" - FLAG_FOR_XIANGKHOUANG_LA_XI = "\U0001f3f4\U000e006c\U000e0061\U000e0078\U000e0069\U000e007f" - FLAG_FOR_PSKOV_RU_PSK = "\U0001f3f4\U000e0072\U000e0075\U000e0070\U000e0073\U000e006b\U000e007f" - FLAG_FOR_OTAGO_NZ_OTA = "\U0001f3f4\U000e006e\U000e007a\U000e006f\U000e0074\U000e0061\U000e007f" - FLAG_FOR_CANTERBURY_NZ_CAN = "\U0001f3f4\U000e006e\U000e007a\U000e0063\U000e0061\U000e006e\U000e007f" - FLAG_FOR_HA_IL_SA_06 = "\U0001f3f4\U000e0073\U000e0061\U000e0030\U000e0036\U000e007f" - FLAG_FOR_EASTERN_RW_02 = "\U0001f3f4\U000e0072\U000e0077\U000e0030\U000e0032\U000e007f" - FLAG_FOR_TUVA_RU_TY = "\U0001f3f4\U000e0072\U000e0075\U000e0074\U000e0079\U000e007f" - FLAG_FOR_ANSE_BOILEAU_SC_02 = "\U0001f3f4\U000e0073\U000e0063\U000e0030\U000e0032\U000e007f" - FLAG_FOR_ZABAYKALSKY_KRAI_RU_ZAB = "\U0001f3f4\U000e0072\U000e0075\U000e007a\U000e0061\U000e0062\U000e007f" - FLAG_FOR_BAIE_SAINTE_ANNE_SC_07 = "\U0001f3f4\U000e0073\U000e0063\U000e0030\U000e0037\U000e007f" - FAMILY_MAN_DARK_SKIN_TONE_MAN_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f468\U0001f3ff\u200d\U0001f467\U0001f3ff\u200d\U0001f466\U0001f3ff" - FLAG_FOR_CHOISEUL_SB_CH = "\U0001f3f4\U000e0073\U000e0062\U000e0063\U000e0068\U000e007f" - FLAG_FOR_SOUTHERN_RW_05 = "\U0001f3f4\U000e0072\U000e0077\U000e0030\U000e0035\U000e007f" - FLAG_FOR_VAS_HU_VA = "\U0001f3f4\U000e0068\U000e0075\U000e0076\U000e0061\U000e007f" - FLAG_FOR_ASIR_SA_14 = "\U0001f3f4\U000e0073\U000e0061\U000e0031\U000e0034\U000e007f" - FLAG_FOR_NAJRAN_SA_10 = "\U0001f3f4\U000e0073\U000e0061\U000e0031\U000e0030\U000e007f" - FLAG_FOR_AL_JAWF_SA_12 = "\U0001f3f4\U000e0073\U000e0061\U000e0031\U000e0032\U000e007f" - FLAG_FOR_WESTERN_RW_04 = "\U0001f3f4\U000e0072\U000e0077\U000e0030\U000e0034\U000e007f" - FLAG_FOR_RENNELL_AND_BELLONA_SB_RB = "\U0001f3f4\U000e0073\U000e0062\U000e0072\U000e0062\U000e007f" - FLAG_FOR_EASTERN_SA_04 = "\U0001f3f4\U000e0073\U000e0061\U000e0030\U000e0034\U000e007f" - FLAG_FOR_MALAITA_SB_ML = "\U0001f3f4\U000e0073\U000e0062\U000e006d\U000e006c\U000e007f" - FLAG_FOR_KRASNOYARSK_KRAI_RU_KYA = "\U0001f3f4\U000e0072\U000e0075\U000e006b\U000e0079\U000e0061\U000e007f" - FLAG_FOR_BEAU_VALLON_SC_08 = "\U0001f3f4\U000e0073\U000e0063\U000e0030\U000e0038\U000e007f" - FLAG_FOR_KIGALI_RW_01 = "\U0001f3f4\U000e0072\U000e0077\U000e0030\U000e0031\U000e007f" - FLAG_FOR_ISABEL_SB_IS = "\U0001f3f4\U000e0073\U000e0062\U000e0069\U000e0073\U000e007f" - FLAG_FOR_NORTHERN_BORDERS_SA_08 = "\U0001f3f4\U000e0073\U000e0061\U000e0030\U000e0038\U000e007f" - FLAG_FOR_YAROSLAVL_RU_YAR = "\U0001f3f4\U000e0072\U000e0075\U000e0079\U000e0061\U000e0072\U000e007f" - FLAG_FOR_WILTZ_LU_WI = "\U0001f3f4\U000e006c\U000e0075\U000e0077\U000e0069\U000e007f" - FLAG_FOR_HONIARA_SB_CT = "\U0001f3f4\U000e0073\U000e0062\U000e0063\U000e0074\U000e007f" - FLAG_FOR_NORTHERN_RW_03 = "\U0001f3f4\U000e0072\U000e0077\U000e0030\U000e0033\U000e007f" - FLAG_FOR_SONSOROL_PW_370 = "\U0001f3f4\U000e0070\U000e0077\U000e0033\U000e0037\U000e0030\U000e007f" - FLAG_FOR_TABUK_SA_07 = "\U0001f3f4\U000e0073\U000e0061\U000e0030\U000e0037\U000e007f" - FLAG_FOR_LA_RIVIERE_ANGLAISE_SC_16 = "\U0001f3f4\U000e0073\U000e0063\U000e0031\U000e0036\U000e007f" - FLAG_FOR_GRAND_ANSE_MAHE_SC_13 = "\U0001f3f4\U000e0073\U000e0063\U000e0031\U000e0033\U000e007f" - FLAG_FOR_POINTE_LA_RUE_SC_20 = "\U0001f3f4\U000e0073\U000e0063\U000e0032\U000e0030\U000e007f" - FLAG_FOR_CASCADE_SC_11 = "\U0001f3f4\U000e0073\U000e0063\U000e0031\U000e0031\U000e007f" - FLAG_FOR_NORTH_DARFUR_SD_DN = "\U0001f3f4\U000e0073\U000e0064\U000e0064\U000e006e\U000e007f" - FLAG_FOR_WEST_KURDUFAN_SD_GK = "\U0001f3f4\U000e0073\U000e0064\U000e0067\U000e006b\U000e007f" - FLAG_FOR_RIVER_NILE_SD_NR = "\U0001f3f4\U000e0073\U000e0064\U000e006e\U000e0072\U000e007f" - FLAG_FOR_EAST_DARFUR_SD_DE = "\U0001f3f4\U000e0073\U000e0064\U000e0064\U000e0065\U000e007f" - FLAG_FOR_NORRBOTTEN_SE_BD = "\U0001f3f4\U000e0073\U000e0065\U000e0062\U000e0064\U000e007f" - FLAG_FOR_SODERMANLAND_SE_D = "\U0001f3f4\U000e0073\U000e0065\U000e0064\U000e007f" - FLAG_FOR_TAKAMAKA_SC_23 = "\U0001f3f4\U000e0073\U000e0063\U000e0032\U000e0033\U000e007f" - FLAG_FOR_FRENCH_POLYNESIA_FR_PF = "\U0001f3f4\U000e0066\U000e0072\U000e0070\U000e0066\U000e007f" - FLAG_FOR_AL_QADARIF_SD_GD = "\U0001f3f4\U000e0073\U000e0064\U000e0067\U000e0064\U000e007f" - FLAG_FOR_VASTERBOTTEN_SE_AC = "\U0001f3f4\U000e0073\U000e0065\U000e0061\U000e0063\U000e007f" - FLAG_FOR_NORTHERN_SD_NO = "\U0001f3f4\U000e0073\U000e0064\U000e006e\U000e006f\U000e007f" - FLAG_FOR_MONT_FLEURI_SC_18 = "\U0001f3f4\U000e0073\U000e0063\U000e0031\U000e0038\U000e007f" - FLAG_FOR_CENTRAL_SB_CE = "\U0001f3f4\U000e0073\U000e0062\U000e0063\U000e0065\U000e007f" - FLAG_FOR_CENTRAL_DARFUR_SD_DC = "\U0001f3f4\U000e0073\U000e0064\U000e0064\U000e0063\U000e007f" - FLAG_FOR_VOLGOGRAD_RU_VGG = "\U0001f3f4\U000e0072\U000e0075\U000e0076\U000e0067\U000e0067\U000e007f" - FLAG_FOR_PLAISANCE_SC_19 = "\U0001f3f4\U000e0073\U000e0063\U000e0031\U000e0039\U000e007f" - FLAG_FOR_MONT_BUXTON_SC_17 = "\U0001f3f4\U000e0073\U000e0063\U000e0031\U000e0037\U000e007f" - FLAG_FOR_KASSALA_SD_KA = "\U0001f3f4\U000e0073\U000e0064\U000e006b\U000e0061\U000e007f" - FLAG_FOR_LES_MAMELLES_SC_24 = "\U0001f3f4\U000e0073\U000e0063\U000e0032\U000e0034\U000e007f" - FLAG_FOR_GLACIS_SC_12 = "\U0001f3f4\U000e0073\U000e0063\U000e0031\U000e0032\U000e007f" - FLAG_FOR_ROSTOV_RU_ROS = "\U0001f3f4\U000e0072\U000e0075\U000e0072\U000e006f\U000e0073\U000e007f" - FLAG_FOR_SENNAR_SD_SI = "\U0001f3f4\U000e0073\U000e0064\U000e0073\U000e0069\U000e007f" - FLAG_FOR_LA_DIGUE_SC_15 = "\U0001f3f4\U000e0073\U000e0063\U000e0031\U000e0035\U000e007f" - FLAG_FOR_SOUTH_DARFUR_SD_DS = "\U0001f3f4\U000e0073\U000e0064\U000e0064\U000e0073\U000e007f" - FLAG_FOR_WHITE_NILE_SD_NW = "\U0001f3f4\U000e0073\U000e0064\U000e006e\U000e0077\U000e007f" - FLAG_FOR_AL_JAZIRAH_SD_GZ = "\U0001f3f4\U000e0073\U000e0064\U000e0067\U000e007a\U000e007f" - FLAG_FOR_SOUTH_KURDUFAN_SD_KS = "\U0001f3f4\U000e0073\U000e0064\U000e006b\U000e0073\U000e007f" - FLAG_FOR_NORTH_KURDUFAN_SD_KN = "\U0001f3f4\U000e0073\U000e0064\U000e006b\U000e006e\U000e007f" - FLAG_FOR_PORT_GLAUD_SC_21 = "\U0001f3f4\U000e0073\U000e0063\U000e0032\U000e0031\U000e007f" - FLAG_FOR_OSTERGOTLAND_SE_E = "\U0001f3f4\U000e0073\U000e0065\U000e0065\U000e007f" - FLAG_FOR_GRAND_ANSE_PRASLIN_SC_14 = "\U0001f3f4\U000e0073\U000e0063\U000e0031\U000e0034\U000e007f" - FLAG_FOR_JONKOPING_SE_F = "\U0001f3f4\U000e0073\U000e0065\U000e0066\U000e007f" - FLAG_FOR_VARMLAND_SE_S = "\U0001f3f4\U000e0073\U000e0065\U000e0073\U000e007f" - FLAG_FOR_CERKLJE_NA_GORENJSKEM_SI_012 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0031\U000e0032\U000e007f" - FLAG_FOR_CERKNO_SI_014 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0031\U000e0034\U000e007f" - FLAG_FOR_SVETI_NIKOLE_MK_69 = "\U0001f3f4\U000e006d\U000e006b\U000e0036\U000e0039\U000e007f" - FLAG_FOR_NORTH_EAST_SG_02 = "\U0001f3f4\U000e0073\U000e0067\U000e0030\U000e0032\U000e007f" - FLAG_FOR_SOUTH_EAST_SG_04 = "\U0001f3f4\U000e0073\U000e0067\U000e0030\U000e0034\U000e007f" - FLAG_FOR_NORTH_WEST_SG_03 = "\U0001f3f4\U000e0073\U000e0067\U000e0030\U000e0033\U000e007f" - FLAG_FOR_DESTRNIK_SI_018 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0031\U000e0038\U000e007f" - FLAG_FOR_SOUTH_WEST_SG_05 = "\U0001f3f4\U000e0073\U000e0067\U000e0030\U000e0035\U000e007f" - FLAG_FOR_BRDA_SI_007 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0030\U000e0037\U000e007f" - FLAG_FOR_OREBRO_SE_T = "\U0001f3f4\U000e0073\U000e0065\U000e0074\U000e007f" - FLAG_FOR_KRONOBERG_SE_G = "\U0001f3f4\U000e0073\U000e0065\U000e0067\U000e007f" - FLAG_FOR_ASCENSION_ISLAND_SH_AC = "\U0001f3f4\U000e0073\U000e0068\U000e0061\U000e0063\U000e007f" - FLAG_FOR_VASTMANLAND_SE_U = "\U0001f3f4\U000e0073\U000e0065\U000e0075\U000e007f" - FLAG_FOR_CERKNICA_SI_013 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0031\U000e0033\U000e007f" - FLAG_FOR_CRENSOVCI_SI_015 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0031\U000e0035\U000e007f" - FLAG_FOR_REZINA_MD_RE = "\U0001f3f4\U000e006d\U000e0064\U000e0072\U000e0065\U000e007f" - KISS_WOMAN_LIGHT_SKIN_TONE_MAN_DARK_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3ff" - FLAG_FOR_BLED_SI_003 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0030\U000e0033\U000e007f" - FLAG_FOR_GOIAS_BR_GO = "\U0001f3f4\U000e0062\U000e0072\U000e0067\U000e006f\U000e007f" - FLAG_FOR_BELTINCI_SI_002 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0030\U000e0032\U000e007f" - FLAG_FOR_CRNOMELJ_SI_017 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0031\U000e0037\U000e007f" - FLAG_FOR_BREZOVICA_SI_008 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0030\U000e0038\U000e007f" - FLAG_FOR_MINYA_EG_MN = "\U0001f3f4\U000e0065\U000e0067\U000e006d\U000e006e\U000e007f" - FLAG_FOR_RIVIERE_NOIRE_MU_BL = "\U0001f3f4\U000e006d\U000e0075\U000e0062\U000e006c\U000e007f" - FLAG_FOR_GOVI_ALTAI_MN_065 = "\U0001f3f4\U000e006d\U000e006e\U000e0030\U000e0036\U000e0035\U000e007f" - FLAG_FOR_AJDOVSCINA_SI_001 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0030\U000e0031\U000e007f" - FLAG_FOR_BOROVNICA_SI_005 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0030\U000e0035\U000e007f" - FLAG_FOR_JURSINCI_SI_042 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0034\U000e0032\U000e007f" - FLAG_FOR_IDRIJA_SI_036 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0033\U000e0036\U000e007f" - FLAG_FOR_KUZMA_SI_056 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0035\U000e0036\U000e007f" - FLAG_FOR_ULYANOVSK_RU_ULY = "\U0001f3f4\U000e0072\U000e0075\U000e0075\U000e006c\U000e0079\U000e007f" - FLAG_FOR_IZOLA_SI_040 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0034\U000e0030\U000e007f" - FLAG_FOR_KOZJE_SI_051 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0035\U000e0031\U000e007f" - FLAG_FOR_KUNGOTA_SI_055 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0035\U000e0035\U000e007f" - FLAG_FOR_JESENICE_SI_041 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0034\U000e0031\U000e007f" - FLAG_FOR_GORNJI_GRAD_SI_030 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0033\U000e0030\U000e007f" - FLAG_FOR_DIVACA_SI_019 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0031\U000e0039\U000e007f" - FLAG_FOR_IVANCNA_GORICA_SI_039 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0033\U000e0039\U000e007f" - FLAG_FOR_KRSKO_SI_054 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0035\U000e0034\U000e007f" - FLAG_FOR_KAMNIK_SI_043 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0034\U000e0033\U000e007f" - FLAG_FOR_DOBREPOLJE_SI_020 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0032\U000e0030\U000e007f" - FLAG_FOR_SALOVCI_SI_033 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0033\U000e0033\U000e007f" - FLAG_FOR_KOBILJE_SI_047 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0034\U000e0037\U000e007f" - FLAG_FOR_HRASTNIK_SI_034 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0033\U000e0034\U000e007f" - FLAG_FOR_DOBROVA_POLHOV_GRADEC_SI_021 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0032\U000e0031\U000e007f" - FLAG_FOR_DRAVOGRAD_SI_025 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0032\U000e0035\U000e007f" - FLAG_FOR_STOCKHOLM_SE_AB = "\U0001f3f4\U000e0073\U000e0065\U000e0061\U000e0062\U000e007f" - FLAG_FOR_DUPLEK_SI_026 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0032\U000e0036\U000e007f" - FLAG_FOR_SALACGRIVA_LV_086 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0038\U000e0036\U000e007f" - FLAG_FOR_KOCEVJE_SI_048 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0034\U000e0038\U000e007f" - FLAG_FOR_KRANJSKA_GORA_SI_053 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0035\U000e0033\U000e007f" - FLAG_FOR_KANAL_SI_044 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0034\U000e0034\U000e007f" - FLAG_FOR_GORNJI_PETROVCI_SI_031 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0033\U000e0031\U000e007f" - FLAG_FOR_GROSUPLJE_SI_032 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0033\U000e0032\U000e007f" - FLAG_FOR_GORISNICA_SI_028 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0032\U000e0038\U000e007f" - FLAG_FOR_TEMOTU_SB_TE = "\U0001f3f4\U000e0073\U000e0062\U000e0074\U000e0065\U000e007f" - FLAG_FOR_DORNAVA_SI_024 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0032\U000e0034\U000e007f" - FLAG_FOR_DOMZALE_SI_023 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0032\U000e0033\U000e007f" - FLAG_FOR_KOBARID_SI_046 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0034\U000e0036\U000e007f" - FLAG_FOR_GORNJA_RADGONA_SI_029 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0032\U000e0039\U000e007f" - FLAG_FOR_ILIRSKA_BISTRICA_SI_038 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0033\U000e0038\U000e007f" - FLAG_FOR_KOMEN_SI_049 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0034\U000e0039\U000e007f" - FLAG_FOR_MARIBOR_SI_070 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0037\U000e0030\U000e007f" - FLAG_FOR_MEZICA_SI_074 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0037\U000e0034\U000e007f" - FLAG_FOR_LUCE_SI_067 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0036\U000e0037\U000e007f" - FLAG_FOR_PODVELKA_SI_093 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0039\U000e0033\U000e007f" - FLAG_FOR_LOSKI_POTOK_SI_066 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0036\U000e0036\U000e007f" - FLAG_FOR_NAZARJE_SI_083 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0038\U000e0033\U000e007f" - FLAG_FOR_LITIJA_SI_060 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0036\U000e0030\U000e007f" - FLAG_FOR_LUKOVICA_SI_068 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0036\U000e0038\U000e007f" - FLAG_FOR_PESNICA_SI_089 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0038\U000e0039\U000e007f" - FLAG_FOR_OSILNICA_SI_088 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0038\U000e0038\U000e007f" - FLAG_FOR_LENDAVA_SI_059 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0035\U000e0039\U000e007f" - FLAG_FOR_MIREN_KOSTANJEVICA_SI_075 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0037\U000e0035\U000e007f" - FLAG_FOR_MORAVSKE_TOPLICE_SI_078 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0037\U000e0038\U000e007f" - FLAG_FOR_PODCETRTEK_SI_092 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0039\U000e0032\U000e007f" - FLAG_FOR_LJUTOMER_SI_063 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0036\U000e0033\U000e007f" - FLAG_FOR_LASKO_SI_057 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0035\U000e0037\U000e007f" - FLAG_FOR_PIVKA_SI_091 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0039\U000e0031\U000e007f" - FLAG_FOR_METLIKA_SI_073 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0037\U000e0033\U000e007f" - FLAG_FOR_HERCEG_NOVI_ME_08 = "\U0001f3f4\U000e006d\U000e0065\U000e0030\U000e0038\U000e007f" - FLAG_FOR_LOSKA_DOLINA_SI_065 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0036\U000e0035\U000e007f" - FLAG_FOR_ANSE_ETOILE_SC_03 = "\U0001f3f4\U000e0073\U000e0063\U000e0030\U000e0033\U000e007f" - FLAG_FOR_ORMOZ_SI_087 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0038\U000e0037\U000e007f" - FLAG_FOR_LOGATEC_SI_064 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0036\U000e0034\U000e007f" - FLAG_FOR_NAKLO_SI_082 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0038\U000e0032\U000e007f" - FLAG_FOR_NOVA_GORICA_SI_084 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0038\U000e0034\U000e007f" - FLAG_FOR_LJUBNO_SI_062 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0036\U000e0032\U000e007f" - FLAG_FOR_MISLINJA_SI_076 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0037\U000e0036\U000e007f" - FLAG_FOR_MEDVODE_SI_071 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0037\U000e0031\U000e007f" - FLAG_FOR_POSTOJNA_SI_094 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0039\U000e0034\U000e007f" - FLAG_FOR_MORAVCE_SI_077 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0037\U000e0037\U000e007f" - FLAG_FOR_LJUBLJANA_SI_061 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0036\U000e0031\U000e007f" - FLAG_FOR_MAJSPERK_SI_069 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0036\U000e0039\U000e007f" - FLAG_FOR_MENGES_SI_072 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0037\U000e0032\U000e007f" - FLAG_FOR_MUTA_SI_081 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0038\U000e0031\U000e007f" - FLAG_FOR_MOZIRJE_SI_079 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0037\U000e0039\U000e007f" - FLAG_FOR_MURSKA_SOBOTA_SI_080 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0038\U000e0030\U000e007f" - FLAG_FOR_RIBNICA_SI_104 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0030\U000e0034\U000e007f" - FLAG_FOR_SKOFLJICA_SI_123 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0032\U000e0033\U000e007f" - FLAG_FOR_SEZANA_SI_111 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0031\U000e0031\U000e007f" - FLAG_FOR_TRBOVLJE_SI_129 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0032\U000e0039\U000e007f" - FLAG_FOR_SLOVENSKE_KONJICE_SI_114 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0031\U000e0034\U000e007f" - FLAG_FOR_STORE_SI_127 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0032\U000e0037\U000e007f" - FLAG_FOR_PUCONCI_SI_097 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0039\U000e0037\U000e007f" - FLAG_FOR_TREBNJE_SI_130 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0033\U000e0030\U000e007f" - FLAG_FOR_ROGATEC_SI_107 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0030\U000e0037\U000e007f" - FLAG_FOR_TOLMIN_SI_128 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0032\U000e0038\U000e007f" - FLAG_FOR_RADOVLJICA_SI_102 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0030\U000e0032\U000e007f" - FLAG_FOR_SEVNICA_SI_110 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0031\U000e0030\U000e007f" - FLAG_FOR_STARSE_SI_115 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0031\U000e0035\U000e007f" - FLAG_FOR_RADLJE_OB_DRAVI_SI_101 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0030\U000e0031\U000e007f" - FLAG_FOR_PTUJ_SI_096 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0039\U000e0036\U000e007f" - FLAG_FOR_SLOVENSKA_BISTRICA_SI_113 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0031\U000e0033\U000e007f" - FLAG_FOR_SEMIC_SI_109 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0030\U000e0039\U000e007f" - FLAG_FOR_PREDDVOR_SI_095 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0039\U000e0035\U000e007f" - FLAG_FOR_RADECE_SI_099 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0039\U000e0039\U000e007f" - FLAG_FOR_SOSTANJ_SI_126 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0032\U000e0036\U000e007f" - FLAG_FOR_ROGASKA_SLATINA_SI_106 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0030\U000e0036\U000e007f" - FLAG_FOR_TRZIC_SI_131 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0033\U000e0031\U000e007f" - FLAG_FOR_RUSE_SI_108 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0030\U000e0038\U000e007f" - FLAG_FOR_SMARJE_PRI_JELSAH_SI_124 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0032\U000e0034\U000e007f" - FLAG_FOR_TURNISCE_SI_132 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0033\U000e0032\U000e007f" - FLAG_FOR_SKOCJAN_SI_121 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0032\U000e0031\U000e007f" - FLAG_FOR_SVETI_JURIJ_SI_116 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0031\U000e0036\U000e007f" - FLAG_FOR_RACE_FRAM_SI_098 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0039\U000e0038\U000e007f" - FLAG_FOR_SLOVENJ_GRADEC_SI_112 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0031\U000e0032\U000e007f" - FLAG_FOR_SENTILJ_SI_118 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0031\U000e0038\U000e007f" - FLAG_FOR_ROGASOVCI_SI_105 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0030\U000e0035\U000e007f" - FLAG_FOR_SENTJERNEJ_SI_119 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0031\U000e0039\U000e007f" - FLAG_FOR_SENCUR_SI_117 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0031\U000e0037\U000e007f" - FLAG_FOR_SENTJUR_SI_120 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0032\U000e0030\U000e007f" - FLAG_FOR_RAVNE_NA_KOROSKEM_SI_103 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0030\U000e0033\U000e007f" - FLAG_FOR_DOLENJSKE_TOPLICE_SI_157 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0035\U000e0037\U000e007f" - FLAG_FOR_MIKLAVZ_NA_DRAVSKEM_POLJU_SI_169 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0036\U000e0039\U000e007f" - FLAG_FOR_VIDEM_SI_135 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0033\U000e0035\U000e007f" - FLAG_FOR_ZRECE_SI_144 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0034\U000e0034\U000e007f" - FLAG_FOR_WEST_DARFUR_SD_DW = "\U0001f3f4\U000e0073\U000e0064\U000e0064\U000e0077\U000e007f" - FLAG_FOR_KOSTEL_SI_165 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0036\U000e0035\U000e007f" - FLAG_FOR_VOJNIK_SI_139 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0033\U000e0039\U000e007f" - FLAG_FOR_BENEDIKT_SI_148 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0034\U000e0038\U000e007f" - FLAG_FOR_HORJUL_SI_162 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0036\U000e0032\U000e007f" - FLAG_FOR_OPLOTNICA_SI_171 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0037\U000e0031\U000e007f" - FLAG_FOR_JEZERSKO_SI_163 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0036\U000e0033\U000e007f" - FLAG_FOR_VITANJE_SI_137 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0033\U000e0037\U000e007f" - FLAG_FOR_ZAGORJE_OB_SAVI_SI_142 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0034\U000e0032\U000e007f" - FLAG_FOR_VUZENICA_SI_141 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0034\U000e0031\U000e007f" - FLAG_FOR_VIPAVA_SI_136 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0033\U000e0036\U000e007f" - FLAG_FOR_HAJDINA_SI_159 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0035\U000e0039\U000e007f" - FLAG_FOR_ZELEZNIKI_SI_146 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0034\U000e0036\U000e007f" - FLAG_FOR_ZIRI_SI_147 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0034\U000e0037\U000e007f" - FLAG_FOR_KRIZEVCI_SI_166 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0036\U000e0036\U000e007f" - FLAG_FOR_CERKVENJAK_SI_153 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0035\U000e0033\U000e007f" - FLAG_FOR_VRHNIKA_SI_140 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0034\U000e0030\U000e007f" - FLAG_FOR_HOCE_SLIVNICA_SI_160 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0036\U000e0030\U000e007f" - FLAG_FOR_VODICE_SI_138 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0033\U000e0038\U000e007f" - FLAG_FOR_MIRNA_PEC_SI_170 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0037\U000e0030\U000e007f" - FLAG_FOR_MARKOVCI_SI_168 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0036\U000e0038\U000e007f" - FLAG_FOR_HODOS_SI_161 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0036\U000e0031\U000e007f" - FLAG_FOR_DOBRNA_SI_155 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0035\U000e0035\U000e007f" - FAMILY_WOMAN_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f467\U0001f3fb\u200d\U0001f476\U0001f3fb" - FLAG_FOR_KOMENDA_SI_164 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0036\U000e0034\U000e007f" - FLAG_FOR_ZALEC_SI_190 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0039\U000e0030\U000e007f" - FLAG_FOR_TRNOVSKA_VAS_SI_185 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0038\U000e0035\U000e007f" - FLAG_FOR_SMARTNO_PRI_LITIJI_SI_194 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0039\U000e0034\U000e007f" - FLAG_FOR_GORJE_SI_207 = "\U0001f3f4\U000e0073\U000e0069\U000e0032\U000e0030\U000e0037\U000e007f" - FLAG_FOR_PREBOLD_SI_174 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0037\U000e0034\U000e007f" - FLAG_FOR_SELNICA_OB_DRAVI_SI_178 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0037\U000e0038\U000e007f" - FLAG_FOR_APACE_SI_195 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0039\U000e0035\U000e007f" - FLAG_FOR_CIRKULANE_SI_196 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0039\U000e0036\U000e007f" - FLAG_FOR_SOLCAVA_SI_180 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0038\U000e0030\U000e007f" - FLAG_FOR_SEMPETER_VRTOJBA_SI_183 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0038\U000e0033\U000e007f" - FLAG_FOR_VALLON_DE_LA_ROUSSE_MC_VR = "\U0001f3f4\U000e006d\U000e0063\U000e0076\U000e0072\U000e007f" - FLAG_FOR_SODRAZICA_SI_179 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0037\U000e0039\U000e007f" - FLAG_FOR_RENCE_VOGRSKO_SI_201 = "\U0001f3f4\U000e0073\U000e0069\U000e0032\U000e0030\U000e0031\U000e007f" - FLAG_FOR_PREVALJE_SI_175 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0037\U000e0035\U000e007f" - FLAG_FOR_POLJCANE_SI_200 = "\U0001f3f4\U000e0073\U000e0069\U000e0032\U000e0030\U000e0030\U000e007f" - FLAG_FOR_RIBNICA_NA_POHORJU_SI_177 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0037\U000e0037\U000e007f" - FLAG_FOR_VERZEJ_SI_188 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0038\U000e0038\U000e007f" - FLAG_FOR_MAKOLE_SI_198 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0039\U000e0038\U000e007f" - FLAG_FOR_PODLEHNIK_SI_172 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0037\U000e0032\U000e007f" - FLAG_FOR_POLZELA_SI_173 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0037\U000e0033\U000e007f" - FLAG_FOR_RAZKRIZJE_SI_176 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0037\U000e0036\U000e007f" - FLAG_FOR_TABOR_SI_184 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0038\U000e0034\U000e007f" - FLAG_FOR_KOSTANJEVICA_NA_KRKI_SI_197 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0039\U000e0037\U000e007f" - FLAG_FOR_ZETALE_SI_191 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0039\U000e0031\U000e007f" - FLAG_FOR_ZUZEMBERK_SI_193 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0039\U000e0033\U000e007f" - FLAG_FOR_STRAZA_SI_203 = "\U0001f3f4\U000e0073\U000e0069\U000e0032\U000e0030\U000e0033\U000e007f" - FLAG_FOR_VRANSKO_SI_189 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0038\U000e0039\U000e007f" - FLAG_FOR_TUBAS_PS_TBS = "\U0001f3f4\U000e0070\U000e0073\U000e0074\U000e0062\U000e0073\U000e007f" - FLAG_FOR_TRZIN_SI_186 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0038\U000e0036\U000e007f" - FLAG_FOR_SVETI_TOMAZ_SI_205 = "\U0001f3f4\U000e0073\U000e0069\U000e0032\U000e0030\U000e0035\U000e007f" - FLAG_FOR_MOKRONOG_TREBELNO_SI_199 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0039\U000e0039\U000e007f" - FLAG_FOR_WESTERN_AREA_SL_W = "\U0001f3f4\U000e0073\U000e006c\U000e0077\U000e007f" - FLAG_FOR_CALABRIA_IT_78 = "\U0001f3f4\U000e0069\U000e0074\U000e0037\U000e0038\U000e007f" - FLAG_FOR_SERRAVALLE_SM_09 = "\U0001f3f4\U000e0073\U000e006d\U000e0030\U000e0039\U000e007f" - FLAG_FOR_FAETANO_SM_04 = "\U0001f3f4\U000e0073\U000e006d\U000e0030\U000e0034\U000e007f" - FLAG_FOR_SENTRUPERT_SI_211 = "\U0001f3f4\U000e0073\U000e0069\U000e0032\U000e0031\U000e0031\U000e007f" - FLAG_FOR_TRNAVA_SK_TA = "\U0001f3f4\U000e0073\U000e006b\U000e0074\U000e0061\U000e007f" - FLAG_FOR_ACQUAVIVA_SM_01 = "\U0001f3f4\U000e0073\U000e006d\U000e0030\U000e0031\U000e007f" - FLAG_FOR_SOUTHERN_SL_S = "\U0001f3f4\U000e0073\U000e006c\U000e0073\U000e007f" - FLAG_FOR_PRESOV_SK_PV = "\U0001f3f4\U000e0073\U000e006b\U000e0070\U000e0076\U000e007f" - FLAG_FOR_KOSICE_SK_KI = "\U0001f3f4\U000e0073\U000e006b\U000e006b\U000e0069\U000e007f" - FLAG_FOR_NORTHERN_SL_N = "\U0001f3f4\U000e0073\U000e006c\U000e006e\U000e007f" - FLAG_FOR_KAOLACK_SN_KL = "\U0001f3f4\U000e0073\U000e006e\U000e006b\U000e006c\U000e007f" - FLAG_FOR_SAN_MARINO_SM_07 = "\U0001f3f4\U000e0073\U000e006d\U000e0030\U000e0037\U000e007f" - FLAG_FOR_FIORENTINO_SM_05 = "\U0001f3f4\U000e0073\U000e006d\U000e0030\U000e0035\U000e007f" - FLAG_FOR_TAMBACOUNDA_SN_TC = "\U0001f3f4\U000e0073\U000e006e\U000e0074\U000e0063\U000e007f" - FLAG_FOR_SEOUL_KR_11 = "\U0001f3f4\U000e006b\U000e0072\U000e0031\U000e0031\U000e007f" - FLAG_FOR_CHIESANUOVA_SM_02 = "\U0001f3f4\U000e0073\U000e006d\U000e0030\U000e0032\U000e007f" - FLAG_FOR_KOLDA_SN_KD = "\U0001f3f4\U000e0073\U000e006e\U000e006b\U000e0064\U000e007f" - FLAG_FOR_KAFFRINE_SN_KA = "\U0001f3f4\U000e0073\U000e006e\U000e006b\U000e0061\U000e007f" - FLAG_FOR_SEDHIOU_SN_SE = "\U0001f3f4\U000e0073\U000e006e\U000e0073\U000e0065\U000e007f" - FLAG_FOR_FATICK_SN_FK = "\U0001f3f4\U000e0073\U000e006e\U000e0066\U000e006b\U000e007f" - FLAG_FOR_TRENCIN_SK_TC = "\U0001f3f4\U000e0073\U000e006b\U000e0074\U000e0063\U000e007f" - FLAG_FOR_BANSKA_BYSTRICA_SK_BC = "\U0001f3f4\U000e0073\U000e006b\U000e0062\U000e0063\U000e007f" - FLAG_FOR_LOUGA_SN_LG = "\U0001f3f4\U000e0073\U000e006e\U000e006c\U000e0067\U000e007f" - FLAG_FOR_NITRA_SK_NI = "\U0001f3f4\U000e0073\U000e006b\U000e006e\U000e0069\U000e007f" - FLAG_FOR_RECICA_OB_SAVINJI_SI_209 = "\U0001f3f4\U000e0073\U000e0069\U000e0032\U000e0030\U000e0039\U000e007f" - FLAG_FOR_RAJASTHAN_IN_RJ = "\U0001f3f4\U000e0069\U000e006e\U000e0072\U000e006a\U000e007f" - FLAG_FOR_LENINGRAD_RU_LEN = "\U0001f3f4\U000e0072\U000e0075\U000e006c\U000e0065\U000e006e\U000e007f" - FLAG_FOR_ZILINA_SK_ZI = "\U0001f3f4\U000e0073\U000e006b\U000e007a\U000e0069\U000e007f" - FLAG_FOR_BORGO_MAGGIORE_SM_06 = "\U0001f3f4\U000e0073\U000e006d\U000e0030\U000e0036\U000e007f" - FLAG_FOR_MATAM_SN_MT = "\U0001f3f4\U000e0073\U000e006e\U000e006d\U000e0074\U000e007f" - FLAG_FOR_DOMAGNANO_SM_03 = "\U0001f3f4\U000e0073\U000e006d\U000e0030\U000e0033\U000e007f" - FLAG_FOR_KEDOUGOU_SN_KE = "\U0001f3f4\U000e0073\U000e006e\U000e006b\U000e0065\U000e007f" - FLAG_FOR_MIRNA_SI_212 = "\U0001f3f4\U000e0073\U000e0069\U000e0032\U000e0031\U000e0032\U000e007f" - FLAG_FOR_THIES_SN_TH = "\U0001f3f4\U000e0073\U000e006e\U000e0074\U000e0068\U000e007f" - FLAG_FOR_MONTEGIARDINO_SM_08 = "\U0001f3f4\U000e0073\U000e006d\U000e0030\U000e0038\U000e007f" - FLAG_FOR_VALCEA_RO_VL = "\U0001f3f4\U000e0072\U000e006f\U000e0076\U000e006c\U000e007f" - FLAG_FOR_CLUJ_RO_CJ = "\U0001f3f4\U000e0072\U000e006f\U000e0063\U000e006a\U000e007f" - FLAG_FOR_TOGDHEER_SO_TO = "\U0001f3f4\U000e0073\U000e006f\U000e0074\U000e006f\U000e007f" - FLAG_FOR_UPPER_NILE_SS_NU = "\U0001f3f4\U000e0073\U000e0073\U000e006e\U000e0075\U000e007f" - FLAG_FOR_SARAMACCA_SR_SA = "\U0001f3f4\U000e0073\U000e0072\U000e0073\U000e0061\U000e007f" - FLAG_FOR_WESTERN_BAHR_EL_GHAZAL_SS_BW = "\U0001f3f4\U000e0073\U000e0073\U000e0062\U000e0077\U000e007f" - FLAG_FOR_WARRAP_SS_WR = "\U0001f3f4\U000e0073\U000e0073\U000e0077\U000e0072\U000e007f" - DIE_FACE_6 = "\u2685" - FLAG_FOR_NICKERIE_SR_NI = "\U0001f3f4\U000e0073\U000e0072\U000e006e\U000e0069\U000e007f" - FLAG_FOR_EASTERN_EQUATORIA_SS_EE = "\U0001f3f4\U000e0073\U000e0073\U000e0065\U000e0065\U000e007f" - FLAG_FOR_BAKOOL_SO_BK = "\U0001f3f4\U000e0073\U000e006f\U000e0062\U000e006b\U000e007f" - FLAG_FOR_PARAMARIBO_SR_PM = "\U0001f3f4\U000e0073\U000e0072\U000e0070\U000e006d\U000e007f" - FLAG_FOR_WANICA_SR_WA = "\U0001f3f4\U000e0073\U000e0072\U000e0077\U000e0061\U000e007f" - FLAG_FOR_ANGAUR_PW_010 = "\U0001f3f4\U000e0070\U000e0077\U000e0030\U000e0031\U000e0030\U000e007f" - FLAG_FOR_MUDUG_SO_MU = "\U0001f3f4\U000e0073\U000e006f\U000e006d\U000e0075\U000e007f" - FLAG_FOR_MIDDLE_JUBA_SO_JD = "\U0001f3f4\U000e0073\U000e006f\U000e006a\U000e0064\U000e007f" - FLAG_FOR_WESTERN_EQUATORIA_SS_EW = "\U0001f3f4\U000e0073\U000e0073\U000e0065\U000e0077\U000e007f" - FLAG_FOR_PRINCIPE_ST_P = "\U0001f3f4\U000e0073\U000e0074\U000e0070\U000e007f" - FLAG_FOR_CORONIE_SR_CR = "\U0001f3f4\U000e0073\U000e0072\U000e0063\U000e0072\U000e007f" - FLAG_FOR_CONSTANTA_RO_CT = "\U0001f3f4\U000e0072\U000e006f\U000e0063\U000e0074\U000e007f" - FLAG_FOR_HIRAN_SO_HI = "\U0001f3f4\U000e0073\U000e006f\U000e0068\U000e0069\U000e007f" - FLAG_FOR_KARELIA_RU_KR = "\U0001f3f4\U000e0072\U000e0075\U000e006b\U000e0072\U000e007f" - FLAG_FOR_LOWER_SHEBELLE_SO_SH = "\U0001f3f4\U000e0073\U000e006f\U000e0073\U000e0068\U000e007f" - FLAG_FOR_GEDO_SO_GE = "\U0001f3f4\U000e0073\U000e006f\U000e0067\U000e0065\U000e007f" - FLAG_FOR_ZIGUINCHOR_SN_ZG = "\U0001f3f4\U000e0073\U000e006e\U000e007a\U000e0067\U000e007f" - FLAG_FOR_BOQUERON_PY_19 = "\U0001f3f4\U000e0070\U000e0079\U000e0031\U000e0039\U000e007f" - FLAG_FOR_MIDDLE_SHEBELLE_SO_SD = "\U0001f3f4\U000e0073\U000e006f\U000e0073\U000e0064\U000e007f" - FLAG_FOR_SANAAG_SO_SA = "\U0001f3f4\U000e0073\U000e006f\U000e0073\U000e0061\U000e007f" - FLAG_FOR_BROKOPONDO_SR_BR = "\U0001f3f4\U000e0073\U000e0072\U000e0062\U000e0072\U000e007f" - FLAG_FOR_BREZICE_SI_009 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0030\U000e0039\U000e007f" - FLAG_FOR_HRPELJE_KOZINA_SI_035 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0033\U000e0035\U000e007f" - FLAG_FOR_AWDAL_SO_AW = "\U0001f3f4\U000e0073\U000e006f\U000e0061\U000e0077\U000e007f" - FLAG_FOR_LATAKIA_SY_LA = "\U0001f3f4\U000e0073\U000e0079\U000e006c\U000e0061\U000e007f" - FLAG_FOR_AS_SUWAYDA_SY_SU = "\U0001f3f4\U000e0073\U000e0079\U000e0073\U000e0075\U000e007f" - FLAG_FOR_AR_RAQQAH_SY_RA = "\U0001f3f4\U000e0073\U000e0079\U000e0072\U000e0061\U000e007f" - FLAG_FOR_CUSCATLAN_SV_CU = "\U0001f3f4\U000e0073\U000e0076\U000e0063\U000e0075\U000e007f" - FLAG_FOR_CHALATENANGO_SV_CH = "\U0001f3f4\U000e0073\U000e0076\U000e0063\U000e0068\U000e007f" - FLAG_FOR_GALGUDUUD_SO_GA = "\U0001f3f4\U000e0073\U000e006f\U000e0067\U000e0061\U000e007f" - FLAG_FOR_DOBROVNIK_SI_156 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0035\U000e0036\U000e007f" - FLAG_FOR_BORKOU_TD_BO = "\U0001f3f4\U000e0074\U000e0064\U000e0062\U000e006f\U000e007f" - FLAG_FOR_LUBOMBO_SZ_LU = "\U0001f3f4\U000e0073\U000e007a\U000e006c\U000e0075\U000e007f" - FLAG_FOR_MANZINI_SZ_MA = "\U0001f3f4\U000e0073\U000e007a\U000e006d\U000e0061\U000e007f" - FLAG_FOR_MORAZAN_SV_MO = "\U0001f3f4\U000e0073\U000e0076\U000e006d\U000e006f\U000e007f" - FLAG_FOR_DARAA_SY_DR = "\U0001f3f4\U000e0073\U000e0079\U000e0064\U000e0072\U000e007f" - FLAG_FOR_SANTA_ANA_SV_SA = "\U0001f3f4\U000e0073\U000e0076\U000e0073\U000e0061\U000e007f" - FLAG_FOR_TARTUS_SY_TA = "\U0001f3f4\U000e0073\U000e0079\U000e0074\U000e0061\U000e007f" - FLAG_FOR_SHISELWENI_SZ_SH = "\U0001f3f4\U000e0073\U000e007a\U000e0073\U000e0068\U000e007f" - FLAG_FOR_LA_UNION_SV_UN = "\U0001f3f4\U000e0073\U000e0076\U000e0075\U000e006e\U000e007f" - FLAG_FOR_BATHA_TD_BA = "\U0001f3f4\U000e0074\U000e0064\U000e0062\U000e0061\U000e007f" - FLAG_FOR_KANEM_TD_KA = "\U0001f3f4\U000e0074\U000e0064\U000e006b\U000e0061\U000e007f" - FLAG_FOR_GUADELOUPE_FR_GUA = "\U0001f3f4\U000e0066\U000e0072\U000e0067\U000e0075\U000e0061\U000e007f" - FLAG_FOR_QUNEITRA_SY_QU = "\U0001f3f4\U000e0073\U000e0079\U000e0071\U000e0075\U000e007f" - FLAG_FOR_HOMS_SY_HI = "\U0001f3f4\U000e0073\U000e0079\U000e0068\U000e0069\U000e007f" - FLAG_FOR_AL_HASAKAH_SY_HA = "\U0001f3f4\U000e0073\U000e0079\U000e0068\U000e0061\U000e007f" - FLAG_FOR_HAMA_SY_HM = "\U0001f3f4\U000e0073\U000e0079\U000e0068\U000e006d\U000e007f" - FLAG_FOR_AHUACHAPAN_SV_AH = "\U0001f3f4\U000e0073\U000e0076\U000e0061\U000e0068\U000e007f" - FLAG_FOR_SONSONATE_SV_SO = "\U0001f3f4\U000e0073\U000e0076\U000e0073\U000e006f\U000e007f" - FLAG_FOR_CABANAS_SV_CA = "\U0001f3f4\U000e0073\U000e0076\U000e0063\U000e0061\U000e007f" - FLAG_FOR_ENNEDI_EST_TD_EE = "\U0001f3f4\U000e0074\U000e0064\U000e0065\U000e0065\U000e007f" - FLAG_FOR_ALEPPO_SY_HL = "\U0001f3f4\U000e0073\U000e0079\U000e0068\U000e006c\U000e007f" - FLAG_FOR_GOVISUMBER_MN_064 = "\U0001f3f4\U000e006d\U000e006e\U000e0030\U000e0036\U000e0034\U000e007f" - FLAG_FOR_LA_LIBERTAD_SV_LI = "\U0001f3f4\U000e0073\U000e0076\U000e006c\U000e0069\U000e007f" - FLAG_FOR_BAHR_EL_GAZEL_TD_BG = "\U0001f3f4\U000e0074\U000e0064\U000e0062\U000e0067\U000e007f" - FLAG_FOR_SIPALIWINI_SR_SI = "\U0001f3f4\U000e0073\U000e0072\U000e0073\U000e0069\U000e007f" - FLAG_FOR_FINNMARK_NO_20 = "\U0001f3f4\U000e006e\U000e006f\U000e0032\U000e0030\U000e007f" - FLAG_FOR_NONTHABURI_TH_12 = "\U0001f3f4\U000e0074\U000e0068\U000e0031\U000e0032\U000e007f" - FLAG_FOR_PATHUM_THANI_TH_13 = "\U0001f3f4\U000e0074\U000e0068\U000e0031\U000e0033\U000e007f" - FAMILY_MAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f469\U0001f3fd\u200d\U0001f476\U0001f3fd" - FLAG_FOR_WADI_FIRA_TD_WF = "\U0001f3f4\U000e0074\U000e0064\U000e0077\U000e0066\U000e007f" - FLAG_FOR_PRACHIN_BURI_TH_25 = "\U0001f3f4\U000e0074\U000e0068\U000e0032\U000e0035\U000e007f" - FLAG_FOR_BURI_RAM_TH_31 = "\U0001f3f4\U000e0074\U000e0068\U000e0033\U000e0031\U000e007f" - FLAG_FOR_NAKHON_RATCHASIMA_TH_30 = "\U0001f3f4\U000e0074\U000e0068\U000e0033\U000e0030\U000e007f" - FLAG_FOR_N_DJAMENA_TD_ND = "\U0001f3f4\U000e0074\U000e0064\U000e006e\U000e0064\U000e007f" - FLAG_FOR_PHRA_NAKHON_SI_AYUTTHAYA_TH_14 = "\U0001f3f4\U000e0074\U000e0068\U000e0031\U000e0034\U000e007f" - FLAG_FOR_MARSASKALA_MT_27 = "\U0001f3f4\U000e006d\U000e0074\U000e0032\U000e0037\U000e007f" - FLAG_FOR_LOGONE_OCCIDENTAL_TD_LO = "\U0001f3f4\U000e0074\U000e0064\U000e006c\U000e006f\U000e007f" - FLAG_FOR_CHACHOENGSAO_TH_24 = "\U0001f3f4\U000e0074\U000e0068\U000e0032\U000e0034\U000e007f" - FLAG_FOR_TIBESTI_TD_TI = "\U0001f3f4\U000e0074\U000e0064\U000e0074\U000e0069\U000e007f" - FLAG_FOR_CHAI_NAT_TH_18 = "\U0001f3f4\U000e0074\U000e0068\U000e0031\U000e0038\U000e007f" - FLAG_FOR_MOYEN_CHARI_TD_MC = "\U0001f3f4\U000e0074\U000e0064\U000e006d\U000e0063\U000e007f" - FLAG_FOR_TANDJILE_TD_TA = "\U0001f3f4\U000e0074\U000e0064\U000e0074\U000e0061\U000e007f" - FLAG_FOR_SAINT_HELENA_SH_HL = "\U0001f3f4\U000e0073\U000e0068\U000e0068\U000e006c\U000e007f" - FLAG_FOR_PLATEAUX_TG_P = "\U0001f3f4\U000e0074\U000e0067\U000e0070\U000e007f" - FLAG_FOR_SALAMAT_TD_SA = "\U0001f3f4\U000e0074\U000e0064\U000e0073\U000e0061\U000e007f" - FLAG_FOR_CHON_BURI_TH_20 = "\U0001f3f4\U000e0074\U000e0068\U000e0032\U000e0030\U000e007f" - FLAG_FOR_MANDOUL_TD_MA = "\U0001f3f4\U000e0074\U000e0064\U000e006d\U000e0061\U000e007f" - FLAG_FOR_MAYO_KEBBI_EST_TD_ME = "\U0001f3f4\U000e0074\U000e0064\U000e006d\U000e0065\U000e007f" - FLAG_FOR_SURIN_TH_32 = "\U0001f3f4\U000e0074\U000e0068\U000e0033\U000e0032\U000e007f" - FLAG_FOR_ARDAHAN_TR_75 = "\U0001f3f4\U000e0074\U000e0072\U000e0037\U000e0035\U000e007f" - FLAG_FOR_NAKHON_NAYOK_TH_26 = "\U0001f3f4\U000e0074\U000e0068\U000e0032\U000e0036\U000e007f" - FLAG_FOR_SARABURI_TH_19 = "\U0001f3f4\U000e0074\U000e0068\U000e0031\U000e0039\U000e007f" - FLAG_FOR_RIF_DIMASHQ_SY_RD = "\U0001f3f4\U000e0073\U000e0079\U000e0072\U000e0064\U000e007f" - FLAG_FOR_SAMUT_PRAKAN_TH_11 = "\U0001f3f4\U000e0074\U000e0068\U000e0031\U000e0031\U000e007f" - FLAG_FOR_SA_KAEO_TH_27 = "\U0001f3f4\U000e0074\U000e0068\U000e0032\U000e0037\U000e007f" - FLAG_FOR_GUERA_TD_GR = "\U0001f3f4\U000e0074\U000e0064\U000e0067\U000e0072\U000e007f" - FLAG_FOR_OUADDAI_TD_OD = "\U0001f3f4\U000e0074\U000e0064\U000e006f\U000e0064\U000e007f" - FLAG_FOR_SAVANES_TG_S = "\U0001f3f4\U000e0074\U000e0067\U000e0073\U000e007f" - FLAG_FOR_PHRAE_TH_54 = "\U0001f3f4\U000e0074\U000e0068\U000e0035\U000e0034\U000e007f" - FLAG_FOR_KHON_KAEN_TH_40 = "\U0001f3f4\U000e0074\U000e0068\U000e0034\U000e0030\U000e007f" - FLAG_FOR_UTHAI_THANI_TH_61 = "\U0001f3f4\U000e0074\U000e0068\U000e0036\U000e0031\U000e007f" - FLAG_FOR_MUKDAHAN_TH_49 = "\U0001f3f4\U000e0074\U000e0068\U000e0034\U000e0039\U000e007f" - FLAG_FOR_PHITSANULOK_TH_65 = "\U0001f3f4\U000e0074\U000e0068\U000e0036\U000e0035\U000e007f" - FLAG_FOR_LAMPANG_TH_52 = "\U0001f3f4\U000e0074\U000e0068\U000e0035\U000e0032\U000e007f" - FLAG_FOR_SUPHANBURI_TH_72 = "\U0001f3f4\U000e0074\U000e0068\U000e0037\U000e0032\U000e007f" - FLAG_FOR_RATCHABURI_TH_70 = "\U0001f3f4\U000e0074\U000e0068\U000e0037\U000e0030\U000e007f" - FLAG_FOR_UDON_THANI_TH_41 = "\U0001f3f4\U000e0074\U000e0068\U000e0034\U000e0031\U000e007f" - FLAG_FOR_LOEI_TH_42 = "\U0001f3f4\U000e0074\U000e0068\U000e0034\U000e0032\U000e007f" - FLAG_FOR_NONG_KHAI_TH_43 = "\U0001f3f4\U000e0074\U000e0068\U000e0034\U000e0033\U000e007f" - FLAG_FOR_NAKHON_SAWAN_TH_60 = "\U0001f3f4\U000e0074\U000e0068\U000e0036\U000e0030\U000e007f" - FLAG_FOR_PHETCHABUN_TH_67 = "\U0001f3f4\U000e0074\U000e0068\U000e0036\U000e0037\U000e007f" - FLAG_FOR_SAKON_NAKHON_TH_47 = "\U0001f3f4\U000e0074\U000e0068\U000e0034\U000e0037\U000e007f" - FLAG_FOR_KALASIN_TH_46 = "\U0001f3f4\U000e0074\U000e0068\U000e0034\U000e0036\U000e007f" - FLAG_FOR_KANCHANABURI_TH_71 = "\U0001f3f4\U000e0074\U000e0068\U000e0037\U000e0031\U000e007f" - FLAG_FOR_TOTONICAPAN_GT_TO = "\U0001f3f4\U000e0067\U000e0074\U000e0074\U000e006f\U000e007f" - FLAG_FOR_SAMUT_SAKHON_TH_74 = "\U0001f3f4\U000e0074\U000e0068\U000e0037\U000e0034\U000e007f" - FLAG_FOR_TIGRAY_ET_TI = "\U0001f3f4\U000e0065\U000e0074\U000e0074\U000e0069\U000e007f" - FLAG_FOR_NAKHON_PHANOM_TH_48 = "\U0001f3f4\U000e0074\U000e0068\U000e0034\U000e0038\U000e007f" - FLAG_FOR_NAKHON_PATHOM_TH_73 = "\U0001f3f4\U000e0074\U000e0068\U000e0037\U000e0033\U000e007f" - FLAG_FOR_UBON_RATCHATHANI_TH_34 = "\U0001f3f4\U000e0074\U000e0068\U000e0033\U000e0034\U000e007f" - FLAG_FOR_KAMPHAENG_PHET_TH_62 = "\U0001f3f4\U000e0074\U000e0068\U000e0036\U000e0032\U000e007f" - FLAG_FOR_YASOTHON_TH_35 = "\U0001f3f4\U000e0074\U000e0068\U000e0033\U000e0035\U000e007f" - FLAG_FOR_CHIANG_RAI_TH_57 = "\U0001f3f4\U000e0074\U000e0068\U000e0035\U000e0037\U000e007f" - FLAG_FOR_CHIANG_MAI_TH_50 = "\U0001f3f4\U000e0074\U000e0068\U000e0035\U000e0030\U000e007f" - FLAG_FOR_NONG_BUA_LAM_PHU_TH_39 = "\U0001f3f4\U000e0074\U000e0068\U000e0033\U000e0039\U000e007f" - FLAG_FOR_TAK_TH_63 = "\U0001f3f4\U000e0074\U000e0068\U000e0036\U000e0033\U000e007f" - FLAG_FOR_BUENG_KAN_TH_38 = "\U0001f3f4\U000e0074\U000e0068\U000e0033\U000e0038\U000e007f" - FLAG_FOR_LAMPHUN_TH_51 = "\U0001f3f4\U000e0074\U000e0068\U000e0035\U000e0031\U000e007f" - FLAG_FOR_PHAYAO_TH_56 = "\U0001f3f4\U000e0074\U000e0068\U000e0035\U000e0036\U000e007f" - FLAG_FOR_ROI_ET_TH_45 = "\U0001f3f4\U000e0074\U000e0068\U000e0034\U000e0035\U000e007f" - FLAG_FOR_PHICHIT_TH_66 = "\U0001f3f4\U000e0074\U000e0068\U000e0036\U000e0036\U000e007f" - FLAG_FOR_MAHA_SARAKHAM_TH_44 = "\U0001f3f4\U000e0074\U000e0068\U000e0034\U000e0034\U000e007f" - FLAG_FOR_SI_SA_KET_TH_33 = "\U0001f3f4\U000e0074\U000e0068\U000e0033\U000e0033\U000e007f" - FLAG_FOR_NAN_TH_55 = "\U0001f3f4\U000e0074\U000e0068\U000e0035\U000e0035\U000e007f" - FLAG_FOR_SUKHOTHAI_TH_64 = "\U0001f3f4\U000e0074\U000e0068\U000e0036\U000e0034\U000e007f" - FLAG_FOR_AILEU_TL_AL = "\U0001f3f4\U000e0074\U000e006c\U000e0061\U000e006c\U000e007f" - FLAG_FOR_MOQUEGUA_PE_MOQ = "\U0001f3f4\U000e0070\U000e0065\U000e006d\U000e006f\U000e0071\U000e007f" - FLAG_FOR_BAUCAU_TL_BA = "\U0001f3f4\U000e0074\U000e006c\U000e0062\U000e0061\U000e007f" - FLAG_FOR_AINARO_TL_AN = "\U0001f3f4\U000e0074\U000e006c\U000e0061\U000e006e\U000e007f" - FLAG_FOR_YAP_FM_YAP = "\U0001f3f4\U000e0066\U000e006d\U000e0079\U000e0061\U000e0070\U000e007f" - FLAG_FOR_SATUN_TH_91 = "\U0001f3f4\U000e0074\U000e0068\U000e0039\U000e0031\U000e007f" - FLAG_FOR_PHANG_NGA_TH_82 = "\U0001f3f4\U000e0074\U000e0068\U000e0038\U000e0032\U000e007f" - FLAG_FOR_TRANG_TH_92 = "\U0001f3f4\U000e0074\U000e0068\U000e0039\U000e0032\U000e007f" - FLAG_FOR_SONGKHLA_TH_90 = "\U0001f3f4\U000e0074\U000e0068\U000e0039\U000e0030\U000e007f" - FLAG_FOR_DASOGUZ_TM_D = "\U0001f3f4\U000e0074\U000e006d\U000e0064\U000e007f" - KISS_WOMAN_DARK_SKIN_TONE_WOMAN_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3ff" - FLAG_FOR_PATTAYA_TH_S = "\U0001f3f4\U000e0074\U000e0068\U000e0073\U000e007f" - FLAG_FOR_BLEKINGE_SE_K = "\U0001f3f4\U000e0073\U000e0065\U000e006b\U000e007f" - FLAG_FOR_MANATUTO_TL_MT = "\U0001f3f4\U000e0074\U000e006c\U000e006d\U000e0074\U000e007f" - FLAG_FOR_PATTANI_TH_94 = "\U0001f3f4\U000e0074\U000e0068\U000e0039\U000e0034\U000e007f" - FLAG_FOR_HIROSHIMA_JP_34 = "\U0001f3f4\U000e006a\U000e0070\U000e0033\U000e0034\U000e007f" - FLAG_FOR_JIWAKA_PG_JWK = "\U0001f3f4\U000e0070\U000e0067\U000e006a\U000e0077\U000e006b\U000e007f" - FLAG_FOR_LIQUICA_TL_LI = "\U0001f3f4\U000e0074\U000e006c\U000e006c\U000e0069\U000e007f" - FLAG_FOR_KHATLON_TJ_KT = "\U0001f3f4\U000e0074\U000e006a\U000e006b\U000e0074\U000e007f" - FLAG_FOR_SUGHD_TJ_SU = "\U0001f3f4\U000e0074\U000e006a\U000e0073\U000e0075\U000e007f" - FLAG_FOR_VIQUEQUE_TL_VI = "\U0001f3f4\U000e0074\U000e006c\U000e0076\U000e0069\U000e007f" - FLAG_FOR_BOBONARO_TL_BO = "\U0001f3f4\U000e0074\U000e006c\U000e0062\U000e006f\U000e007f" - FLAG_FOR_SURAT_THANI_TH_84 = "\U0001f3f4\U000e0074\U000e0068\U000e0038\U000e0034\U000e007f" - FLAG_FOR_RANONG_TH_85 = "\U0001f3f4\U000e0074\U000e0068\U000e0038\U000e0035\U000e007f" - FLAG_FOR_NORD_EST_HT_NE = "\U0001f3f4\U000e0068\U000e0074\U000e006e\U000e0065\U000e007f" - FLAG_FOR_PRACHUAP_KHIRI_KHAN_TH_77 = "\U0001f3f4\U000e0074\U000e0068\U000e0037\U000e0037\U000e007f" - FLAG_FOR_PHETCHABURI_TH_76 = "\U0001f3f4\U000e0074\U000e0068\U000e0037\U000e0036\U000e007f" - FLAG_FOR_NARATHIWAT_TH_96 = "\U0001f3f4\U000e0074\U000e0068\U000e0039\U000e0036\U000e007f" - FLAG_FOR_MAHDIA_TN_53 = "\U0001f3f4\U000e0074\U000e006e\U000e0035\U000e0033\U000e007f" - FLAG_FOR_TONGATAPU_TO_04 = "\U0001f3f4\U000e0074\U000e006f\U000e0030\U000e0034\U000e007f" - FLAG_FOR_VAVA_U_TO_05 = "\U0001f3f4\U000e0074\U000e006f\U000e0030\U000e0035\U000e007f" - FLAG_FOR_KAIROUAN_TN_41 = "\U0001f3f4\U000e0074\U000e006e\U000e0034\U000e0031\U000e007f" - FLAG_FOR_BOLU_TR_14 = "\U0001f3f4\U000e0074\U000e0072\U000e0031\U000e0034\U000e007f" - FLAG_FOR_BINGOL_TR_12 = "\U0001f3f4\U000e0074\U000e0072\U000e0031\U000e0032\U000e007f" - FLAG_FOR_AMASYA_TR_05 = "\U0001f3f4\U000e0074\U000e0072\U000e0030\U000e0035\U000e007f" - FLAG_FOR_BEJA_TN_31 = "\U0001f3f4\U000e0074\U000e006e\U000e0033\U000e0031\U000e007f" - FLAG_FOR_ZAGHOUAN_TN_22 = "\U0001f3f4\U000e0074\U000e006e\U000e0032\U000e0032\U000e007f" - FLAG_FOR_BILECIK_TR_11 = "\U0001f3f4\U000e0074\U000e0072\U000e0031\U000e0031\U000e007f" - FLAG_FOR_AGRI_TR_04 = "\U0001f3f4\U000e0074\U000e0072\U000e0030\U000e0034\U000e007f" - FLAG_FOR_KEF_TN_33 = "\U0001f3f4\U000e0074\U000e006e\U000e0033\U000e0033\U000e007f" - FLAG_FOR_MANOUBA_TN_14 = "\U0001f3f4\U000e0074\U000e006e\U000e0031\U000e0034\U000e007f" - FLAG_FOR_MEDENINE_TN_82 = "\U0001f3f4\U000e0074\U000e006e\U000e0038\U000e0032\U000e007f" - FLAG_FOR_BALIKESIR_TR_10 = "\U0001f3f4\U000e0074\U000e0072\U000e0031\U000e0030\U000e007f" - FLAG_FOR_EWA_NR_09 = "\U0001f3f4\U000e006e\U000e0072\U000e0030\U000e0039\U000e007f" - FLAG_FOR_TOZEUR_TN_72 = "\U0001f3f4\U000e0074\U000e006e\U000e0037\U000e0032\U000e007f" - FLAG_FOR_EUA_TO_01 = "\U0001f3f4\U000e0074\U000e006f\U000e0030\U000e0031\U000e007f" - FLAG_FOR_KASSERINE_TN_42 = "\U0001f3f4\U000e0074\U000e006e\U000e0034\U000e0032\U000e007f" - FLAG_FOR_GABES_TN_81 = "\U0001f3f4\U000e0074\U000e006e\U000e0038\U000e0031\U000e007f" - FLAG_FOR_KAUNAS_LT_16 = "\U0001f3f4\U000e006c\U000e0074\U000e0031\U000e0036\U000e007f" - FLAG_FOR_SILIANA_TN_34 = "\U0001f3f4\U000e0074\U000e006e\U000e0033\U000e0034\U000e007f" - FLAG_FOR_JENDOUBA_TN_32 = "\U0001f3f4\U000e0074\U000e006e\U000e0033\U000e0032\U000e007f" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f466\U0001f3fd" - FLAG_FOR_BIZERTE_TN_23 = "\U0001f3f4\U000e0074\U000e006e\U000e0032\U000e0033\U000e007f" - COUPLE_WITH_HEART_MAN_MEDIUM_SKIN_TONE_WOMAN = "\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f469" - FLAG_FOR_NIUAS_TO_03 = "\U0001f3f4\U000e0074\U000e006f\U000e0030\U000e0033\U000e007f" - FLAG_FOR_AYDIN_TR_09 = "\U0001f3f4\U000e0074\U000e0072\U000e0030\U000e0039\U000e007f" - FLAG_FOR_SIDI_BOUZID_TN_43 = "\U0001f3f4\U000e0074\U000e006e\U000e0034\U000e0033\U000e007f" - FLAG_FOR_JOHOR_MY_01 = "\U0001f3f4\U000e006d\U000e0079\U000e0030\U000e0031\U000e007f" - FLAG_FOR_KEBILI_TN_73 = "\U0001f3f4\U000e0074\U000e006e\U000e0037\U000e0033\U000e007f" - FLAG_FOR_TATAOUINE_TN_83 = "\U0001f3f4\U000e0074\U000e006e\U000e0038\U000e0033\U000e007f" - FLAG_FOR_MONASTIR_TN_52 = "\U0001f3f4\U000e0074\U000e006e\U000e0035\U000e0032\U000e007f" - FLAG_FOR_ARTVIN_TR_08 = "\U0001f3f4\U000e0074\U000e0072\U000e0030\U000e0038\U000e007f" - FLAG_FOR_GRAUBUNDEN_CH_GR = "\U0001f3f4\U000e0063\U000e0068\U000e0067\U000e0072\U000e007f" - FLAG_FOR_MALATYA_TR_44 = "\U0001f3f4\U000e0074\U000e0072\U000e0034\U000e0034\U000e007f" - FLAG_FOR_DENIZLI_TR_20 = "\U0001f3f4\U000e0074\U000e0072\U000e0032\U000e0030\U000e007f" - FLAG_FOR_RIZE_TR_53 = "\U0001f3f4\U000e0074\U000e0072\U000e0035\U000e0033\U000e007f" - FLAG_FOR_DIYARBAKIR_TR_21 = "\U0001f3f4\U000e0074\U000e0072\U000e0032\U000e0031\U000e007f" - FLAG_FOR_EDIRNE_TR_22 = "\U0001f3f4\U000e0074\U000e0072\U000e0032\U000e0032\U000e007f" - FLAG_FOR_BURSA_TR_16 = "\U0001f3f4\U000e0074\U000e0072\U000e0031\U000e0036\U000e007f" - FLAG_FOR_SIIRT_TR_56 = "\U0001f3f4\U000e0074\U000e0072\U000e0035\U000e0036\U000e007f" - FLAG_FOR_BOKE_REGION_GN_B = "\U0001f3f4\U000e0067\U000e006e\U000e0062\U000e007f" - FLAG_FOR_KAHRAMANMARAS_TR_46 = "\U0001f3f4\U000e0074\U000e0072\U000e0034\U000e0036\U000e007f" - FLAG_FOR_MARDIN_TR_47 = "\U0001f3f4\U000e0074\U000e0072\U000e0034\U000e0037\U000e007f" - FLAG_FOR_ERZURUM_TR_25 = "\U0001f3f4\U000e0074\U000e0072\U000e0032\U000e0035\U000e007f" - FLAG_FOR_ORO_PG_NPP = "\U0001f3f4\U000e0070\U000e0067\U000e006e\U000e0070\U000e0070\U000e007f" - FLAG_FOR_NIGDE_TR_51 = "\U0001f3f4\U000e0074\U000e0072\U000e0035\U000e0031\U000e007f" - FLAG_FOR_ORDU_TR_52 = "\U0001f3f4\U000e0074\U000e0072\U000e0035\U000e0032\U000e007f" - FLAG_FOR_NGOBE_BUGLE_PA_NB = "\U0001f3f4\U000e0070\U000e0061\U000e006e\U000e0062\U000e007f" - FLAG_FOR_HAKKARI_TR_30 = "\U0001f3f4\U000e0074\U000e0072\U000e0033\U000e0030\U000e007f" - FLAG_FOR_SAMSUN_TR_55 = "\U0001f3f4\U000e0074\U000e0072\U000e0035\U000e0035\U000e007f" - FLAG_FOR_HATAY_TR_31 = "\U0001f3f4\U000e0074\U000e0072\U000e0033\U000e0031\U000e007f" - FLAG_FOR_SAKARYA_TR_54 = "\U0001f3f4\U000e0074\U000e0072\U000e0035\U000e0034\U000e007f" - FLAG_FOR_KIRKLARELI_TR_39 = "\U0001f3f4\U000e0074\U000e0072\U000e0033\U000e0039\U000e007f" - FLAG_FOR_VICTORIA_MT_45 = "\U0001f3f4\U000e006d\U000e0074\U000e0034\U000e0035\U000e007f" - FLAG_FOR_KIRSEHIR_TR_40 = "\U0001f3f4\U000e0074\U000e0072\U000e0034\U000e0030\U000e007f" - FLAG_FOR_ISPARTA_TR_32 = "\U0001f3f4\U000e0074\U000e0072\U000e0033\U000e0032\U000e007f" - FLAG_FOR_ESKISEHIR_TR_26 = "\U0001f3f4\U000e0074\U000e0072\U000e0032\U000e0036\U000e007f" - FLAG_FOR_BURDUR_TR_15 = "\U0001f3f4\U000e0074\U000e0072\U000e0031\U000e0035\U000e007f" - FLAG_FOR_MANISA_TR_45 = "\U0001f3f4\U000e0074\U000e0072\U000e0034\U000e0035\U000e007f" - FLAG_FOR_PRAHA_HLAVNI_MESTO_CZ_10 = "\U0001f3f4\U000e0063\U000e007a\U000e0031\U000e0030\U000e007f" - FLAG_FOR_ELAZIG_TR_23 = "\U0001f3f4\U000e0074\U000e0072\U000e0032\U000e0033\U000e007f" - FLAG_FOR_CANAKKALE_TR_17 = "\U0001f3f4\U000e0074\U000e0072\U000e0031\U000e0037\U000e007f" - FLAG_FOR_MAYARO_RIO_CLARO_TT_MRC = "\U0001f3f4\U000e0074\U000e0074\U000e006d\U000e0072\U000e0063\U000e007f" - FLAG_FOR_TOKAT_TR_60 = "\U0001f3f4\U000e0074\U000e0072\U000e0036\U000e0030\U000e007f" - FLAG_FOR_TOBAGO_TT_TOB = "\U0001f3f4\U000e0074\U000e0074\U000e0074\U000e006f\U000e0062\U000e007f" - FLAG_FOR_DIEGO_MARTIN_TT_DMN = "\U0001f3f4\U000e0074\U000e0074\U000e0064\U000e006d\U000e006e\U000e007f" - FLAG_FOR_SAN_FERNANDO_TT_SFO = "\U0001f3f4\U000e0074\U000e0074\U000e0073\U000e0066\U000e006f\U000e007f" - FLAG_FOR_KILIS_TR_79 = "\U0001f3f4\U000e0074\U000e0072\U000e0037\U000e0039\U000e007f" - KISS_WOMAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fc" - FLAG_FOR_PENZA_RU_PNZ = "\U0001f3f4\U000e0072\U000e0075\U000e0070\U000e006e\U000e007a\U000e007f" - FLAG_FOR_SANLIURFA_TR_63 = "\U0001f3f4\U000e0074\U000e0072\U000e0036\U000e0033\U000e007f" - FLAG_FOR_ERZINCAN_TR_24 = "\U0001f3f4\U000e0074\U000e0072\U000e0032\U000e0034\U000e007f" - FLAG_FOR_TEKIRDAG_TR_59 = "\U0001f3f4\U000e0074\U000e0072\U000e0035\U000e0039\U000e007f" - FLAG_FOR_KIRIKKALE_TR_71 = "\U0001f3f4\U000e0074\U000e0072\U000e0037\U000e0031\U000e007f" - FLAG_FOR_IGDIR_TR_76 = "\U0001f3f4\U000e0074\U000e0072\U000e0037\U000e0036\U000e007f" - FLAG_FOR_COUVA_TABAQUITE_TALPARO_TT_CTT = "\U0001f3f4\U000e0074\U000e0074\U000e0063\U000e0074\U000e0074\U000e007f" - FLAG_FOR_KARAMAN_TR_70 = "\U0001f3f4\U000e0074\U000e0072\U000e0037\U000e0030\U000e007f" - FLAG_FOR_ZONGULDAK_TR_67 = "\U0001f3f4\U000e0074\U000e0072\U000e0036\U000e0037\U000e007f" - FLAG_FOR_TUNCELI_TR_62 = "\U0001f3f4\U000e0074\U000e0072\U000e0036\U000e0032\U000e007f" - FLAG_FOR_POINT_FORTIN_TT_PTF = "\U0001f3f4\U000e0074\U000e0074\U000e0070\U000e0074\U000e0066\U000e007f" - FLAG_FOR_PENAL_DEBE_TT_PED = "\U0001f3f4\U000e0074\U000e0074\U000e0070\U000e0065\U000e0064\U000e007f" - FLAG_FOR_SAN_JUAN_LAVENTILLE_TT_SJL = "\U0001f3f4\U000e0074\U000e0074\U000e0073\U000e006a\U000e006c\U000e007f" - FLAG_FOR_PRINCES_TOWN_TT_PRT = "\U0001f3f4\U000e0074\U000e0074\U000e0070\U000e0072\U000e0074\U000e007f" - FLAG_FOR_YALOVA_TR_77 = "\U0001f3f4\U000e0074\U000e0072\U000e0037\U000e0037\U000e007f" - FLAG_FOR_BAYBURT_TR_69 = "\U0001f3f4\U000e0074\U000e0072\U000e0036\U000e0039\U000e007f" - FLAG_FOR_BAIE_LAZARE_SC_06 = "\U0001f3f4\U000e0073\U000e0063\U000e0030\U000e0036\U000e007f" - FLAG_FOR_CHAGUANAS_TT_CHA = "\U0001f3f4\U000e0074\U000e0074\U000e0063\U000e0068\U000e0061\U000e007f" - FLAG_FOR_BARTIN_TR_74 = "\U0001f3f4\U000e0074\U000e0072\U000e0037\U000e0034\U000e007f" - FLAG_FOR_SIPARIA_TT_SIP = "\U0001f3f4\U000e0074\U000e0074\U000e0073\U000e0069\U000e0070\U000e007f" - FLAG_FOR_SINOP_TR_57 = "\U0001f3f4\U000e0074\U000e0072\U000e0035\U000e0037\U000e007f" - FLAG_FOR_GAZIANTEP_TR_27 = "\U0001f3f4\U000e0074\U000e0072\U000e0032\U000e0037\U000e007f" - FLAG_FOR_KARABUK_TR_78 = "\U0001f3f4\U000e0074\U000e0072\U000e0037\U000e0038\U000e007f" - FLAG_FOR_SANTA_BARBARA_HN_SB = "\U0001f3f4\U000e0068\U000e006e\U000e0073\U000e0062\U000e007f" - FLAG_FOR_CHIAYI_COUNTY_TW_CYI = "\U0001f3f4\U000e0074\U000e0077\U000e0063\U000e0079\U000e0069\U000e007f" - FLAG_FOR_KAGERA_TZ_05 = "\U0001f3f4\U000e0074\U000e007a\U000e0030\U000e0035\U000e007f" - FLAG_FOR_NANUMEA_TV_NMA = "\U0001f3f4\U000e0074\U000e0076\U000e006e\U000e006d\U000e0061\U000e007f" - FLAG_FOR_TAINAN_TW_TNN = "\U0001f3f4\U000e0074\U000e0077\U000e0074\U000e006e\U000e006e\U000e007f" - FLAG_FOR_DODOMA_TZ_03 = "\U0001f3f4\U000e0074\U000e007a\U000e0030\U000e0033\U000e007f" - FLAG_FOR_CHIAYI_TW_CYQ = "\U0001f3f4\U000e0074\U000e0077\U000e0063\U000e0079\U000e0071\U000e007f" - FLAG_FOR_YUNLIN_TW_YUN = "\U0001f3f4\U000e0074\U000e0077\U000e0079\U000e0075\U000e006e\U000e007f" - FLAG_FOR_NEW_TAIPEI_TW_NWT = "\U0001f3f4\U000e0074\U000e0077\U000e006e\U000e0077\U000e0074\U000e007f" - FLAG_FOR_KINMEN_TW_KIN = "\U0001f3f4\U000e0074\U000e0077\U000e006b\U000e0069\U000e006e\U000e007f" - FLAG_FOR_HUALIEN_TW_HUA = "\U0001f3f4\U000e0074\U000e0077\U000e0068\U000e0075\U000e0061\U000e007f" - FLAG_FOR_TAICHUNG_TW_TXG = "\U0001f3f4\U000e0074\U000e0077\U000e0074\U000e0078\U000e0067\U000e007f" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f469\U0001f3fe\u200d\U0001f466\U0001f3fe\u200d\U0001f476\U0001f3fe" - FLAG_FOR_PINGTUNG_TW_PIF = "\U0001f3f4\U000e0074\U000e0077\U000e0070\U000e0069\U000e0066\U000e007f" - FLAG_FOR_IRINGA_TZ_04 = "\U0001f3f4\U000e0074\U000e007a\U000e0030\U000e0034\U000e007f" - FLAG_FOR_KEELUNG_TW_KEE = "\U0001f3f4\U000e0074\U000e0077\U000e006b\U000e0065\U000e0065\U000e007f" - KISS_MAN_MEDIUM_DARK_SKIN_TONE_MAN = "\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468" - FLAG_FOR_AGLONA_LV_001 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0030\U000e0031\U000e007f" - FLAG_FOR_CANKOVA_SI_152 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0035\U000e0032\U000e007f" - FLAG_FOR_ARUSHA_TZ_01 = "\U0001f3f4\U000e0074\U000e007a\U000e0030\U000e0031\U000e007f" - FLAG_FOR_NUI_TV_NUI = "\U0001f3f4\U000e0074\U000e0076\U000e006e\U000e0075\U000e0069\U000e007f" - FLAG_FOR_YILAN_TW_ILA = "\U0001f3f4\U000e0074\U000e0077\U000e0069\U000e006c\U000e0061\U000e007f" - FLAG_FOR_VERACRUZ_MX_VER = "\U0001f3f4\U000e006d\U000e0078\U000e0076\U000e0065\U000e0072\U000e007f" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f469\U0001f3fe\u200d\U0001f467\U0001f3fe" - FLAG_FOR_LIENCHIANG_TW_LIE = "\U0001f3f4\U000e0074\U000e0077\U000e006c\U000e0069\U000e0065\U000e007f" - FLAG_FOR_NUKUFETAU_TV_NKF = "\U0001f3f4\U000e0074\U000e0076\U000e006e\U000e006b\U000e0066\U000e007f" - FLAG_FOR_VAITUPU_TV_VAI = "\U0001f3f4\U000e0074\U000e0076\U000e0076\U000e0061\U000e0069\U000e007f" - FLAG_FOR_VOLYN_UA_07 = "\U0001f3f4\U000e0075\U000e0061\U000e0030\U000e0037\U000e007f" - FLAG_FOR_MTWARA_TZ_17 = "\U0001f3f4\U000e0074\U000e007a\U000e0031\U000e0037\U000e007f" - FLAG_FOR_TAMBOV_RU_TAM = "\U0001f3f4\U000e0072\U000e0075\U000e0074\U000e0061\U000e006d\U000e007f" - FLAG_FOR_KATAVI_TZ_28 = "\U0001f3f4\U000e0074\U000e007a\U000e0032\U000e0038\U000e007f" - FLAG_FOR_SOUTH_PEMBA_TZ_10 = "\U0001f3f4\U000e0074\U000e007a\U000e0031\U000e0030\U000e007f" - FLAG_FOR_RUKWA_TZ_20 = "\U0001f3f4\U000e0074\U000e007a\U000e0032\U000e0030\U000e007f" - FLAG_FOR_PORT_LOUIS_DISTRICT_MU_PL = "\U0001f3f4\U000e006d\U000e0075\U000e0070\U000e006c\U000e007f" - FLAG_FOR_MANYARA_TZ_26 = "\U0001f3f4\U000e0074\U000e007a\U000e0032\U000e0036\U000e007f" - FLAG_FOR_SEVASTOPOL_UA_40 = "\U0001f3f4\U000e0075\U000e0061\U000e0034\U000e0030\U000e007f" - FLAG_FOR_NJOMBE_TZ_29 = "\U0001f3f4\U000e0074\U000e007a\U000e0032\U000e0039\U000e007f" - FLAG_FOR_ZAPORIZHZHYA_UA_23 = "\U0001f3f4\U000e0075\U000e0061\U000e0032\U000e0033\U000e007f" - FLAG_FOR_MBEYA_TZ_14 = "\U0001f3f4\U000e0074\U000e007a\U000e0031\U000e0034\U000e007f" - FLAG_FOR_KYIVSHCHYNA_UA_32 = "\U0001f3f4\U000e0075\U000e0061\U000e0033\U000e0032\U000e007f" - FLAG_FOR_RUVUMA_TZ_21 = "\U0001f3f4\U000e0074\U000e007a\U000e0032\U000e0031\U000e007f" - FLAG_FOR_SHINYANGA_TZ_22 = "\U0001f3f4\U000e0074\U000e007a\U000e0032\U000e0032\U000e007f" - FLAG_FOR_ZAKARPATTIA_UA_21 = "\U0001f3f4\U000e0075\U000e0061\U000e0032\U000e0031\U000e007f" - FLAG_FOR_NORTE_GW_N = "\U0001f3f4\U000e0067\U000e0077\U000e006e\U000e007f" - FLAG_FOR_RIVNENSHCHYNA_UA_56 = "\U0001f3f4\U000e0075\U000e0061\U000e0035\U000e0036\U000e007f" - FLAG_FOR_SAINT_GEORGE_DM_04 = "\U0001f3f4\U000e0064\U000e006d\U000e0030\U000e0034\U000e007f" - FLAG_FOR_ODESHCHYNA_UA_51 = "\U0001f3f4\U000e0075\U000e0061\U000e0035\U000e0031\U000e007f" - FLAG_FOR_SIMIYU_TZ_30 = "\U0001f3f4\U000e0074\U000e007a\U000e0033\U000e0030\U000e007f" - FLAG_FOR_GEITA_TZ_27 = "\U0001f3f4\U000e0074\U000e007a\U000e0032\U000e0037\U000e007f" - FLAG_FOR_POLTAVSHCHYNA_UA_53 = "\U0001f3f4\U000e0075\U000e0061\U000e0035\U000e0033\U000e007f" - FLAG_FOR_TABORA_TZ_24 = "\U0001f3f4\U000e0074\U000e007a\U000e0032\U000e0034\U000e007f" - FLAG_FOR_SINGIDA_TZ_23 = "\U0001f3f4\U000e0074\U000e007a\U000e0032\U000e0033\U000e007f" - FLAG_FOR_MWANZA_TZ_18 = "\U0001f3f4\U000e0074\U000e007a\U000e0031\U000e0038\U000e007f" - FLAG_FOR_SELENGE_MN_049 = "\U0001f3f4\U000e006d\U000e006e\U000e0030\U000e0034\U000e0039\U000e007f" - FLAG_FOR_VINNYCHCHYNA_UA_05 = "\U0001f3f4\U000e0075\U000e0061\U000e0030\U000e0035\U000e007f" - FLAG_FOR_FLORIDA_US_FL = "\U0001f3f4\U000e0075\U000e0073\U000e0066\U000e006c\U000e007f" - FLAG_FOR_LOFA_LR_LO = "\U0001f3f4\U000e006c\U000e0072\U000e006c\U000e006f\U000e007f" - RECYCLING_SYMBOL_FOR_TYPE_7_PLASTICS = "\u2679" - FLAG_FOR_BAKER_ISLAND_UM_81 = "\U0001f3f4\U000e0075\U000e006d\U000e0038\U000e0031\U000e007f" - FLAG_FOR_NORTHERN_UG_N = "\U0001f3f4\U000e0075\U000e0067\U000e006e\U000e007f" - FLAG_FOR_INGUSHETIA_RU_IN = "\U0001f3f4\U000e0072\U000e0075\U000e0069\U000e006e\U000e007f" - FLAG_FOR_KHERSONSHCHYNA_UA_65 = "\U0001f3f4\U000e0075\U000e0061\U000e0036\U000e0035\U000e007f" - FLAG_FOR_WAKE_ISLAND_UM_79 = "\U0001f3f4\U000e0075\U000e006d\U000e0037\U000e0039\U000e007f" - FLAG_FOR_PALMYRA_ATOLL_UM_95 = "\U0001f3f4\U000e0075\U000e006d\U000e0039\U000e0035\U000e007f" - FLAG_FOR_KHARKIVSHCHYNA_UA_63 = "\U0001f3f4\U000e0075\U000e0061\U000e0036\U000e0033\U000e007f" - FLAG_FOR_KHMELNYCHCHYNA_UA_68 = "\U0001f3f4\U000e0075\U000e0061\U000e0036\U000e0038\U000e007f" - FLAG_FOR_WESTERN_UG_W = "\U0001f3f4\U000e0075\U000e0067\U000e0077\U000e007f" - FLAG_FOR_EASTERN_UG_E = "\U0001f3f4\U000e0075\U000e0067\U000e0065\U000e007f" - FLAG_FOR_IDLIB_SY_ID = "\U0001f3f4\U000e0073\U000e0079\U000e0069\U000e0064\U000e007f" - FLAG_FOR_TERNOPILSHCHYNA_UA_61 = "\U0001f3f4\U000e0075\U000e0061\U000e0036\U000e0031\U000e007f" - FLAG_FOR_CHERNIVTSI_OBLAST_UA_77 = "\U0001f3f4\U000e0075\U000e0061\U000e0037\U000e0037\U000e007f" - FLAG_FOR_PARA_SR_PR = "\U0001f3f4\U000e0073\U000e0072\U000e0070\U000e0072\U000e007f" - FLAG_FOR_SUMSHCHYNA_UA_59 = "\U0001f3f4\U000e0075\U000e0061\U000e0035\U000e0039\U000e007f" - FAMILY_WOMAN_WOMAN_BABY_BOY = "\U0001f469\u200d\U0001f469\u200d\U0001f476\u200d\U0001f466" - FLAG_FOR_JERICHO_PS_JRH = "\U0001f3f4\U000e0070\U000e0073\U000e006a\U000e0072\U000e0068\U000e007f" - FLAG_FOR_MIDWAY_ATOLL_UM_71 = "\U0001f3f4\U000e0075\U000e006d\U000e0037\U000e0031\U000e007f" - FLAG_FOR_JOHNSTON_ATOLL_UM_67 = "\U0001f3f4\U000e0075\U000e006d\U000e0036\U000e0037\U000e007f" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f466\U0001f3fc\u200d\U0001f476\U0001f3fc" - FLAG_FOR_CANELONES_UY_CA = "\U0001f3f4\U000e0075\U000e0079\U000e0063\U000e0061\U000e007f" - FLAG_FOR_KOSTROMA_RU_KOS = "\U0001f3f4\U000e0072\U000e0075\U000e006b\U000e006f\U000e0073\U000e007f" - FLAG_FOR_JARVIS_ISLAND_UM_86 = "\U0001f3f4\U000e0075\U000e006d\U000e0038\U000e0036\U000e007f" - FLAG_FOR_PENNSYLVANIA_US_PA = "\U0001f3f4\U000e0075\U000e0073\U000e0070\U000e0061\U000e007f" - FLAG_FOR_PUEBLA_MX_PUE = "\U0001f3f4\U000e006d\U000e0078\U000e0070\U000e0075\U000e0065\U000e007f" - FLAG_FOR_SVETA_TROJICA_V_SLOVENSKIH_GORICAH_SI_204 = "\U0001f3f4\U000e0073\U000e0069\U000e0032\U000e0030\U000e0034\U000e007f" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f468\U0001f3fc\u200d\U0001f466\U0001f3fc\u200d\U0001f466\U0001f3fc" - FLAG_FOR_CHERKASHCHYNA_UA_71 = "\U0001f3f4\U000e0075\U000e0061\U000e0037\U000e0031\U000e007f" - FLAG_FOR_ALEXANDRIA_EG_ALX = "\U0001f3f4\U000e0065\U000e0067\U000e0061\U000e006c\U000e0078\U000e007f" - FLAG_FOR_CENTRAL_MACEDONIA_GR_B = "\U0001f3f4\U000e0067\U000e0072\U000e0062\U000e007f" - FLAG_FOR_ARTIGAS_UY_AR = "\U0001f3f4\U000e0075\U000e0079\U000e0061\U000e0072\U000e007f" - FLAG_FOR_OSLO_NO_03 = "\U0001f3f4\U000e006e\U000e006f\U000e0030\U000e0033\U000e007f" - FLAG_FOR_LOGONE_ORIENTAL_TD_LR = "\U0001f3f4\U000e0074\U000e0064\U000e006c\U000e0072\U000e007f" - FLAG_FOR_SINT_EUSTATIUS_NL_BQ3 = "\U0001f3f4\U000e006e\U000e006c\U000e0062\U000e0071\U000e0033\U000e007f" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f468\U0001f3fe\u200d\U0001f466\U0001f3fe\u200d\U0001f476\U0001f3fe" - FLAG_FOR_KINGMAN_REEF_UM_89 = "\U0001f3f4\U000e0075\U000e006d\U000e0038\U000e0039\U000e007f" - FLAG_FOR_DNIPROPETROVSHCHYNA_UA_12 = "\U0001f3f4\U000e0075\U000e0061\U000e0031\U000e0032\U000e007f" - FLAG_FOR_VRANCEA_RO_VN = "\U0001f3f4\U000e0072\U000e006f\U000e0076\U000e006e\U000e007f" - FLAG_FOR_NORTH_PEMBA_TZ_06 = "\U0001f3f4\U000e0074\U000e007a\U000e0030\U000e0036\U000e007f" - FLAG_FOR_MONTEVIDEO_UY_MO = "\U0001f3f4\U000e0075\U000e0079\U000e006d\U000e006f\U000e007f" - FLAG_FOR_COLONIA_UY_CO = "\U0001f3f4\U000e0075\U000e0079\U000e0063\U000e006f\U000e007f" - FLAG_FOR_JIZZAKH_UZ_JI = "\U0001f3f4\U000e0075\U000e007a\U000e006a\U000e0069\U000e007f" - FLAG_FOR_OKLAHOMA_US_OK = "\U0001f3f4\U000e0075\U000e0073\U000e006f\U000e006b\U000e007f" - FLAG_FOR_TREINTA_Y_TRES_UY_TT = "\U0001f3f4\U000e0075\U000e0079\U000e0074\U000e0074\U000e007f" - FLAG_FOR_SOUTH_CAROLINA_US_SC = "\U0001f3f4\U000e0075\U000e0073\U000e0073\U000e0063\U000e007f" - FLAG_FOR_PAYSANDU_UY_PA = "\U0001f3f4\U000e0075\U000e0079\U000e0070\U000e0061\U000e007f" - FLAG_FOR_TASHKENT_PROVINCE_UZ_TO = "\U0001f3f4\U000e0075\U000e007a\U000e0074\U000e006f\U000e007f" - FLAG_FOR_NAMANGAN_UZ_NG = "\U0001f3f4\U000e0075\U000e007a\U000e006e\U000e0067\U000e007f" - FLAG_FOR_DURAZNO_UY_DU = "\U0001f3f4\U000e0075\U000e0079\U000e0064\U000e0075\U000e007f" - FLAG_FOR_ARAGUA_VE_D = "\U0001f3f4\U000e0076\U000e0065\U000e0064\U000e007f" - FLAG_FOR_KARAKALPAKSTAN_UZ_QR = "\U0001f3f4\U000e0075\U000e007a\U000e0071\U000e0072\U000e007f" - FLAG_FOR_CERRO_LARGO_UY_CL = "\U0001f3f4\U000e0075\U000e0079\U000e0063\U000e006c\U000e007f" - FLAG_FOR_KELANTAN_MY_03 = "\U0001f3f4\U000e006d\U000e0079\U000e0030\U000e0033\U000e007f" - FLAG_FOR_FLORES_UY_FS = "\U0001f3f4\U000e0075\U000e0079\U000e0066\U000e0073\U000e007f" - FLAG_FOR_FERGANA_UZ_FA = "\U0001f3f4\U000e0075\U000e007a\U000e0066\U000e0061\U000e007f" - FLAG_FOR_MARYLAND_US_MD = "\U0001f3f4\U000e0075\U000e0073\U000e006d\U000e0064\U000e007f" - FLAG_FOR_ANZOATEGUI_VE_B = "\U0001f3f4\U000e0076\U000e0065\U000e0062\U000e007f" - FLAG_FOR_SAINT_ANDREW_VC_02 = "\U0001f3f4\U000e0076\U000e0063\U000e0030\U000e0032\U000e007f" - FLAG_FOR_BUKHARA_UZ_BU = "\U0001f3f4\U000e0075\U000e007a\U000e0062\U000e0075\U000e007f" - FLAG_FOR_MELEKEOK_PW_212 = "\U0001f3f4\U000e0070\U000e0077\U000e0032\U000e0031\U000e0032\U000e007f" - FLAG_FOR_TELENESTI_MD_TE = "\U0001f3f4\U000e006d\U000e0064\U000e0074\U000e0065\U000e007f" - FLAG_FOR_CHARLOTTE_VC_01 = "\U0001f3f4\U000e0076\U000e0063\U000e0030\U000e0031\U000e007f" - FLAG_FOR_ANDIJAN_UZ_AN = "\U0001f3f4\U000e0075\U000e007a\U000e0061\U000e006e\U000e007f" - FLAG_FOR_SALTO_UY_SA = "\U0001f3f4\U000e0075\U000e0079\U000e0073\U000e0061\U000e007f" - FLAG_FOR_SORIANO_UY_SO = "\U0001f3f4\U000e0075\U000e0079\U000e0073\U000e006f\U000e007f" - FLAG_FOR_SIRDARYO_UZ_SI = "\U0001f3f4\U000e0075\U000e007a\U000e0073\U000e0069\U000e007f" - FLAG_FOR_TASHKENT_UZ_TK = "\U0001f3f4\U000e0075\U000e007a\U000e0074\U000e006b\U000e007f" - FLAG_FOR_CAPITAL_VE_A = "\U0001f3f4\U000e0076\U000e0065\U000e0061\U000e007f" - FLAG_FOR_SAINT_DAVID_VC_03 = "\U0001f3f4\U000e0076\U000e0063\U000e0030\U000e0033\U000e007f" - FLAG_FOR_MALDONADO_UY_MA = "\U0001f3f4\U000e0075\U000e0079\U000e006d\U000e0061\U000e007f" - FLAG_FOR_RIO_NEGRO_UY_RN = "\U0001f3f4\U000e0075\U000e0079\U000e0072\U000e006e\U000e007f" - FLAG_FOR_LAVALLEJA_UY_LA = "\U0001f3f4\U000e0075\U000e0079\U000e006c\U000e0061\U000e007f" - FLAG_FOR_NAVOIY_UZ_NW = "\U0001f3f4\U000e0075\U000e007a\U000e006e\U000e0077\U000e007f" - FLAG_FOR_SAMARQAND_UZ_SA = "\U0001f3f4\U000e0075\U000e007a\U000e0073\U000e0061\U000e007f" - FLAG_FOR_SURXONDARYO_UZ_SU = "\U0001f3f4\U000e0075\U000e007a\U000e0073\U000e0075\U000e007f" - FLAG_FOR_QASHQADARYO_UZ_QA = "\U0001f3f4\U000e0075\U000e007a\U000e0071\U000e0061\U000e007f" - FLAG_FOR_TACUAREMBO_UY_TA = "\U0001f3f4\U000e0075\U000e0079\U000e0074\U000e0061\U000e007f" - FLAG_FOR_SON_LA_VN_05 = "\U0001f3f4\U000e0076\U000e006e\U000e0030\U000e0035\U000e007f" - FLAG_FOR_LANG_SON_VN_09 = "\U0001f3f4\U000e0076\U000e006e\U000e0030\U000e0039\U000e007f" - FLAG_FOR_HA_GIANG_VN_03 = "\U0001f3f4\U000e0076\U000e006e\U000e0030\U000e0033\U000e007f" - FLAG_FOR_MIRANDA_VE_M = "\U0001f3f4\U000e0076\U000e0065\U000e006d\U000e007f" - FLAG_FOR_TUYEN_QUANG_VN_07 = "\U0001f3f4\U000e0076\U000e006e\U000e0030\U000e0037\U000e007f" - FLAG_FOR_THANH_HOA_VN_21 = "\U0001f3f4\U000e0076\U000e006e\U000e0032\U000e0031\U000e007f" - FLAG_FOR_NGHE_AN_VN_22 = "\U0001f3f4\U000e0076\U000e006e\U000e0032\U000e0032\U000e007f" - FLAG_FOR_COJEDES_VE_H = "\U0001f3f4\U000e0076\U000e0065\U000e0068\U000e007f" - FLAG_FOR_CAO_BANG_VN_04 = "\U0001f3f4\U000e0076\U000e006e\U000e0030\U000e0034\U000e007f" - FLAG_FOR_QUANG_NGAI_VN_29 = "\U0001f3f4\U000e0076\U000e006e\U000e0032\U000e0039\U000e007f" - FLAG_FOR_SANTANDER_CO_SAN = "\U0001f3f4\U000e0063\U000e006f\U000e0073\U000e0061\U000e006e\U000e007f" - FLAG_FOR_NINH_BINH_VN_18 = "\U0001f3f4\U000e0076\U000e006e\U000e0031\U000e0038\U000e007f" - FLAG_FOR_YEN_BAI_VN_06 = "\U0001f3f4\U000e0076\U000e006e\U000e0030\U000e0036\U000e007f" - FLAG_FOR_THAI_BINH_VN_20 = "\U0001f3f4\U000e0076\U000e006e\U000e0032\U000e0030\U000e007f" - FLAG_FOR_BARINAS_VE_E = "\U0001f3f4\U000e0076\U000e0065\U000e0065\U000e007f" - FLAG_FOR_HA_TINH_VN_23 = "\U0001f3f4\U000e0076\U000e006e\U000e0032\U000e0033\U000e007f" - FLAG_FOR_LOWER_RIVER_DIVISION_GM_L = "\U0001f3f4\U000e0067\U000e006d\U000e006c\U000e007f" - FLAG_FOR_IG_SI_037 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0033\U000e0037\U000e007f" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f468\U0001f3fc\u200d\U0001f467\U0001f3fc\u200d\U0001f467\U0001f3fc" - FLAG_FOR_LARA_VE_K = "\U0001f3f4\U000e0076\U000e0065\U000e006b\U000e007f" - FLAG_FOR_QUANG_NINH_VN_13 = "\U0001f3f4\U000e0076\U000e006e\U000e0031\U000e0033\U000e007f" - FLAG_FOR_AU_CAP_SC_04 = "\U0001f3f4\U000e0073\U000e0063\U000e0030\U000e0034\U000e007f" - FLAG_FOR_HOA_BINH_VN_14 = "\U0001f3f4\U000e0076\U000e006e\U000e0031\U000e0034\U000e007f" - FLAG_FOR_KON_TUM_VN_28 = "\U0001f3f4\U000e0076\U000e006e\U000e0032\U000e0038\U000e007f" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f466\U0001f3fc\u200d\U0001f466\U0001f3fc" - FLAG_FOR_ZANZIBAR_URBAN_WEST_TZ_15 = "\U0001f3f4\U000e0074\U000e007a\U000e0031\U000e0035\U000e007f" - FLAG_FOR_OECUSSE_TL_OE = "\U0001f3f4\U000e0074\U000e006c\U000e006f\U000e0065\U000e007f" - FLAG_FOR_YARACUY_VE_U = "\U0001f3f4\U000e0076\U000e0065\U000e0075\U000e007f" - FLAG_FOR_BINH_PHUOC_VN_58 = "\U0001f3f4\U000e0076\U000e006e\U000e0035\U000e0038\U000e007f" - FLAG_FOR_NINH_THUAN_VN_36 = "\U0001f3f4\U000e0076\U000e006e\U000e0033\U000e0036\U000e007f" - FLAG_FOR_OSHIKOTO_NA_OT = "\U0001f3f4\U000e006e\U000e0061\U000e006f\U000e0074\U000e007f" - FLAG_FOR_CARGADOS_CARAJOS_MU_CC = "\U0001f3f4\U000e006d\U000e0075\U000e0063\U000e0063\U000e007f" - FLAG_FOR_PHU_YEN_VN_32 = "\U0001f3f4\U000e0076\U000e006e\U000e0033\U000e0032\U000e007f" - FLAG_FOR_NAM_INH_VN_67 = "\U0001f3f4\U000e0076\U000e006e\U000e0036\U000e0037\U000e007f" - FLAG_FOR_BAC_GIANG_VN_54 = "\U0001f3f4\U000e0076\U000e006e\U000e0035\U000e0034\U000e007f" - FLAG_FOR_TIEN_GIANG_VN_46 = "\U0001f3f4\U000e0076\U000e006e\U000e0034\U000e0036\U000e007f" - FLAG_FOR_NORTHERN_MW_N = "\U0001f3f4\U000e006d\U000e0077\U000e006e\U000e007f" - FLAG_FOR_CAN_THO_VN_CT = "\U0001f3f4\U000e0076\U000e006e\U000e0063\U000e0074\U000e007f" - FLAG_FOR_BINH_THUAN_VN_40 = "\U0001f3f4\U000e0076\U000e006e\U000e0034\U000e0030\U000e007f" - FLAG_FOR_AK_LAK_VN_33 = "\U0001f3f4\U000e0076\U000e006e\U000e0033\U000e0033\U000e007f" - FLAG_FOR_LAM_ONG_VN_35 = "\U0001f3f4\U000e0076\U000e006e\U000e0033\U000e0035\U000e007f" - FLAG_FOR_BEN_TRE_VN_50 = "\U0001f3f4\U000e0076\U000e006e\U000e0035\U000e0030\U000e007f" - FLAG_FOR_TAY_NINH_VN_37 = "\U0001f3f4\U000e0076\U000e006e\U000e0033\U000e0037\U000e007f" - FLAG_FOR_BINH_INH_VN_31 = "\U0001f3f4\U000e0076\U000e006e\U000e0033\U000e0031\U000e007f" - FLAG_FOR_BAC_LIEU_VN_55 = "\U0001f3f4\U000e0076\U000e006e\U000e0035\U000e0035\U000e007f" - FLAG_FOR_ONG_THAP_VN_45 = "\U0001f3f4\U000e0076\U000e006e\U000e0034\U000e0035\U000e007f" - FLAG_FOR_KIEN_GIANG_VN_47 = "\U0001f3f4\U000e0076\U000e006e\U000e0034\U000e0037\U000e007f" - FLAG_FOR_ONG_NAI_VN_39 = "\U0001f3f4\U000e0076\U000e006e\U000e0033\U000e0039\U000e007f" - FLAG_FOR_BA_RIA_VUNG_TAU_VN_43 = "\U0001f3f4\U000e0076\U000e006e\U000e0034\U000e0033\U000e007f" - FLAG_FOR_UTAH_US_UT = "\U0001f3f4\U000e0075\U000e0073\U000e0075\U000e0074\U000e007f" - FLAG_FOR_VINH_PHUC_VN_70 = "\U0001f3f4\U000e0076\U000e006e\U000e0037\U000e0030\U000e007f" - FLAG_FOR_IEN_BIEN_VN_71 = "\U0001f3f4\U000e0076\U000e006e\U000e0037\U000e0031\U000e007f" - FLAG_FOR_BAC_KAN_VN_53 = "\U0001f3f4\U000e0076\U000e006e\U000e0035\U000e0033\U000e007f" - FLAG_FOR_DA_NANG_VN_DN = "\U0001f3f4\U000e0076\U000e006e\U000e0064\U000e006e\U000e007f" - FLAG_FOR_HA_NAM_VN_63 = "\U0001f3f4\U000e0076\U000e006e\U000e0036\U000e0033\U000e007f" - FLAG_FOR_AK_NONG_VN_72 = "\U0001f3f4\U000e0076\U000e006e\U000e0037\U000e0032\U000e007f" - FLAG_FOR_SOC_TRANG_VN_52 = "\U0001f3f4\U000e0076\U000e006e\U000e0035\U000e0032\U000e007f" - FLAG_FOR_GIA_LAI_VN_30 = "\U0001f3f4\U000e0076\U000e006e\U000e0033\U000e0030\U000e007f" - FLAG_FOR_CA_MAU_VN_59 = "\U0001f3f4\U000e0076\U000e006e\U000e0035\U000e0039\U000e007f" - FLAG_FOR_BOLIVAR_VE_F = "\U0001f3f4\U000e0076\U000e0065\U000e0066\U000e007f" - FLAG_FOR_KHANH_HOA_VN_34 = "\U0001f3f4\U000e0076\U000e006e\U000e0033\U000e0034\U000e007f" - FLAG_FOR_LONG_AN_VN_41 = "\U0001f3f4\U000e0076\U000e006e\U000e0034\U000e0031\U000e007f" - FLAG_FOR_SHEFA_VU_SEE = "\U0001f3f4\U000e0076\U000e0075\U000e0073\U000e0065\U000e0065\U000e007f" - FLAG_FOR_AMRAN_YE_AM = "\U0001f3f4\U000e0079\U000e0065\U000e0061\U000e006d\U000e007f" - FLAG_FOR_RAYMAH_YE_RA = "\U0001f3f4\U000e0079\U000e0065\U000e0072\U000e0061\U000e007f" - FLAG_FOR_MAE_HONG_SON_TH_58 = "\U0001f3f4\U000e0074\U000e0068\U000e0035\U000e0038\U000e007f" - FLAG_FOR_AL_MAHRAH_YE_MR = "\U0001f3f4\U000e0079\U000e0065\U000e006d\U000e0072\U000e007f" - FLAG_FOR_AL_MAHWIT_YE_MW = "\U0001f3f4\U000e0079\U000e0065\U000e006d\U000e0077\U000e007f" - FLAG_FOR_SATUPA_ITEA_WS_SA = "\U0001f3f4\U000e0077\U000e0073\U000e0073\U000e0061\U000e007f" - FLAG_FOR_GAGA_EMAUGA_WS_GE = "\U0001f3f4\U000e0077\U000e0073\U000e0067\U000e0065\U000e007f" - FLAG_FOR_HAIPHONG_VN_HP = "\U0001f3f4\U000e0076\U000e006e\U000e0068\U000e0070\U000e007f" - FAMILY_MAN_LIGHT_SKIN_TONE_MAN_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f468\U0001f3fb\u200d\U0001f467\U0001f3fb" - FLAG_FOR_DHALE_YE_DA = "\U0001f3f4\U000e0079\U000e0065\U000e0064\U000e0061\U000e007f" - FLAG_FOR_DHAMAR_YE_DH = "\U0001f3f4\U000e0079\U000e0065\U000e0064\U000e0068\U000e007f" - FLAG_FOR_TUAMASAGA_WS_TU = "\U0001f3f4\U000e0077\U000e0073\U000e0074\U000e0075\U000e007f" - FLAG_FOR_AL_BAYDA_YE_BA = "\U0001f3f4\U000e0079\U000e0065\U000e0062\U000e0061\U000e007f" - FLAG_FOR_HAJJAH_YE_HJ = "\U0001f3f4\U000e0079\U000e0065\U000e0068\U000e006a\U000e007f" - FAMILY_MAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f469\U0001f3fd\u200d\U0001f476\U0001f3fd\u200d\U0001f476\U0001f3fd" - FLAG_FOR_SHABWAH_YE_SH = "\U0001f3f4\U000e0079\U000e0065\U000e0073\U000e0068\U000e007f" - FAMILY_WOMAN_MAN_GIRL_BOY = "\U0001f469\u200d\U0001f468\u200d\U0001f467\u200d\U0001f466" - FLAG_FOR_ATUA_WS_AT = "\U0001f3f4\U000e0077\U000e0073\U000e0061\U000e0074\U000e007f" - FLAG_FOR_UVEA_WF_UV = "\U0001f3f4\U000e0077\U000e0066\U000e0075\U000e0076\U000e007f" - FLAG_FOR_PENAMA_VU_PAM = "\U0001f3f4\U000e0076\U000e0075\U000e0070\U000e0061\U000e006d\U000e007f" - FLAG_FOR_CRNA_NA_KOROSKEM_SI_016 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0031\U000e0036\U000e007f" - FLAG_FOR_ADAN_YE_AD = "\U0001f3f4\U000e0079\U000e0065\U000e0061\U000e0064\U000e007f" - FLAG_FOR_ABYAN_YE_AB = "\U0001f3f4\U000e0079\U000e0065\U000e0061\U000e0062\U000e007f" - FLAG_FOR_PALAULI_WS_PA = "\U0001f3f4\U000e0077\U000e0073\U000e0070\U000e0061\U000e007f" - FLAG_FOR_CENTAR_ZUPA_MK_78 = "\U0001f3f4\U000e006d\U000e006b\U000e0037\U000e0038\U000e007f" - FLAG_FOR_VA_A_O_FONOTI_WS_VF = "\U0001f3f4\U000e0077\U000e0073\U000e0076\U000e0066\U000e007f" - FLAG_FOR_BULAWAYO_ZW_BU = "\U0001f3f4\U000e007a\U000e0077\U000e0062\U000e0075\U000e007f" - FLAG_FOR_HARARE_ZW_HA = "\U0001f3f4\U000e007a\U000e0077\U000e0068\U000e0061\U000e007f" - FLAG_FOR_GAUTENG_ZA_GT = "\U0001f3f4\U000e007a\U000e0061\U000e0067\U000e0074\U000e007f" - FLAG_FOR_TAIZ_YE_TA = "\U0001f3f4\U000e0079\U000e0065\U000e0074\U000e0061\U000e007f" - FLAG_FOR_NEAMT_RO_NT = "\U0001f3f4\U000e0072\U000e006f\U000e006e\U000e0074\U000e007f" - FLAG_FOR_NORTHERN_ZM_05 = "\U0001f3f4\U000e007a\U000e006d\U000e0030\U000e0035\U000e007f" - COUPLE_WITH_HEART_MAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fd" - FLAG_FOR_MUCHINGA_ZM_10 = "\U0001f3f4\U000e007a\U000e006d\U000e0031\U000e0030\U000e007f" - FLAG_FOR_COPPERBELT_ZM_08 = "\U0001f3f4\U000e007a\U000e006d\U000e0030\U000e0038\U000e007f" - FLAG_FOR_NORTH_WESTERN_ZM_06 = "\U0001f3f4\U000e007a\U000e006d\U000e0030\U000e0036\U000e007f" - KISS_MAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fc" - FLAG_FOR_LUAPULA_ZM_04 = "\U0001f3f4\U000e007a\U000e006d\U000e0030\U000e0034\U000e007f" - FLAG_FOR_LIMPOPO_ZA_LP = "\U0001f3f4\U000e007a\U000e0061\U000e006c\U000e0070\U000e007f" - FLAG_FOR_KOCAELI_TR_41 = "\U0001f3f4\U000e0074\U000e0072\U000e0034\U000e0031\U000e007f" - FLAG_FOR_LUSAKA_ZM_09 = "\U0001f3f4\U000e007a\U000e006d\U000e0030\U000e0039\U000e007f" - FLAG_FOR_WESTERN_ZM_01 = "\U0001f3f4\U000e007a\U000e006d\U000e0030\U000e0031\U000e007f" - FLAG_FOR_REPUBLIKA_SRPSKA_BA_SRP = "\U0001f3f4\U000e0062\U000e0061\U000e0073\U000e0072\U000e0070\U000e007f" - FLAG_FOR_NORTHERN_CAPE_ZA_NC = "\U0001f3f4\U000e007a\U000e0061\U000e006e\U000e0063\U000e007f" - FLAG_FOR_SOUTHLAND_NZ_STL = "\U0001f3f4\U000e006e\U000e007a\U000e0073\U000e0074\U000e006c\U000e007f" - FLAG_FOR_MPUMALANGA_ZA_MP = "\U0001f3f4\U000e007a\U000e0061\U000e006d\U000e0070\U000e007f" - FLAG_FOR_EASTERN_ZM_03 = "\U0001f3f4\U000e007a\U000e006d\U000e0030\U000e0033\U000e007f" - FLAG_FOR_MATABELELAND_NORTH_ZW_MN = "\U0001f3f4\U000e007a\U000e0077\U000e006d\U000e006e\U000e007f" - FLAG_FOR_MASHONALAND_EAST_ZW_ME = "\U0001f3f4\U000e007a\U000e0077\U000e006d\U000e0065\U000e007f" - FLAG_FOR_CENTRAL_ZM_02 = "\U0001f3f4\U000e007a\U000e006d\U000e0030\U000e0032\U000e007f" - FLAG_FOR_MASHONALAND_WEST_ZW_MW = "\U0001f3f4\U000e007a\U000e0077\U000e006d\U000e0077\U000e007f" - FLAG_FOR_SOUTHERN_ZM_07 = "\U0001f3f4\U000e007a\U000e006d\U000e0030\U000e0037\U000e007f" - FLAG_FOR_MASVINGO_ZW_MV = "\U0001f3f4\U000e007a\U000e0077\U000e006d\U000e0076\U000e007f" - FLAG_FOR_MATABELELAND_SOUTH_ZW_MS = "\U0001f3f4\U000e007a\U000e0077\U000e006d\U000e0073\U000e007f" - FLAG_FOR_EASTERN_CAPE_ZA_EC = "\U0001f3f4\U000e007a\U000e0061\U000e0065\U000e0063\U000e007f" - FLAG_FOR_NEW_VALLEY_EG_WAD = "\U0001f3f4\U000e0065\U000e0067\U000e0077\U000e0061\U000e0064\U000e007f" - FLAG_FOR_BOLIVAR_EC_B = "\U0001f3f4\U000e0065\U000e0063\U000e0062\U000e007f" - FLAG_FOR_CHANTHABURI_TH_22 = "\U0001f3f4\U000e0074\U000e0068\U000e0032\U000e0032\U000e007f" - FLAG_FOR_ARTA_DJ_AR = "\U0001f3f4\U000e0064\U000e006a\U000e0061\U000e0072\U000e007f" - TAG_COLON = "\U000e003a" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f468\U0001f3fd\u200d\U0001f476\U0001f3fd\u200d\U0001f466\U0001f3fd" - FAMILY_MAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f468\U0001f3fd\u200d\U0001f467\U0001f3fd" - FLAG_FOR_NIUTAO_TV_NIT = "\U0001f3f4\U000e0074\U000e0076\U000e006e\U000e0069\U000e0074\U000e007f" - TAG_HYPHEN_MINUS = "\U000e002d" - FLAG_FOR_PRIMORSKY_KRAI_RU_PRI = "\U0001f3f4\U000e0072\U000e0075\U000e0070\U000e0072\U000e0069\U000e007f" - FLAG_FOR_MANICALAND_ZW_MA = "\U0001f3f4\U000e007a\U000e0077\U000e006d\U000e0061\U000e007f" - FLAG_FOR_CHUY_KG_C = "\U0001f3f4\U000e006b\U000e0067\U000e0063\U000e007f" - FLAG_FOR_MAINE_US_ME = "\U0001f3f4\U000e0075\U000e0073\U000e006d\U000e0065\U000e007f" - FLAG_FOR_LOWER_JUBA_SO_JH = "\U0001f3f4\U000e0073\U000e006f\U000e006a\U000e0068\U000e007f" - FLAG_FOR_FUKUSHIMA_JP_07 = "\U0001f3f4\U000e006a\U000e0070\U000e0030\U000e0037\U000e007f" - FLAG_FOR_PUDUCHERRY_IN_PY = "\U0001f3f4\U000e0069\U000e006e\U000e0070\U000e0079\U000e007f" - FLAG_FOR_KRABI_TH_81 = "\U0001f3f4\U000e0074\U000e0068\U000e0038\U000e0031\U000e007f" - FLAG_FOR_UTRECHT_NL_UT = "\U0001f3f4\U000e006e\U000e006c\U000e0075\U000e0074\U000e007f" - FLAG_FOR_MANITOBA_CA_MB = "\U0001f3f4\U000e0063\U000e0061\U000e006d\U000e0062\U000e007f" - FLAG_FOR_GAGA_IFOMAUGA_WS_GI = "\U0001f3f4\U000e0077\U000e0073\U000e0067\U000e0069\U000e007f" - FLAG_FOR_NORTH_KHORASAN_IR_31 = "\U0001f3f4\U000e0069\U000e0072\U000e0033\U000e0031\U000e007f" - KISS_WOMAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fe" - FLAG_FOR_IOWA_US_IA = "\U0001f3f4\U000e0075\U000e0073\U000e0069\U000e0061\U000e007f" - FLAG_FOR_LARNACA_CY_03 = "\U0001f3f4\U000e0063\U000e0079\U000e0030\U000e0033\U000e007f" - RESTRICTED_LEFT_ENTRY_2 = "\u26e1" - FLAG_FOR_NORMANDIE_FR_NOR = "\U0001f3f4\U000e0066\U000e0072\U000e006e\U000e006f\U000e0072\U000e007f" - FLAG_FOR_HAWAII_US_HI = "\U0001f3f4\U000e0075\U000e0073\U000e0068\U000e0069\U000e007f" - FLAG_FOR_ZAMBEZIA_MZ_Q = "\U0001f3f4\U000e006d\U000e007a\U000e0071\U000e007f" - FLAG_FOR_MURMANSK_RU_MUR = "\U0001f3f4\U000e0072\U000e0075\U000e006d\U000e0075\U000e0072\U000e007f" - FLAG_FOR_CANARIES_LC_12 = "\U0001f3f4\U000e006c\U000e0063\U000e0031\U000e0032\U000e007f" - FLAG_FOR_NEW_HAMPSHIRE_US_NH = "\U0001f3f4\U000e0075\U000e0073\U000e006e\U000e0068\U000e007f" - FLAG_FOR_DJIBOUTI_DJ_DJ = "\U0001f3f4\U000e0064\U000e006a\U000e0064\U000e006a\U000e007f" - FLAG_FOR_JIZAN_SA_09 = "\U0001f3f4\U000e0073\U000e0061\U000e0030\U000e0039\U000e007f" - FLAG_FOR_MARSA_MT_26 = "\U0001f3f4\U000e006d\U000e0074\U000e0032\U000e0036\U000e007f" - FLAG_FOR_MUGLA_TR_48 = "\U0001f3f4\U000e0074\U000e0072\U000e0034\U000e0038\U000e007f" - TAG_LATIN_CAPITAL_LETTER_G = "\U000e0047" - FLAG_FOR_LAANE_VIRU_EE_59 = "\U0001f3f4\U000e0065\U000e0065\U000e0035\U000e0039\U000e007f" - FLAG_FOR_SAN_JOSE_UY_SJ = "\U0001f3f4\U000e0075\U000e0079\U000e0073\U000e006a\U000e007f" - FLAG_FOR_NORTH_GAZA_PS_NGZ = "\U0001f3f4\U000e0070\U000e0073\U000e006e\U000e0067\U000e007a\U000e007f" - FLAG_FOR_MANUS_PG_MRL = "\U0001f3f4\U000e0070\U000e0067\U000e006d\U000e0072\U000e006c\U000e007f" - FLAG_FOR_SIVAS_TR_58 = "\U0001f3f4\U000e0074\U000e0072\U000e0035\U000e0038\U000e007f" - WOMAN_IN_TUXEDO_MEDIUM_LIGHT_SKIN_TONE = "\U0001f935\U0001f3fc\u200d\u2640\ufe0f" - KISS_WOMAN_MEDIUM_LIGHT_SKIN_TONE_MAN = "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468" - FLAG_FOR_LESSER_POLAND_PL_MA = "\U0001f3f4\U000e0070\U000e006c\U000e006d\U000e0061\U000e007f" - FLAG_FOR_WEST_MACEDONIA_GR_C = "\U0001f3f4\U000e0067\U000e0072\U000e0063\U000e007f" - FLAG_FOR_DEMIR_HISAR_MK_25 = "\U0001f3f4\U000e006d\U000e006b\U000e0032\U000e0035\U000e007f" - FLAG_FOR_ARKHABIL_SUQUTRA_YE_SU = "\U0001f3f4\U000e0079\U000e0065\U000e0073\U000e0075\U000e007f" - FLAG_FOR_COMMEWIJNE_SR_CM = "\U0001f3f4\U000e0073\U000e0072\U000e0063\U000e006d\U000e007f" - FAMILY_MAN_DARK_SKIN_TONE_WOMAN_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f469\U0001f3ff\u200d\U0001f466\U0001f3ff\u200d\U0001f467\U0001f3ff" - COUPLE_WITH_HEART_WOMAN_WOMAN_LIGHT_SKIN_TONE = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fb" - FLAG_FOR_PRYKARPATTIA_UA_26 = "\U0001f3f4\U000e0075\U000e0061\U000e0032\U000e0036\U000e007f" - FLAG_FOR_NUEVA_ESPARTA_VE_O = "\U0001f3f4\U000e0076\U000e0065\U000e006f\U000e007f" - FLAG_FOR_HARJU_EE_37 = "\U0001f3f4\U000e0065\U000e0065\U000e0033\U000e0037\U000e007f" - FLAG_FOR_GUJARAT_IN_GJ = "\U0001f3f4\U000e0069\U000e006e\U000e0067\U000e006a\U000e007f" - FLAG_FOR_JEKABPILS_MUNICIPALITY_LV_042 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0034\U000e0032\U000e007f" - FLAG_FOR_NAVASSA_ISLAND_UM_76 = "\U0001f3f4\U000e0075\U000e006d\U000e0037\U000e0036\U000e007f" - KISS_MAN_DARK_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fd" - FLAG_FOR_OGRE_LV_067 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0036\U000e0037\U000e007f" - FLAG_FOR_YAREN_NR_14 = "\U0001f3f4\U000e006e\U000e0072\U000e0031\U000e0034\U000e007f" - FLAG_FOR_SCHELLENBERG_LI_08 = "\U0001f3f4\U000e006c\U000e0069\U000e0030\U000e0038\U000e007f" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f476\U0001f3fe" - FLAG_FOR_TELANGANA_IN_TG = "\U0001f3f4\U000e0069\U000e006e\U000e0074\U000e0067\U000e007f" - FLAG_FOR_OROMIA_ET_OR = "\U0001f3f4\U000e0065\U000e0074\U000e006f\U000e0072\U000e007f" - FLAG_FOR_UUSIMAA_FI_18 = "\U0001f3f4\U000e0066\U000e0069\U000e0031\U000e0038\U000e007f" - FLAG_FOR_SASSANDRA_MARAHOUE_CI_SM = "\U0001f3f4\U000e0063\U000e0069\U000e0073\U000e006d\U000e007f" - FLAG_FOR_HA_APAI_TO_02 = "\U0001f3f4\U000e0074\U000e006f\U000e0030\U000e0032\U000e007f" - FLAG_FOR_GUAYAS_EC_G = "\U0001f3f4\U000e0065\U000e0063\U000e0067\U000e007f" - FLAG_FOR_DUZCE_TR_81 = "\U0001f3f4\U000e0074\U000e0072\U000e0038\U000e0031\U000e007f" - FLAG_FOR_APURIMAC_PE_APU = "\U0001f3f4\U000e0070\U000e0065\U000e0061\U000e0070\U000e0075\U000e007f" - FAMILY_MAN_LIGHT_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f469\U0001f3fb\u200d\U0001f476\U0001f3fb\u200d\U0001f466\U0001f3fb" - FAMILY_WOMAN_MAN_BABY_BOY = "\U0001f469\u200d\U0001f468\u200d\U0001f476\u200d\U0001f466" - FLAG_FOR_SOUTHERN_NATIONS_NATIONALITIES_AND_PEOPLES_ET_SN = "\U0001f3f4\U000e0065\U000e0074\U000e0073\U000e006e\U000e007f" - FLAG_FOR_CHOCO_CO_CHO = "\U0001f3f4\U000e0063\U000e006f\U000e0063\U000e0068\U000e006f\U000e007f" - FAMILY_MAN_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f466\U0001f3fb\u200d\U0001f466\U0001f3fb" - FLAG_FOR_SAINT_PATRICK_VC_05 = "\U0001f3f4\U000e0076\U000e0063\U000e0030\U000e0035\U000e007f" - FLAG_FOR_HAWALLI_KW_HA = "\U0001f3f4\U000e006b\U000e0077\U000e0068\U000e0061\U000e007f" - FLAG_FOR_ADRAR_MR_07 = "\U0001f3f4\U000e006d\U000e0072\U000e0030\U000e0037\U000e007f" - FLAG_FOR_BOUENZA_CG_11 = "\U0001f3f4\U000e0063\U000e0067\U000e0031\U000e0031\U000e007f" - FLAG_FOR_WYOMING_US_WY = "\U0001f3f4\U000e0075\U000e0073\U000e0077\U000e0079\U000e007f" - FLAG_FOR_BAY_ISLANDS_HN_IB = "\U0001f3f4\U000e0068\U000e006e\U000e0069\U000e0062\U000e007f" - FLAG_FOR_HSINCHU_TW_HSZ = "\U0001f3f4\U000e0074\U000e0077\U000e0068\U000e0073\U000e007a\U000e007f" - TAG_FULL_STOP = "\U000e002e" - FLAG_FOR_VAKAGA_CF_VK = "\U0001f3f4\U000e0063\U000e0066\U000e0076\U000e006b\U000e007f" - FLAG_FOR_ALBERTA_CA_AB = "\U0001f3f4\U000e0063\U000e0061\U000e0061\U000e0062\U000e007f" - FAMILY_MAN_LIGHT_SKIN_TONE_MAN_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f468\U0001f3fb\u200d\U0001f466\U0001f3fb\u200d\U0001f467\U0001f3fb" - FLAG_FOR_PORTUGUESA_VE_P = "\U0001f3f4\U000e0076\U000e0065\U000e0070\U000e007f" - FLAG_FOR_ILE_DE_FRANCE_FR_IDF = "\U0001f3f4\U000e0066\U000e0072\U000e0069\U000e0064\U000e0066\U000e007f" - FLAG_FOR_MISSISSIPPI_US_MS = "\U0001f3f4\U000e0075\U000e0073\U000e006d\U000e0073\U000e007f" - FLAG_FOR_SKOFJA_LOKA_SI_122 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0032\U000e0032\U000e007f" - FLAG_FOR_VAINODE_LV_100 = "\U0001f3f4\U000e006c\U000e0076\U000e0031\U000e0030\U000e0030\U000e007f" - FLAG_FOR_AUSTRALIAN_CAPITAL_TERRITORY_AU_ACT = "\U0001f3f4\U000e0061\U000e0075\U000e0061\U000e0063\U000e0074\U000e007f" - FLAG_FOR_GUARICO_VE_J = "\U0001f3f4\U000e0076\U000e0065\U000e006a\U000e007f" - FLAG_FOR_SAINT_CATHERINE_JM_14 = "\U0001f3f4\U000e006a\U000e006d\U000e0031\U000e0034\U000e007f" - FLAG_FOR_SOKOTO_NG_SO = "\U0001f3f4\U000e006e\U000e0067\U000e0073\U000e006f\U000e007f" - FLAG_FOR_AREQUIPA_PE_ARE = "\U0001f3f4\U000e0070\U000e0065\U000e0061\U000e0072\U000e0065\U000e007f" - FLAG_FOR_SONORA_MX_SON = "\U0001f3f4\U000e006d\U000e0078\U000e0073\U000e006f\U000e006e\U000e007f" - FLAG_FOR_CHANGHUA_TW_CHA = "\U0001f3f4\U000e0074\U000e0077\U000e0063\U000e0068\U000e0061\U000e007f" - FLAG_FOR_LERIBE_LS_C = "\U0001f3f4\U000e006c\U000e0073\U000e0063\U000e007f" - FLAG_FOR_SAINTE_DEVOTE_CHAPEL_MC_SD = "\U0001f3f4\U000e006d\U000e0063\U000e0073\U000e0064\U000e007f" - FLAG_FOR_ISSYK_KUL_KG_Y = "\U0001f3f4\U000e006b\U000e0067\U000e0079\U000e007f" - FLAG_FOR_CHANDIGARH_IN_CH = "\U0001f3f4\U000e0069\U000e006e\U000e0063\U000e0068\U000e007f" - FAMILY_MAN_MAN_BOY_BABY = "\U0001f468\u200d\U0001f468\u200d\U0001f466\u200d\U0001f476" - FLAG_FOR_ARAGON_ES_AR = "\U0001f3f4\U000e0065\U000e0073\U000e0061\U000e0072\U000e007f" - KISS_MAN_MAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fc" - FLAG_FOR_PLASNICA_MK_61 = "\U0001f3f4\U000e006d\U000e006b\U000e0036\U000e0031\U000e007f" - FLAG_FOR_ANTALYA_TR_07 = "\U0001f3f4\U000e0074\U000e0072\U000e0030\U000e0037\U000e007f" - FLAG_FOR_VARAZDIN_HR_05 = "\U0001f3f4\U000e0068\U000e0072\U000e0030\U000e0035\U000e007f" - FAMILY_WOMAN_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f466\U0001f3ff" - FLAG_FOR_TAFEA_VU_TAE = "\U0001f3f4\U000e0076\U000e0075\U000e0074\U000e0061\U000e0065\U000e007f" - FLAG_FOR_VELIKE_LASCE_SI_134 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0033\U000e0034\U000e007f" - FLAG_FOR_QUERETARO_MX_QUE = "\U0001f3f4\U000e006d\U000e0078\U000e0071\U000e0075\U000e0065\U000e007f" - FLAG_FOR_DEIR_AL_BALAH_PS_DEB = "\U0001f3f4\U000e0070\U000e0073\U000e0064\U000e0065\U000e0062\U000e007f" - FLAG_FOR_MEXICO_STATE_MX_MEX = "\U0001f3f4\U000e006d\U000e0078\U000e006d\U000e0065\U000e0078\U000e007f" - FLAG_FOR_LEON_NI_LE = "\U0001f3f4\U000e006e\U000e0069\U000e006c\U000e0065\U000e007f" - FLAG_FOR_NORTH_PROVINCE_MV_NO = "\U0001f3f4\U000e006d\U000e0076\U000e006e\U000e006f\U000e007f" - FLAG_FOR_ST_BARTHELEMY_FR_BL = "\U0001f3f4\U000e0066\U000e0072\U000e0062\U000e006c\U000e007f" - FLAG_FOR_HORDALAND_NO_12 = "\U0001f3f4\U000e006e\U000e006f\U000e0031\U000e0032\U000e007f" - FLAG_FOR_PLZENSKY_KRAJ_CZ_32 = "\U0001f3f4\U000e0063\U000e007a\U000e0033\U000e0032\U000e007f" - FLAG_FOR_EGER_HU_EG = "\U0001f3f4\U000e0068\U000e0075\U000e0065\U000e0067\U000e007f" - FLAG_FOR_TABASCO_MX_TAB = "\U0001f3f4\U000e006d\U000e0078\U000e0074\U000e0061\U000e0062\U000e007f" - FLAG_FOR_FLANDERS_BE_VLG = "\U0001f3f4\U000e0062\U000e0065\U000e0076\U000e006c\U000e0067\U000e007f" - FLAG_FOR_KERMANSHAH_IR_17 = "\U0001f3f4\U000e0069\U000e0072\U000e0031\U000e0037\U000e007f" - FLAG_FOR_APURE_VE_C = "\U0001f3f4\U000e0076\U000e0065\U000e0063\U000e007f" - FLAG_FOR_SILA_TD_SI = "\U0001f3f4\U000e0074\U000e0064\U000e0073\U000e0069\U000e007f" - FLAG_FOR_DURANGO_MX_DUR = "\U0001f3f4\U000e006d\U000e0078\U000e0064\U000e0075\U000e0072\U000e007f" - FLAG_FOR_MARAMURES_RO_MM = "\U0001f3f4\U000e0072\U000e006f\U000e006d\U000e006d\U000e007f" - FLAG_FOR_VELENJE_SI_133 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0033\U000e0033\U000e007f" - FLAG_FOR_SAINT_JOHN_DM_05 = "\U0001f3f4\U000e0064\U000e006d\U000e0030\U000e0035\U000e007f" - FLAG_FOR_NORTH_GYEONGSANG_KR_47 = "\U0001f3f4\U000e006b\U000e0072\U000e0034\U000e0037\U000e007f" - FLAG_FOR_MOROGORO_TZ_16 = "\U0001f3f4\U000e0074\U000e007a\U000e0031\U000e0036\U000e007f" - FLAG_FOR_NORTH_HOLLAND_NL_NH = "\U0001f3f4\U000e006e\U000e006c\U000e006e\U000e0068\U000e007f" - FLAG_FOR_MERIDA_VE_L = "\U0001f3f4\U000e0076\U000e0065\U000e006c\U000e007f" - FLAG_FOR_AUCKLAND_NZ_AUK = "\U0001f3f4\U000e006e\U000e007a\U000e0061\U000e0075\U000e006b\U000e007f" - FLAG_FOR_RIO_GRANDE_DO_SUL_BR_RS = "\U0001f3f4\U000e0062\U000e0072\U000e0072\U000e0073\U000e007f" - FLAG_FOR_RYAZAN_RU_RYA = "\U0001f3f4\U000e0072\U000e0075\U000e0072\U000e0079\U000e0061\U000e007f" - FLAG_FOR_DAR_ES_SALAAM_TZ_02 = "\U0001f3f4\U000e0074\U000e007a\U000e0030\U000e0032\U000e007f" - FAMILY_MAN_LIGHT_SKIN_TONE_MAN_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f468\U0001f3fb\u200d\U0001f476\U0001f3fb\u200d\U0001f476\U0001f3fb" - FLAG_FOR_GAFSA_TN_71 = "\U0001f3f4\U000e0074\U000e006e\U000e0037\U000e0031\U000e007f" - FLAG_FOR_MASHONALAND_CENTRAL_ZW_MC = "\U0001f3f4\U000e007a\U000e0077\U000e006d\U000e0063\U000e007f" - FLAG_FOR_BOVEC_SI_006 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0030\U000e0036\U000e007f" - FLAG_FOR_ANKARAN_SI_213 = "\U0001f3f4\U000e0073\U000e0069\U000e0032\U000e0031\U000e0033\U000e007f" - KISS_MAN_MEDIUM_SKIN_TONE_MAN_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fb" - TAG_LATIN_CAPITAL_LETTER_O = "\U000e004f" - FLAG_FOR_GUERRERO_MX_GRO = "\U0001f3f4\U000e006d\U000e0078\U000e0067\U000e0072\U000e006f\U000e007f" - FLAG_FOR_DOBJE_SI_154 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0035\U000e0034\U000e007f" - FAMILY_MAN_DARK_SKIN_TONE_WOMAN_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f469\U0001f3ff\u200d\U0001f467\U0001f3ff" - FLAG_FOR_RETALHULEU_GT_RE = "\U0001f3f4\U000e0067\U000e0074\U000e0072\U000e0065\U000e007f" - FLAG_FOR_NARINO_CO_NAR = "\U0001f3f4\U000e0063\U000e006f\U000e006e\U000e0061\U000e0072\U000e007f" - FLAG_FOR_TUNIS_TN_11 = "\U0001f3f4\U000e0074\U000e006e\U000e0031\U000e0031\U000e007f" - FLAG_FOR_TIBET_CN_54 = "\U0001f3f4\U000e0063\U000e006e\U000e0035\U000e0034\U000e007f" - FLAG_FOR_MALAMPA_VU_MAP = "\U0001f3f4\U000e0076\U000e0075\U000e006d\U000e0061\U000e0070\U000e007f" - FLAG_FOR_PIEDMONT_IT_21 = "\U0001f3f4\U000e0069\U000e0074\U000e0032\U000e0031\U000e007f" - FLAG_FOR_KAYSERI_TR_38 = "\U0001f3f4\U000e0074\U000e0072\U000e0033\U000e0038\U000e007f" - FLAG_FOR_EQUATEUR_CD_EQ = "\U0001f3f4\U000e0063\U000e0064\U000e0065\U000e0071\U000e007f" - FLAG_FOR_SABAH_MY_12 = "\U0001f3f4\U000e006d\U000e0079\U000e0031\U000e0032\U000e007f" - COUPLE_WITH_HEART_WOMAN_LIGHT_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fb" - FLAG_FOR_NYANGA_GA_5 = "\U0001f3f4\U000e0067\U000e0061\U000e0035\U000e007f" - FLAG_FOR_TAVASTIA_PROPER_FI_06 = "\U0001f3f4\U000e0066\U000e0069\U000e0030\U000e0036\U000e007f" - FLAG_FOR_CARAS_SEVERIN_RO_CS = "\U0001f3f4\U000e0072\U000e006f\U000e0063\U000e0073\U000e007f" - FLAG_FOR_CENTRAL_VISAYAS_PH_07 = "\U0001f3f4\U000e0070\U000e0068\U000e0030\U000e0037\U000e007f" - FLAG_FOR_MASERU_LS_A = "\U0001f3f4\U000e006c\U000e0073\U000e0061\U000e007f" - FLAG_FOR_SAINT_ANDREW_GD_01 = "\U0001f3f4\U000e0067\U000e0064\U000e0030\U000e0031\U000e007f" - FLAG_FOR_LAI_CHAU_VN_01 = "\U0001f3f4\U000e0076\U000e006e\U000e0030\U000e0031\U000e007f" - FLAG_FOR_CHAIYAPHUM_TH_36 = "\U0001f3f4\U000e0074\U000e0068\U000e0033\U000e0036\U000e007f" - FLAG_FOR_ESMERALDAS_EC_E = "\U0001f3f4\U000e0065\U000e0063\U000e0065\U000e007f" - FLAG_FOR_DILI_TL_DI = "\U0001f3f4\U000e0074\U000e006c\U000e0064\U000e0069\U000e007f" - FLAG_FOR_USULUTAN_SV_US = "\U0001f3f4\U000e0073\U000e0076\U000e0075\U000e0073\U000e007f" - FLAG_FOR_MARSABIT_KE_25 = "\U0001f3f4\U000e006b\U000e0065\U000e0032\U000e0035\U000e007f" - FLAG_FOR_FALCON_VE_I = "\U0001f3f4\U000e0076\U000e0065\U000e0069\U000e007f" - FLAG_FOR_ZANZAN_CI_ZZ = "\U0001f3f4\U000e0063\U000e0069\U000e007a\U000e007a\U000e007f" - FLAG_FOR_NORD_UBANGI_CD_NU = "\U0001f3f4\U000e0063\U000e0064\U000e006e\U000e0075\U000e007f" - FLAG_FOR_PAILIN_KH_24 = "\U0001f3f4\U000e006b\U000e0068\U000e0032\U000e0034\U000e007f" - FLAG_FOR_CASTILE_AND_LEON_ES_CL = "\U0001f3f4\U000e0065\U000e0073\U000e0063\U000e006c\U000e007f" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f469\U0001f3fd\u200d\U0001f476\U0001f3fd" - FLAG_FOR_SAMUT_SONGKHRAM_TH_75 = "\U0001f3f4\U000e0074\U000e0068\U000e0037\U000e0035\U000e007f" - FLAG_FOR_ADRAR_DZ_01 = "\U0001f3f4\U000e0064\U000e007a\U000e0030\U000e0031\U000e007f" - FLAG_FOR_EAST_SEPIK_PG_ESW = "\U0001f3f4\U000e0070\U000e0067\U000e0065\U000e0073\U000e0077\U000e007f" - FLAG_FOR_EMILIA_ROMAGNA_IT_45 = "\U0001f3f4\U000e0069\U000e0074\U000e0034\U000e0035\U000e007f" - FLAG_FOR_CHUMPHON_TH_86 = "\U0001f3f4\U000e0074\U000e0068\U000e0038\U000e0036\U000e007f" - FLAG_FOR_MAZSALACA_LV_060 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0036\U000e0030\U000e007f" - FLAG_FOR_BARI_SO_BR = "\U0001f3f4\U000e0073\U000e006f\U000e0062\U000e0072\U000e007f" - FLAG_FOR_ALAND_ISLANDS_FI_01 = "\U0001f3f4\U000e0066\U000e0069\U000e0030\U000e0031\U000e007f" - FLAG_FOR_QUANG_NAM_VN_27 = "\U0001f3f4\U000e0076\U000e006e\U000e0032\U000e0037\U000e007f" - FLAG_FOR_LINE_ISLANDS_KI_L = "\U0001f3f4\U000e006b\U000e0069\U000e006c\U000e007f" - FLAG_FOR_KAGOSHIMA_JP_46 = "\U0001f3f4\U000e006a\U000e0070\U000e0034\U000e0036\U000e007f" - FLAG_FOR_CAIRO_EG_C = "\U0001f3f4\U000e0065\U000e0067\U000e0063\U000e007f" - FLAG_FOR_KUTAHYA_TR_43 = "\U0001f3f4\U000e0074\U000e0072\U000e0034\U000e0033\U000e007f" - FAMILY_MAN_WOMAN_BOY_BABY = "\U0001f468\u200d\U0001f469\u200d\U0001f466\u200d\U0001f476" - FLAG_FOR_AL_BAHAH_SA_11 = "\U0001f3f4\U000e0073\U000e0061\U000e0031\U000e0031\U000e007f" - FLAG_FOR_GREATER_POLAND_PL_WP = "\U0001f3f4\U000e0070\U000e006c\U000e0077\U000e0070\U000e007f" - FLAG_FOR_WEST_AZARBAIJAN_IR_02 = "\U0001f3f4\U000e0069\U000e0072\U000e0030\U000e0032\U000e007f" - FLAG_FOR_YAMALO_NENETS_OKRUG_RU_YAN = "\U0001f3f4\U000e0072\U000e0075\U000e0079\U000e0061\U000e006e\U000e007f" - FLAG_FOR_MONTANA_US_MT = "\U0001f3f4\U000e0075\U000e0073\U000e006d\U000e0074\U000e007f" - KISS_WOMAN_WOMAN_MEDIUM_SKIN_TONE = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fd" - FLAG_FOR_POMEROON_SUPENAAM_GY_PM = "\U0001f3f4\U000e0067\U000e0079\U000e0070\U000e006d\U000e007f" - FLAG_FOR_GWANGJU_CITY_KR_29 = "\U0001f3f4\U000e006b\U000e0072\U000e0032\U000e0039\U000e007f" - FLAG_FOR_DONECHCHYNA_UA_14 = "\U0001f3f4\U000e0075\U000e0061\U000e0031\U000e0034\U000e007f" - FLAG_FOR_SAFI_MT_47 = "\U0001f3f4\U000e006d\U000e0074\U000e0034\U000e0037\U000e007f" - COUPLE_WITH_HEART_MAN_WOMAN_LIGHT_SKIN_TONE = "\U0001f468\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fb" - KISS_MAN_DARK_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fc" - FLAG_FOR_ANTIOQUIA_CO_ANT = "\U0001f3f4\U000e0063\U000e006f\U000e0061\U000e006e\U000e0074\U000e007f" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f468\U0001f3fd\u200d\U0001f467\U0001f3fd\u200d\U0001f467\U0001f3fd" - FLAG_FOR_KRANJ_SI_052 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0035\U000e0032\U000e007f" - FLAG_FOR_MIYAGI_JP_04 = "\U0001f3f4\U000e006a\U000e0070\U000e0030\U000e0034\U000e007f" - FLAG_FOR_MARGIBI_LR_MG = "\U0001f3f4\U000e006c\U000e0072\U000e006d\U000e0067\U000e007f" - FLAG_FOR_ZULIA_VE_V = "\U0001f3f4\U000e0076\U000e0065\U000e0076\U000e007f" - FLAG_FOR_VAISIGANO_WS_VS = "\U0001f3f4\U000e0077\U000e0073\U000e0076\U000e0073\U000e007f" - FLAG_FOR_GLARUS_CH_GL = "\U0001f3f4\U000e0063\U000e0068\U000e0067\U000e006c\U000e007f" - FLAG_FOR_BECHAR_DZ_08 = "\U0001f3f4\U000e0064\U000e007a\U000e0030\U000e0038\U000e007f" - FLAG_FOR_EMBERA_PA_EM = "\U0001f3f4\U000e0070\U000e0061\U000e0065\U000e006d\U000e007f" - FLAG_FOR_LUQA_MT_25 = "\U0001f3f4\U000e006d\U000e0074\U000e0032\U000e0035\U000e007f" - FLAG_FOR_IBB_YE_IB = "\U0001f3f4\U000e0079\U000e0065\U000e0069\U000e0062\U000e007f" - FLAG_FOR_EAST_BERBICE_CORENTYNE_GY_EB = "\U0001f3f4\U000e0067\U000e0079\U000e0065\U000e0062\U000e007f" - FLAG_FOR_U_S_VIRGIN_ISLANDS_US_VI = "\U0001f3f4\U000e0075\U000e0073\U000e0076\U000e0069\U000e007f" - FLAG_FOR_PHATTHALUNG_TH_93 = "\U0001f3f4\U000e0074\U000e0068\U000e0039\U000e0033\U000e007f" - FLAG_FOR_YUCATAN_MX_YUC = "\U0001f3f4\U000e006d\U000e0078\U000e0079\U000e0075\U000e0063\U000e007f" - FLAG_FOR_KALMYKIA_RU_KL = "\U0001f3f4\U000e0072\U000e0075\U000e006b\U000e006c\U000e007f" - FLAG_FOR_ANDALUSIA_ES_AN = "\U0001f3f4\U000e0065\U000e0073\U000e0061\U000e006e\U000e007f" - FLAG_FOR_CAPELLEN_LU_CA = "\U0001f3f4\U000e006c\U000e0075\U000e0063\U000e0061\U000e007f" - COUPLE_WITH_HEART_WOMAN_LIGHT_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fd" - FLAG_FOR_POOL_CG_12 = "\U0001f3f4\U000e0063\U000e0067\U000e0031\U000e0032\U000e007f" - FLAG_FOR_CAUSENI_MD_CS = "\U0001f3f4\U000e006d\U000e0064\U000e0063\U000e0073\U000e007f" - FAMILY_MAN_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f467\U0001f3fb\u200d\U0001f467\U0001f3fb" - FLAG_FOR_SZABOLCS_SZATMAR_BEREG_HU_SZ = "\U0001f3f4\U000e0068\U000e0075\U000e0073\U000e007a\U000e007f" - FLAG_FOR_ZANZIBAR_NORTH_TZ_07 = "\U0001f3f4\U000e0074\U000e007a\U000e0030\U000e0037\U000e007f" - FLAG_FOR_AL_JAWF_YE_JA = "\U0001f3f4\U000e0079\U000e0065\U000e006a\U000e0061\U000e007f" - FLAG_FOR_KOKNESE_LV_046 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0034\U000e0036\U000e007f" - FLAG_FOR_DELTA_NG_DE = "\U0001f3f4\U000e006e\U000e0067\U000e0064\U000e0065\U000e007f" - FAMILY_MAN_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f476\U0001f3ff" - SALTIRE = "\u2613" - FLAG_FOR_NORTH_SINAI_EG_SIN = "\U0001f3f4\U000e0065\U000e0067\U000e0073\U000e0069\U000e006e\U000e007f" - KISS_WOMAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fd" - FAMILY_MAN_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f466\U0001f3ff\u200d\U0001f466\U0001f3ff" - FLAG_FOR_GORENJA_VAS_POLJANE_SI_027 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0032\U000e0037\U000e007f" - WHITE_FLAG_WITH_HORIZONTAL_MIDDLE_BLACK_STRIPE = "\u26ff" - FLAG_FOR_XEWKIJA_MT_62 = "\U0001f3f4\U000e006d\U000e0074\U000e0036\U000e0032\U000e007f" - FLAG_FOR_BAC_NINH_VN_56 = "\U0001f3f4\U000e0076\U000e006e\U000e0035\U000e0036\U000e007f" - FLAG_FOR_HADJER_LAMIS_TD_HL = "\U0001f3f4\U000e0074\U000e0064\U000e0068\U000e006c\U000e007f" - FLAG_FOR_SAINT_MICHAEL_BB_08 = "\U0001f3f4\U000e0062\U000e0062\U000e0030\U000e0038\U000e007f" - FLAG_FOR_MAPUTO_PROVINCE_MZ_L = "\U0001f3f4\U000e006d\U000e007a\U000e006c\U000e007f" - FLAG_FOR_GUIDIMAKA_MR_10 = "\U0001f3f4\U000e006d\U000e0072\U000e0031\U000e0030\U000e007f" - FLAG_FOR_ARACINOVO_MK_02 = "\U0001f3f4\U000e006d\U000e006b\U000e0030\U000e0032\U000e007f" - FLAG_FOR_PAHANG_MY_06 = "\U0001f3f4\U000e006d\U000e0079\U000e0030\U000e0036\U000e007f" - FLAG_FOR_SVAY_RIENG_KH_20 = "\U0001f3f4\U000e006b\U000e0068\U000e0032\U000e0030\U000e007f" - FLAG_FOR_JELGAVA_MUNICIPALITY_LV_041 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0034\U000e0031\U000e007f" - FLAG_FOR_NORTHERN_OSTROBOTHNIA_FI_14 = "\U0001f3f4\U000e0066\U000e0069\U000e0031\U000e0034\U000e007f" - FLAG_FOR_SOUSSE_TN_51 = "\U0001f3f4\U000e0074\U000e006e\U000e0035\U000e0031\U000e007f" - FLAG_FOR_ODRANCI_SI_086 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0038\U000e0036\U000e007f" - FLAG_FOR_ALTO_PARANA_PY_10 = "\U0001f3f4\U000e0070\U000e0079\U000e0031\U000e0030\U000e007f" - FLAG_FOR_WASHINGTON_US_WA = "\U0001f3f4\U000e0075\U000e0073\U000e0077\U000e0061\U000e007f" - FLAG_FOR_TAOYUAN_TW_TAO = "\U0001f3f4\U000e0074\U000e0077\U000e0074\U000e0061\U000e006f\U000e007f" - FLAG_FOR_OMSK_RU_OMS = "\U0001f3f4\U000e0072\U000e0075\U000e006f\U000e006d\U000e0073\U000e007f" - FLAG_FOR_BRATISLAVA_SK_BL = "\U0001f3f4\U000e0073\U000e006b\U000e0062\U000e006c\U000e007f" - FLAG_FOR_GIFU_JP_21 = "\U0001f3f4\U000e006a\U000e0070\U000e0032\U000e0031\U000e007f" - FLAG_FOR_CHIRIQUI_PA_4 = "\U0001f3f4\U000e0070\U000e0061\U000e0034\U000e007f" - FLAG_FOR_HESSE_DE_HE = "\U0001f3f4\U000e0064\U000e0065\U000e0068\U000e0065\U000e007f" - FLAG_FOR_AIWO_NR_01 = "\U0001f3f4\U000e006e\U000e0072\U000e0030\U000e0031\U000e007f" - FLAG_FOR_MA_RIB_YE_MA = "\U0001f3f4\U000e0079\U000e0065\U000e006d\U000e0061\U000e007f" - FLAG_FOR_SINT_EUSTATIUS_BQ_SE = "\U0001f3f4\U000e0062\U000e0071\U000e0073\U000e0065\U000e007f" - FLAG_FOR_ASSAM_IN_AS = "\U0001f3f4\U000e0069\U000e006e\U000e0061\U000e0073\U000e007f" - FLAG_FOR_HUNEDOARA_RO_HD = "\U0001f3f4\U000e0072\U000e006f\U000e0068\U000e0064\U000e007f" - FLAG_FOR_VENTSPILS_MUNICIPALITY_LV_106 = "\U0001f3f4\U000e006c\U000e0076\U000e0031\U000e0030\U000e0036\U000e007f" - FLAG_FOR_SAITAMA_JP_11 = "\U0001f3f4\U000e006a\U000e0070\U000e0031\U000e0031\U000e007f" - FLAG_FOR_CAPITAL_DISTRICT_CO_DC = "\U0001f3f4\U000e0063\U000e006f\U000e0064\U000e0063\U000e007f" - FLAG_FOR_SOUTH_CENTRAL_PROVINCE_MV_SC = "\U0001f3f4\U000e006d\U000e0076\U000e0073\U000e0063\U000e007f" - FLAG_FOR_CHARI_BAGUIRMI_TD_CB = "\U0001f3f4\U000e0074\U000e0064\U000e0063\U000e0062\U000e007f" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f468\U0001f3fe\u200d\U0001f466\U0001f3fe\u200d\U0001f467\U0001f3fe" - FLAG_FOR_ALYTUS_MUNICIPALITY_LT_02 = "\U0001f3f4\U000e006c\U000e0074\U000e0030\U000e0032\U000e007f" - FLAG_FOR_HANOI_VN_HN = "\U0001f3f4\U000e0076\U000e006e\U000e0068\U000e006e\U000e007f" - FLAG_FOR_CRIMEA_UA_43 = "\U0001f3f4\U000e0075\U000e0061\U000e0034\U000e0033\U000e007f" - FLAG_FOR_SAINT_ANDREW_JM_02 = "\U0001f3f4\U000e006a\U000e006d\U000e0030\U000e0032\U000e007f" - FLAG_FOR_SANMA_VU_SAM = "\U0001f3f4\U000e0076\U000e0075\U000e0073\U000e0061\U000e006d\U000e007f" - FLAG_FOR_KHOJAVEND_AZ_XVD = "\U0001f3f4\U000e0061\U000e007a\U000e0078\U000e0076\U000e0064\U000e007f" - FLAG_FOR_LOWER_SAXONY_DE_NI = "\U0001f3f4\U000e0064\U000e0065\U000e006e\U000e0069\U000e007f" - FLAG_FOR_SAN_MARCOS_GT_SM = "\U0001f3f4\U000e0067\U000e0074\U000e0073\U000e006d\U000e007f" - FLAG_FOR_HUNG_YEN_VN_66 = "\U0001f3f4\U000e0076\U000e006e\U000e0036\U000e0036\U000e007f" - FAMILY_WOMAN_DARK_SKIN_TONE_MAN_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f468\U0001f3ff\u200d\U0001f467\U0001f3ff\u200d\U0001f476\U0001f3ff" - FLAG_FOR_SINALOA_MX_SIN = "\U0001f3f4\U000e006d\U000e0078\U000e0073\U000e0069\U000e006e\U000e007f" - FLAG_FOR_MICHIGAN_US_MI = "\U0001f3f4\U000e0075\U000e0073\U000e006d\U000e0069\U000e007f" - FAMILY_MAN_LIGHT_SKIN_TONE_MAN_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f468\U0001f3fb\u200d\U0001f467\U0001f3fb\u200d\U0001f476\U0001f3fb" - FLAG_FOR_BURYAT_RU_BU = "\U0001f3f4\U000e0072\U000e0075\U000e0062\U000e0075\U000e007f" - FLAG_FOR_PODUNAVLJE_RS_10 = "\U0001f3f4\U000e0072\U000e0073\U000e0031\U000e0030\U000e007f" - FLAG_FOR_CURACAO_NL_CW = "\U0001f3f4\U000e006e\U000e006c\U000e0063\U000e0077\U000e007f" - FLAG_FOR_GUANGDONG_CN_44 = "\U0001f3f4\U000e0063\U000e006e\U000e0034\U000e0034\U000e007f" - FLAG_FOR_VASTRA_GOTALAND_SE_O = "\U0001f3f4\U000e0073\U000e0065\U000e006f\U000e007f" - FLAG_FOR_XORAZM_UZ_XO = "\U0001f3f4\U000e0075\U000e007a\U000e0078\U000e006f\U000e007f" - FLAG_FOR_CENTRALE_TG_C = "\U0001f3f4\U000e0074\U000e0067\U000e0063\U000e007f" - FLAG_FOR_BUCHAREST_RO_B = "\U0001f3f4\U000e0072\U000e006f\U000e0062\U000e007f" - FLAG_FOR_SREDISCE_OB_DRAVI_SI_202 = "\U0001f3f4\U000e0073\U000e0069\U000e0032\U000e0030\U000e0032\U000e007f" - FLAG_FOR_HODMEZOVASARHELY_HU_HV = "\U0001f3f4\U000e0068\U000e0075\U000e0068\U000e0076\U000e007f" - FLAG_FOR_BUENOS_AIRES_AR_C = "\U0001f3f4\U000e0061\U000e0072\U000e0063\U000e007f" - FLAG_FOR_KERALA_IN_KL = "\U0001f3f4\U000e0069\U000e006e\U000e006b\U000e006c\U000e007f" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f468\U0001f3fe\u200d\U0001f467\U0001f3fe\u200d\U0001f476\U0001f3fe" - FLAG_FOR_PHUKET_TH_83 = "\U0001f3f4\U000e0074\U000e0068\U000e0038\U000e0033\U000e007f" - FLAG_FOR_SOUTH_KAZAKHSTAN_KZ_YUZ = "\U0001f3f4\U000e006b\U000e007a\U000e0079\U000e0075\U000e007a\U000e007f" - FAMILY_MAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f468\U0001f3fd\u200d\U0001f467\U0001f3fd\u200d\U0001f476\U0001f3fd" - FAMILY_MAN_LIGHT_SKIN_TONE_MAN_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f468\U0001f3fb\u200d\U0001f476\U0001f3fb" - KISS_MAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fc" - FLAG_FOR_BETHLEHEM_PS_BTH = "\U0001f3f4\U000e0070\U000e0073\U000e0062\U000e0074\U000e0068\U000e007f" - FLAG_FOR_OZOLNIEKI_LV_069 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0036\U000e0039\U000e007f" - FLAG_FOR_BUZAU_RO_BZ = "\U0001f3f4\U000e0072\U000e006f\U000e0062\U000e007a\U000e007f" - FLAG_FOR_OUAKA_CF_UK = "\U0001f3f4\U000e0063\U000e0066\U000e0075\U000e006b\U000e007f" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f468\U0001f3fd\u200d\U0001f467\U0001f3fd\u200d\U0001f466\U0001f3fd" - FLAG_FOR_BONG_LR_BG = "\U0001f3f4\U000e006c\U000e0072\U000e0062\U000e0067\U000e007f" - FAMILY_MAN_DARK_SKIN_TONE_MAN_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f468\U0001f3ff\u200d\U0001f467\U0001f3ff\u200d\U0001f476\U0001f3ff" - FLAG_FOR_LEBAP_TM_L = "\U0001f3f4\U000e0074\U000e006d\U000e006c\U000e007f" - FLAG_FOR_VORONEZH_RU_VOR = "\U0001f3f4\U000e0072\U000e0075\U000e0076\U000e006f\U000e0072\U000e007f" - KISS_WOMAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fe" - FLAG_FOR_SAN_JOSE_CR_SJ = "\U0001f3f4\U000e0063\U000e0072\U000e0073\U000e006a\U000e007f" - FLAG_FOR_KISUMU_KE_17 = "\U0001f3f4\U000e006b\U000e0065\U000e0031\U000e0037\U000e007f" - FAMILY_WOMAN_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f467\U0001f3ff\u200d\U0001f466\U0001f3ff" - FLAG_FOR_LAHIJ_YE_LA = "\U0001f3f4\U000e0079\U000e0065\U000e006c\U000e0061\U000e007f" - FLAG_FOR_RACHA_LECHKHUMI_AND_KVEMO_SVANETI_GE_RL = "\U0001f3f4\U000e0067\U000e0065\U000e0072\U000e006c\U000e007f" - FLAG_FOR_ULCINJ_ME_20 = "\U0001f3f4\U000e006d\U000e0065\U000e0032\U000e0030\U000e007f" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f468\U0001f3fc\u200d\U0001f476\U0001f3fc" - FLAG_FOR_OSH_KG_GO = "\U0001f3f4\U000e006b\U000e0067\U000e0067\U000e006f\U000e007f" - FLAG_FOR_AMAZONAS_VE_Z = "\U0001f3f4\U000e0076\U000e0065\U000e007a\U000e007f" - FLAG_FOR_NORTH_RHINE_WESTPHALIA_DE_NW = "\U0001f3f4\U000e0064\U000e0065\U000e006e\U000e0077\U000e007f" - FLAG_FOR_MONAGAS_VE_N = "\U0001f3f4\U000e0076\U000e0065\U000e006e\U000e007f" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f476\U0001f3fc\u200d\U0001f466\U0001f3fc" - FLAG_FOR_CANTABRIA_ES_CB = "\U0001f3f4\U000e0065\U000e0073\U000e0063\U000e0062\U000e007f" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f468\U0001f3fe\u200d\U0001f476\U0001f3fe" - FLAG_FOR_M_SILA_DZ_28 = "\U0001f3f4\U000e0064\U000e007a\U000e0032\U000e0038\U000e007f" - FAMILY_WOMAN_MAN_BOY = "\U0001f469\u200d\U0001f468\u200d\U0001f466" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f467\U0001f3fc\u200d\U0001f466\U0001f3fc" - FAMILY_WOMAN_DARK_SKIN_TONE_MAN_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f468\U0001f3ff\u200d\U0001f467\U0001f3ff" - FLAG_FOR_EASTERN_HIGHLANDS_PG_EHG = "\U0001f3f4\U000e0070\U000e0067\U000e0065\U000e0068\U000e0067\U000e007f" - FLAG_FOR_OHRID_MK_58 = "\U0001f3f4\U000e006d\U000e006b\U000e0035\U000e0038\U000e007f" - FLAG_FOR_SATU_MARE_RO_SM = "\U0001f3f4\U000e0072\U000e006f\U000e0073\U000e006d\U000e007f" - TAG_LATIN_CAPITAL_LETTER_Y = "\U000e0059" - FLAG_FOR_CHERNIHIVSHCHYNA_UA_74 = "\U0001f3f4\U000e0075\U000e0061\U000e0037\U000e0034\U000e007f" - TAG_DIGIT_TWO = "\U000e0032" - FLAG_FOR_RODRIGUES_MU_RO = "\U0001f3f4\U000e006d\U000e0075\U000e0072\U000e006f\U000e007f" - COUPLE_WITH_HEART_WOMAN_MAN_DARK_SKIN_TONE = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3ff" - FAMILY_MAN_DARK_SKIN_TONE_MAN_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f468\U0001f3ff\u200d\U0001f476\U0001f3ff" - FLAG_FOR_HAWKE_S_BAY_NZ_HKB = "\U0001f3f4\U000e006e\U000e007a\U000e0068\U000e006b\U000e0062\U000e007f" - FAMILY_WOMAN_DARK_SKIN_TONE_MAN_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f468\U0001f3ff\u200d\U0001f466\U0001f3ff" - FLAG_FOR_SANT_JULIA_DE_LORIA_AD_06 = "\U0001f3f4\U000e0061\U000e0064\U000e0030\U000e0036\U000e007f" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f476\U0001f3fe\u200d\U0001f467\U0001f3fe" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f468\U0001f3fe\u200d\U0001f466\U0001f3fe" - FLAG_FOR_AN_GIANG_VN_44 = "\U0001f3f4\U000e0076\U000e006e\U000e0034\U000e0034\U000e007f" - FLAG_FOR_UPPSALA_SE_C = "\U0001f3f4\U000e0073\U000e0065\U000e0063\U000e007f" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f476\U0001f3fd\u200d\U0001f467\U0001f3fd" - FLAG_FOR_UDMURT_RU_UD = "\U0001f3f4\U000e0072\U000e0075\U000e0075\U000e0064\U000e007f" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f467\U0001f3fe\u200d\U0001f467\U0001f3fe" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f476\U0001f3fe\u200d\U0001f466\U0001f3fe" - FLAG_FOR_ERMERA_TL_ER = "\U0001f3f4\U000e0074\U000e006c\U000e0065\U000e0072\U000e007f" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f476\U0001f3fe\u200d\U0001f476\U0001f3fe" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f468\U0001f3fd\u200d\U0001f466\U0001f3fd\u200d\U0001f467\U0001f3fd" - FLAG_FOR_PRIMORJE_GORSKI_KOTAR_HR_08 = "\U0001f3f4\U000e0068\U000e0072\U000e0030\U000e0038\U000e007f" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f469\U0001f3fc\u200d\U0001f466\U0001f3fc\u200d\U0001f467\U0001f3fc" - FLAG_FOR_MUS_TR_49 = "\U0001f3f4\U000e0074\U000e0072\U000e0034\U000e0039\U000e007f" - FLAG_FOR_AD_DAKHILIYAH_OM_DA = "\U0001f3f4\U000e006f\U000e006d\U000e0064\U000e0061\U000e007f" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f468\U0001f3fc\u200d\U0001f467\U0001f3fc" - FLAG_FOR_ORIENTAL_MA_04 = "\U0001f3f4\U000e006d\U000e0061\U000e0030\U000e0034\U000e007f" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f468\U0001f3fe\u200d\U0001f466\U0001f3fe\u200d\U0001f476\U0001f3fe" - FLAG_FOR_MAYO_KEBBI_OUEST_TD_MO = "\U0001f3f4\U000e0074\U000e0064\U000e006d\U000e006f\U000e007f" - FLAG_FOR_TBILISI_GE_TB = "\U0001f3f4\U000e0067\U000e0065\U000e0074\U000e0062\U000e007f" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f468\U0001f3fe\u200d\U0001f466\U0001f3fe\u200d\U0001f466\U0001f3fe" - FAMILY_WOMAN_LIGHT_SKIN_TONE_MAN_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f468\U0001f3fb\u200d\U0001f466\U0001f3fb" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f468\U0001f3fd\u200d\U0001f466\U0001f3fd\u200d\U0001f476\U0001f3fd" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f467\U0001f3fe" - FLAG_FOR_PHU_THO_VN_68 = "\U0001f3f4\U000e0076\U000e006e\U000e0036\U000e0038\U000e007f" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f468\U0001f3fd\u200d\U0001f466\U0001f3fd\u200d\U0001f466\U0001f3fd" - FLAG_FOR_FREE_ZA_FS = "\U0001f3f4\U000e007a\U000e0061\U000e0066\U000e0073\U000e007f" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f468\U0001f3fe\u200d\U0001f467\U0001f3fe\u200d\U0001f476\U0001f3fe" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f468\U0001f3fc\u200d\U0001f467\U0001f3fc\u200d\U0001f476\U0001f3fc" - FLAG_FOR_SUCRE_VE_R = "\U0001f3f4\U000e0076\U000e0065\U000e0072\U000e007f" - FAMILY_MAN_DARK_SKIN_TONE_WOMAN_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f469\U0001f3ff\u200d\U0001f466\U0001f3ff\u200d\U0001f466\U0001f3ff" - FLAG_FOR_SALGOTARJAN_HU_ST = "\U0001f3f4\U000e0068\U000e0075\U000e0073\U000e0074\U000e007f" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f468\U0001f3fc\u200d\U0001f476\U0001f3fc" - FLAG_FOR_RAYONG_TH_21 = "\U0001f3f4\U000e0074\U000e0068\U000e0032\U000e0031\U000e007f" - COUPLE_WITH_HEART_WOMAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fc" - FAMILY_MAN_MAN_BABY_GIRL = "\U0001f468\u200d\U0001f468\u200d\U0001f476\u200d\U0001f467" - FAMILY_WOMAN_DARK_SKIN_TONE_MAN_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f468\U0001f3ff\u200d\U0001f466\U0001f3ff\u200d\U0001f467\U0001f3ff" - FAMILY_WOMAN_LIGHT_SKIN_TONE_MAN_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f468\U0001f3fb\u200d\U0001f476\U0001f3fb\u200d\U0001f476\U0001f3fb" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f468\U0001f3fd\u200d\U0001f467\U0001f3fd" - FAMILY_WOMAN_LIGHT_SKIN_TONE_MAN_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f468\U0001f3fb\u200d\U0001f476\U0001f3fb\u200d\U0001f466\U0001f3fb" - FAMILY_WOMAN_DARK_SKIN_TONE_MAN_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f468\U0001f3ff\u200d\U0001f476\U0001f3ff\u200d\U0001f476\U0001f3ff" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f468\U0001f3fe\u200d\U0001f476\U0001f3fe\u200d\U0001f476\U0001f3fe" - FLAG_FOR_SUD_EST_HT_SE = "\U0001f3f4\U000e0068\U000e0074\U000e0073\U000e0065\U000e007f" - FLAG_FOR_CETINJE_ME_06 = "\U0001f3f4\U000e006d\U000e0065\U000e0030\U000e0036\U000e007f" - KISS_WOMAN_LIGHT_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fc" - FLAG_FOR_OVERIJSSEL_NL_OV = "\U0001f3f4\U000e006e\U000e006c\U000e006f\U000e0076\U000e007f" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f468\U0001f3fd\u200d\U0001f467\U0001f3fd\u200d\U0001f476\U0001f3fd" - FLAG_FOR_SMARTNO_OB_PAKI_SI_125 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0032\U000e0035\U000e007f" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f468\U0001f3fd\u200d\U0001f476\U0001f3fd\u200d\U0001f467\U0001f3fd" - FLAG_FOR_CENTRAL_ANDROS_BS_CS = "\U0001f3f4\U000e0062\U000e0073\U000e0063\U000e0073\U000e007f" - FLAG_FOR_GAZA_PS_GZA = "\U0001f3f4\U000e0070\U000e0073\U000e0067\U000e007a\U000e0061\U000e007f" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f469\U0001f3fe\u200d\U0001f466\U0001f3fe\u200d\U0001f467\U0001f3fe" - FLAG_FOR_TASMANIA_AU_TAS = "\U0001f3f4\U000e0061\U000e0075\U000e0074\U000e0061\U000e0073\U000e007f" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f469\U0001f3fd\u200d\U0001f466\U0001f3fd\u200d\U0001f476\U0001f3fd" - FLAG_FOR_GAMBELA_ET_GA = "\U0001f3f4\U000e0065\U000e0074\U000e0067\U000e0061\U000e007f" - FLAG_FOR_SAINT_PETERSBURG_RU_SPE = "\U0001f3f4\U000e0072\U000e0075\U000e0073\U000e0070\U000e0065\U000e007f" - FAMILY_WOMAN_LIGHT_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f469\U0001f3fb\u200d\U0001f466\U0001f3fb\u200d\U0001f467\U0001f3fb" - FLAG_FOR_NORTH_OSSETIA_ALANIA_RU_SE = "\U0001f3f4\U000e0072\U000e0075\U000e0073\U000e0065\U000e007f" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f469\U0001f3fe\u200d\U0001f466\U0001f3fe\u200d\U0001f476\U0001f3fe" - FAMILY_WOMAN_DARK_SKIN_TONE_WOMAN_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f469\U0001f3ff\u200d\U0001f466\U0001f3ff\u200d\U0001f476\U0001f3ff" - FLAG_FOR_GALICIA_ES_GA = "\U0001f3f4\U000e0065\U000e0073\U000e0067\U000e0061\U000e007f" - TAG_REVERSE_SOLIDUS = "\U000e005c" - KISS_MAN_LIGHT_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fc" - FLAG_FOR_KILIMANJARO_TZ_09 = "\U0001f3f4\U000e0074\U000e007a\U000e0030\U000e0039\U000e007f" - FLAG_FOR_GIRESUN_TR_28 = "\U0001f3f4\U000e0074\U000e0072\U000e0032\U000e0038\U000e007f" - FLAG_FOR_KEBBI_NG_KE = "\U0001f3f4\U000e006e\U000e0067\U000e006b\U000e0065\U000e007f" - FLAG_FOR_MAROWIJNE_SR_MA = "\U0001f3f4\U000e0073\U000e0072\U000e006d\U000e0061\U000e007f" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f466\U0001f3fe\u200d\U0001f466\U0001f3fe" - FAMILY_WOMAN_LIGHT_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f469\U0001f3fb\u200d\U0001f476\U0001f3fb" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f469\U0001f3fe\u200d\U0001f467\U0001f3fe\u200d\U0001f476\U0001f3fe" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f466\U0001f3fd\u200d\U0001f476\U0001f3fd" - FAMILY_WOMAN_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f467\U0001f3ff\u200d\U0001f476\U0001f3ff" - FLAG_FOR_SOUTHWEST_FINLAND_FI_19 = "\U0001f3f4\U000e0066\U000e0069\U000e0031\U000e0039\U000e007f" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f469\U0001f3fe\u200d\U0001f467\U0001f3fe\u200d\U0001f467\U0001f3fe" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f468\U0001f3fc\u200d\U0001f466\U0001f3fc\u200d\U0001f467\U0001f3fc" - FAMILY_WOMAN_DARK_SKIN_TONE_WOMAN_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f469\U0001f3ff\u200d\U0001f467\U0001f3ff\u200d\U0001f476\U0001f3ff" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f469\U0001f3fe\u200d\U0001f476\U0001f3fe\u200d\U0001f467\U0001f3fe" - FLAG_FOR_BOKEO_LA_BK = "\U0001f3f4\U000e006c\U000e0061\U000e0062\U000e006b\U000e007f" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f469\U0001f3fc\u200d\U0001f476\U0001f3fc\u200d\U0001f466\U0001f3fc" - FAMILY_WOMAN_LIGHT_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f469\U0001f3fb\u200d\U0001f476\U0001f3fb\u200d\U0001f466\U0001f3fb" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f469\U0001f3fd\u200d\U0001f476\U0001f3fd\u200d\U0001f466\U0001f3fd" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f469\U0001f3fd\u200d\U0001f476\U0001f3fd\u200d\U0001f476\U0001f3fd" - FAMILY_WOMAN_DARK_SKIN_TONE_WOMAN_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f469\U0001f3ff\u200d\U0001f476\U0001f3ff" - FAMILY_WOMAN_DARK_SKIN_TONE_WOMAN_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f469\U0001f3ff\u200d\U0001f476\U0001f3ff\u200d\U0001f467\U0001f3ff" - FLAG_FOR_GASH_BARKA_ER_GB = "\U0001f3f4\U000e0065\U000e0072\U000e0067\U000e0062\U000e007f" - FAMILY_WOMAN_LIGHT_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f469\U0001f3fb\u200d\U0001f476\U0001f3fb\u200d\U0001f467\U0001f3fb" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f469\U0001f3fc\u200d\U0001f476\U0001f3fc\u200d\U0001f476\U0001f3fc" - FLAG_FOR_ARUNACHAL_PRADESH_IN_AR = "\U0001f3f4\U000e0069\U000e006e\U000e0061\U000e0072\U000e007f" - COUPLE_WITH_HEART_MAN_MAN_DARK_SKIN_TONE = "\U0001f468\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3ff" - FAMILY_MAN_LIGHT_SKIN_TONE_MAN_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f468\U0001f3fb\u200d\U0001f467\U0001f3fb\u200d\U0001f467\U0001f3fb" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f469\U0001f3fe\u200d\U0001f476\U0001f3fe" - FAMILY_WOMAN_DARK_SKIN_TONE_MAN_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f468\U0001f3ff\u200d\U0001f467\U0001f3ff\u200d\U0001f466\U0001f3ff" - FLAG_FOR_MINNESOTA_US_MN = "\U0001f3f4\U000e0075\U000e0073\U000e006d\U000e006e\U000e007f" - FLAG_FOR_ROZAJE_ME_17 = "\U0001f3f4\U000e006d\U000e0065\U000e0031\U000e0037\U000e007f" - FAMILY_MAN_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f467\U0001f3ff\u200d\U0001f467\U0001f3ff" - FAMILY_WOMAN_DARK_SKIN_TONE_WOMAN_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f469\U0001f3ff\u200d\U0001f476\U0001f3ff\u200d\U0001f476\U0001f3ff" - FLAG_FOR_EL_OUED_DZ_39 = "\U0001f3f4\U000e0064\U000e007a\U000e0033\U000e0039\U000e007f" - FAMILY_MAN_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f467\U0001f3fd\u200d\U0001f467\U0001f3fd" - FLAG_FOR_OMNOGOVI_MN_053 = "\U0001f3f4\U000e006d\U000e006e\U000e0030\U000e0035\U000e0033\U000e007f" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f467\U0001f3fc" - FLAG_FOR_WEST_COAST_DIVISION_GM_W = "\U0001f3f4\U000e0067\U000e006d\U000e0077\U000e007f" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f466\U0001f3fc\u200d\U0001f467\U0001f3fc" - FLAG_FOR_MALUKU_ISLANDS_ID_ML = "\U0001f3f4\U000e0069\U000e0064\U000e006d\U000e006c\U000e007f" - FLAG_FOR_SVETI_JURIJ_V_SLOVENSKIH_GORICAH_SI_210 = "\U0001f3f4\U000e0073\U000e0069\U000e0032\U000e0031\U000e0030\U000e007f" - FAMILY_MAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f469\U0001f3fd\u200d\U0001f467\U0001f3fd" - FLAG_FOR_TANGA_TZ_25 = "\U0001f3f4\U000e0074\U000e007a\U000e0032\U000e0035\U000e007f" - FLAG_FOR_FAR_NORTH_CM_EN = "\U0001f3f4\U000e0063\U000e006d\U000e0065\U000e006e\U000e007f" - FLAG_FOR_SANNAT_MT_52 = "\U0001f3f4\U000e006d\U000e0074\U000e0035\U000e0032\U000e007f" - FLAG_FOR_INNER_MONGOLIA_CN_15 = "\U0001f3f4\U000e0063\U000e006e\U000e0031\U000e0035\U000e007f" - FLAG_FOR_ST_PIERRE_ANDAMP_MIQUELON_FR_PM = "\U0001f3f4\U000e0066\U000e0072\U000e0070\U000e006d\U000e007f" - NKO_SYMBOL_GBAKURUNEN = "\u07f7" - FLAG_FOR_RIVER_GEE_LR_RG = "\U0001f3f4\U000e006c\U000e0072\U000e0072\U000e0067\U000e007f" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f467\U0001f3fc\u200d\U0001f467\U0001f3fc" - FLAG_FOR_NAIROBI_COUNTY_KE_30 = "\U0001f3f4\U000e006b\U000e0065\U000e0033\U000e0030\U000e007f" - FLAG_FOR_ORYOL_RU_ORL = "\U0001f3f4\U000e0072\U000e0075\U000e006f\U000e0072\U000e006c\U000e007f" - FLAG_FOR_BEJA_PT_02 = "\U0001f3f4\U000e0070\U000e0074\U000e0030\U000e0032\U000e007f" - FLAG_FOR_SKOPJE_MK_85 = "\U0001f3f4\U000e006d\U000e006b\U000e0038\U000e0035\U000e007f" - FAMILY_MAN_DARK_SKIN_TONE_MAN_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f468\U0001f3ff\u200d\U0001f466\U0001f3ff" - FLAG_FOR_MOUNT_LEBANON_LB_JL = "\U0001f3f4\U000e006c\U000e0062\U000e006a\U000e006c\U000e007f" - FLAG_FOR_PERM_KRAI_RU_PER = "\U0001f3f4\U000e0072\U000e0075\U000e0070\U000e0065\U000e0072\U000e007f" - FAMILY_WOMAN_WOMAN_BABY_BABY = "\U0001f469\u200d\U0001f469\u200d\U0001f476\u200d\U0001f476" - KISS_MAN_WOMAN_LIGHT_SKIN_TONE = "\U0001f468\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fb" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f476\U0001f3fc\u200d\U0001f476\U0001f3fc" - FLAG_FOR_ISLA_DE_LA_JUVENTUD_CU_99 = "\U0001f3f4\U000e0063\U000e0075\U000e0039\U000e0039\U000e007f" - UNMARRIED_PARTNERSHIP_SYMBOL = "\u26af" - FLAG_FOR_LAGUNES_CI_LG = "\U0001f3f4\U000e0063\U000e0069\U000e006c\U000e0067\U000e007f" - FLAG_FOR_WESTERN_AUSTRALIA_AU_WA = "\U0001f3f4\U000e0061\U000e0075\U000e0077\U000e0061\U000e007f" - FAMILY_MAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f469\U0001f3fd\u200d\U0001f466\U0001f3fd" - FAMILY_WOMAN_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f467\U0001f3ff" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f467\U0001f3fd" - FLAG_FOR_HEREDIA_CR_H = "\U0001f3f4\U000e0063\U000e0072\U000e0068\U000e007f" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f468\U0001f3fc\u200d\U0001f467\U0001f3fc\u200d\U0001f476\U0001f3fc" - FLAG_FOR_AIGA_I_LE_TAI_WS_AL = "\U0001f3f4\U000e0077\U000e0073\U000e0061\U000e006c\U000e007f" - KISS_WOMAN_LIGHT_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fe" - FLAG_FOR_BAVARIA_DE_BY = "\U0001f3f4\U000e0064\U000e0065\U000e0062\U000e0079\U000e007f" - FLAG_FOR_VALPARAISO_CL_VS = "\U0001f3f4\U000e0063\U000e006c\U000e0076\U000e0073\U000e007f" - WOMAN_IN_BUSINESS_SUIT_LEVITATING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f574\U0001f3fc\u200d\u2640\ufe0f" - FLAG_FOR_BAGHDAD_IQ_BG = "\U0001f3f4\U000e0069\U000e0071\U000e0062\U000e0067\U000e007f" - FLAG_FOR_TENNESSEE_US_TN = "\U0001f3f4\U000e0075\U000e0073\U000e0074\U000e006e\U000e007f" - FAMILY_MAN_WOMAN_BABY = "\U0001f468\u200d\U0001f469\u200d\U0001f476" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f468\U0001f3fd\u200d\U0001f466\U0001f3fd" - FLAG_FOR_LA_PAZ_SV_PA = "\U0001f3f4\U000e0073\U000e0076\U000e0070\U000e0061\U000e007f" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f468\U0001f3fd\u200d\U0001f476\U0001f3fd\u200d\U0001f476\U0001f3fd" - FLAG_FOR_RABAT_MT_46 = "\U0001f3f4\U000e006d\U000e0074\U000e0034\U000e0036\U000e007f" - FLAG_FOR_IMO_NG_IM = "\U0001f3f4\U000e006e\U000e0067\U000e0069\U000e006d\U000e007f" - FLAG_FOR_WELLINGTON_NZ_WGN = "\U0001f3f4\U000e006e\U000e007a\U000e0077\U000e0067\U000e006e\U000e007f" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f476\U0001f3fd\u200d\U0001f476\U0001f3fd" - FLAG_FOR_ZABLJAK_ME_21 = "\U0001f3f4\U000e006d\U000e0065\U000e0032\U000e0031\U000e007f" - FAMILY_MAN_DARK_SKIN_TONE_WOMAN_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f469\U0001f3ff\u200d\U0001f476\U0001f3ff" - FLAG_FOR_BORNO_NG_BO = "\U0001f3f4\U000e006e\U000e0067\U000e0062\U000e006f\U000e007f" - FLAG_FOR_NAKURU_KE_31 = "\U0001f3f4\U000e006b\U000e0065\U000e0033\U000e0031\U000e007f" - FLAG_FOR_CARABOBO_VE_G = "\U0001f3f4\U000e0076\U000e0065\U000e0067\U000e007f" - FLAG_FOR_TEHRAN_IR_07 = "\U0001f3f4\U000e0069\U000e0072\U000e0030\U000e0037\U000e007f" - FLAG_FOR_BADEN_WURTTEMBERG_DE_BW = "\U0001f3f4\U000e0064\U000e0065\U000e0062\U000e0077\U000e007f" - FLAG_FOR_YANGON_MM_06 = "\U0001f3f4\U000e006d\U000e006d\U000e0030\U000e0036\U000e007f" - TAG_LEFT_PARENTHESIS = "\U000e0028" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f468\U0001f3fe\u200d\U0001f467\U0001f3fe\u200d\U0001f466\U0001f3fe" - FLAG_FOR_USTECKY_KRAJ_CZ_42 = "\U0001f3f4\U000e0063\U000e007a\U000e0034\U000e0032\U000e007f" - FLAG_FOR_KUALA_LUMPUR_MY_14 = "\U0001f3f4\U000e006d\U000e0079\U000e0031\U000e0034\U000e007f" - FLAG_FOR_AL_WUSTA_OM_WU = "\U0001f3f4\U000e006f\U000e006d\U000e0077\U000e0075\U000e007f" - FAMILY_WOMAN_LIGHT_SKIN_TONE_MAN_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f468\U0001f3fb\u200d\U0001f467\U0001f3fb\u200d\U0001f466\U0001f3fb" - FLAG_FOR_SOUTH_JEOLLA_KR_46 = "\U0001f3f4\U000e006b\U000e0072\U000e0034\U000e0036\U000e007f" - FLAG_FOR_KHUZESTAN_IR_10 = "\U0001f3f4\U000e0069\U000e0072\U000e0031\U000e0030\U000e007f" - FLAG_FOR_NANTOU_TW_NAN = "\U0001f3f4\U000e0074\U000e0077\U000e006e\U000e0061\U000e006e\U000e007f" - FLAG_FOR_DUSHANBE_TJ_DU = "\U0001f3f4\U000e0074\U000e006a\U000e0064\U000e0075\U000e007f" - FAMILY_MAN_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f476\U0001f3ff\u200d\U0001f467\U0001f3ff" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f476\U0001f3fc\u200d\U0001f467\U0001f3fc" - FLAG_FOR_LIMBURG_NL_LI = "\U0001f3f4\U000e006e\U000e006c\U000e006c\U000e0069\U000e007f" - FLAG_FOR_AYACUCHO_PE_AYA = "\U0001f3f4\U000e0070\U000e0065\U000e0061\U000e0079\U000e0061\U000e007f" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f476\U0001f3fd" - FLAG_FOR_SAINT_PHILIP_AG_08 = "\U0001f3f4\U000e0061\U000e0067\U000e0030\U000e0038\U000e007f" - FLAG_FOR_VALKA_LV_101 = "\U0001f3f4\U000e006c\U000e0076\U000e0031\U000e0030\U000e0031\U000e007f" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f468\U0001f3fc\u200d\U0001f467\U0001f3fc\u200d\U0001f466\U0001f3fc" - FLAG_FOR_MDINA_MT_29 = "\U0001f3f4\U000e006d\U000e0074\U000e0032\U000e0039\U000e007f" - FLAG_FOR_NORTHERN_DENMARK_DK_81 = "\U0001f3f4\U000e0064\U000e006b\U000e0038\U000e0031\U000e007f" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f469\U0001f3fd\u200d\U0001f466\U0001f3fd" - FLAG_FOR_GUAM_US_GU = "\U0001f3f4\U000e0075\U000e0073\U000e0067\U000e0075\U000e007f" - FLAG_FOR_SELANGOR_MY_10 = "\U0001f3f4\U000e006d\U000e0079\U000e0031\U000e0030\U000e007f" - FLAG_FOR_PRINCE_EDWARD_ISLAND_CA_PE = "\U0001f3f4\U000e0063\U000e0061\U000e0070\U000e0065\U000e007f" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f468\U0001f3fe\u200d\U0001f467\U0001f3fe" - FLAG_FOR_GORNO_BADAKHSHAN_TJ_GB = "\U0001f3f4\U000e0074\U000e006a\U000e0067\U000e0062\U000e007f" - FLAG_FOR_OGUN_NG_OG = "\U0001f3f4\U000e006e\U000e0067\U000e006f\U000e0067\U000e007f" - FLAG_FOR_EASTERN_LK_5 = "\U0001f3f4\U000e006c\U000e006b\U000e0035\U000e007f" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f468\U0001f3fe\u200d\U0001f476\U0001f3fe" - FLAG_FOR_SANTA_ROSA_GT_SR = "\U0001f3f4\U000e0067\U000e0074\U000e0073\U000e0072\U000e007f" - FAMILY_MAN_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f466\U0001f3fb\u200d\U0001f467\U0001f3fb" - FLAG_FOR_JAFARA_LY_JI = "\U0001f3f4\U000e006c\U000e0079\U000e006a\U000e0069\U000e007f" - FLAG_FOR_BASILICATA_IT_77 = "\U0001f3f4\U000e0069\U000e0074\U000e0037\U000e0037\U000e007f" - FLAG_FOR_GRONINGEN_NL_GR = "\U0001f3f4\U000e006e\U000e006c\U000e0067\U000e0072\U000e007f" - FLAG_FOR_MATO_GROSSO_DO_SUL_BR_MS = "\U0001f3f4\U000e0062\U000e0072\U000e006d\U000e0073\U000e007f" - FLAG_FOR_KANDAL_KH_8 = "\U0001f3f4\U000e006b\U000e0068\U000e0038\U000e007f" - FLAG_FOR_NORTH_WEST_ZA_NW = "\U0001f3f4\U000e007a\U000e0061\U000e006e\U000e0077\U000e007f" - FLAG_FOR_CONNECTICUT_US_CT = "\U0001f3f4\U000e0075\U000e0073\U000e0063\U000e0074\U000e007f" - FAMILY_MAN_DARK_SKIN_TONE_MAN_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f468\U0001f3ff\u200d\U0001f467\U0001f3ff\u200d\U0001f467\U0001f3ff" - FLAG_FOR_HEBEI_CN_13 = "\U0001f3f4\U000e0063\U000e006e\U000e0031\U000e0033\U000e007f" - FLAG_FOR_FA_ASALELEAGA_WS_FA = "\U0001f3f4\U000e0077\U000e0073\U000e0066\U000e0061\U000e007f" - FLAG_FOR_ALBORZ_IR_32 = "\U0001f3f4\U000e0069\U000e0072\U000e0033\U000e0032\U000e007f" - FLAG_FOR_BRASLOVCE_SI_151 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0035\U000e0031\U000e007f" - FLAG_FOR_HARYANA_IN_HR = "\U0001f3f4\U000e0069\U000e006e\U000e0068\U000e0072\U000e007f" - COUPLE_WITH_HEART_WOMAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fe" - FLAG_FOR_SAN_VICENTE_SV_SV = "\U0001f3f4\U000e0073\U000e0076\U000e0073\U000e0076\U000e007f" - FLAG_FOR_MATO_GROSSO_BR_MT = "\U0001f3f4\U000e0062\U000e0072\U000e006d\U000e0074\U000e007f" - FAMILY_WOMAN_LIGHT_SKIN_TONE_MAN_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f468\U0001f3fb\u200d\U0001f476\U0001f3fb" - FLAG_FOR_PELOPONNESE_GR_J = "\U0001f3f4\U000e0067\U000e0072\U000e006a\U000e007f" - FLAG_FOR_ADAMAWA_NG_AD = "\U0001f3f4\U000e006e\U000e0067\U000e0061\U000e0064\U000e007f" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f469\U0001f3fd\u200d\U0001f467\U0001f3fd\u200d\U0001f466\U0001f3fd" - FLAG_FOR_KWAZULU_NATAL_ZA_NL = "\U0001f3f4\U000e007a\U000e0061\U000e006e\U000e006c\U000e007f" - FLAG_FOR_SINT_MAARTEN_NL_SX = "\U0001f3f4\U000e006e\U000e006c\U000e0073\U000e0078\U000e007f" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f467\U0001f3fe\u200d\U0001f466\U0001f3fe" - TAG_SPACE = "\U000e0020" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f469\U0001f3fe\u200d\U0001f476\U0001f3fe\u200d\U0001f467\U0001f3fe" - FLAG_FOR_CORDILLERA_ADMINISTRATIVE_PH_15 = "\U0001f3f4\U000e0070\U000e0068\U000e0031\U000e0035\U000e007f" - FLAG_FOR_AUCE_LV_010 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0031\U000e0030\U000e007f" - FLAG_FOR_ROCHA_UY_RO = "\U0001f3f4\U000e0075\U000e0079\U000e0072\U000e006f\U000e007f" - FLAG_FOR_FRIESLAND_NL_FR = "\U0001f3f4\U000e006e\U000e006c\U000e0066\U000e0072\U000e007f" - FLAG_FOR_AUVERGNE_RHONE_ALPES_FR_ARA = "\U0001f3f4\U000e0066\U000e0072\U000e0061\U000e0072\U000e0061\U000e007f" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f468\U0001f3fc\u200d\U0001f466\U0001f3fc" - FLAG_FOR_NOHIYAHOI_TOBEI_JUMHURI_TJ_RA = "\U0001f3f4\U000e0074\U000e006a\U000e0072\U000e0061\U000e007f" - APPLE_LOGO = "\uf8ff" - FLAG_FOR_SAO_TOME_ST_S = "\U0001f3f4\U000e0073\U000e0074\U000e0073\U000e007f" - FAMILY_WOMAN_LIGHT_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f469\U0001f3fb\u200d\U0001f467\U0001f3fb\u200d\U0001f466\U0001f3fb" - FAMILY_WOMAN_LIGHT_SKIN_TONE_MAN_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f468\U0001f3fb\u200d\U0001f467\U0001f3fb\u200d\U0001f467\U0001f3fb" - FLAG_FOR_ANDHRA_PRADESH_IN_AP = "\U0001f3f4\U000e0069\U000e006e\U000e0061\U000e0070\U000e007f" - FLAG_FOR_INCUKALNS_LV_037 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0033\U000e0037\U000e007f" - FLAG_FOR_KAKHETI_GE_KA = "\U0001f3f4\U000e0067\U000e0065\U000e006b\U000e0061\U000e007f" - FLAG_FOR_BOURGOGNE_FRANCHE_COMTE_FR_BFC = "\U0001f3f4\U000e0066\U000e0072\U000e0062\U000e0066\U000e0063\U000e007f" - KISS_WOMAN_MEDIUM_SKIN_TONE_MAN = "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468" - FLAG_FOR_ENNEDI_OUEST_TD_EO = "\U0001f3f4\U000e0074\U000e0064\U000e0065\U000e006f\U000e007f" - FLAG_FOR_SOUSS_MASSA_DRAA_MA_13 = "\U0001f3f4\U000e006d\U000e0061\U000e0031\U000e0033\U000e007f" - FLAG_FOR_USAK_TR_64 = "\U0001f3f4\U000e0074\U000e0072\U000e0036\U000e0034\U000e007f" - FLAG_FOR_JEJU_KR_49 = "\U0001f3f4\U000e006b\U000e0072\U000e0034\U000e0039\U000e007f" - FLAG_FOR_CHHATTISGARH_IN_CT = "\U0001f3f4\U000e0069\U000e006e\U000e0063\U000e0074\U000e007f" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f468\U0001f3fe\u200d\U0001f476\U0001f3fe\u200d\U0001f476\U0001f3fe" - FAMILY_WOMAN_LIGHT_SKIN_TONE_MAN_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f468\U0001f3fb\u200d\U0001f467\U0001f3fb\u200d\U0001f476\U0001f3fb" - KISS_WOMAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fe" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f469\U0001f3fe\u200d\U0001f476\U0001f3fe\u200d\U0001f476\U0001f3fe" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f466\U0001f3fd\u200d\U0001f466\U0001f3fd" - FAMILY_MAN_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f466\U0001f3fd\u200d\U0001f466\U0001f3fd" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f467\U0001f3fe\u200d\U0001f476\U0001f3fe" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f469\U0001f3fc\u200d\U0001f466\U0001f3fc\u200d\U0001f466\U0001f3fc" - FLAG_FOR_TLEMCEN_DZ_13 = "\U0001f3f4\U000e0064\U000e007a\U000e0031\U000e0033\U000e007f" - FAMILY_WOMAN_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f476\U0001f3fb\u200d\U0001f476\U0001f3fb" - FLAG_FOR_QUINTANA_ROO_MX_ROO = "\U0001f3f4\U000e006d\U000e0078\U000e0072\U000e006f\U000e006f\U000e007f" - FLAG_FOR_WESTERN_IS_3 = "\U0001f3f4\U000e0069\U000e0073\U000e0033\U000e007f" - FLAG_FOR_BANAADIR_SO_BN = "\U0001f3f4\U000e0073\U000e006f\U000e0062\U000e006e\U000e007f" - FLAG_FOR_RHINELAND_PALATINATE_DE_RP = "\U0001f3f4\U000e0064\U000e0065\U000e0072\U000e0070\U000e007f" - FLAG_FOR_MARITIME_TG_M = "\U0001f3f4\U000e0074\U000e0067\U000e006d\U000e007f" - FLAG_FOR_GYOR_MOSON_SOPRON_HU_GS = "\U0001f3f4\U000e0068\U000e0075\U000e0067\U000e0073\U000e007f" - FLAG_FOR_BEN_AROUS_TN_13 = "\U0001f3f4\U000e0074\U000e006e\U000e0031\U000e0033\U000e007f" - WOMAN_IN_BUSINESS_SUIT_LEVITATING_LIGHT_SKIN_TONE = "\U0001f574\U0001f3fb\u200d\u2640\ufe0f" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f468\U0001f3fc\u200d\U0001f476\U0001f3fc\u200d\U0001f476\U0001f3fc" - FAMILY_WOMAN_DARK_SKIN_TONE_WOMAN_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f469\U0001f3ff\u200d\U0001f467\U0001f3ff" - FLAG_FOR_FUKUI_JP_18 = "\U0001f3f4\U000e006a\U000e0070\U000e0031\U000e0038\U000e007f" - FLAG_FOR_EAST_NEW_BRITAIN_PG_EBR = "\U0001f3f4\U000e0070\U000e0067\U000e0065\U000e0062\U000e0072\U000e007f" - VARIATION_SELECTOR_16 = "\ufe0f" - FLAG_FOR_CENTRAL_EQUATORIA_SS_EC = "\U0001f3f4\U000e0073\U000e0073\U000e0065\U000e0063\U000e007f" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f469\U0001f3fe\u200d\U0001f467\U0001f3fe\u200d\U0001f476\U0001f3fe" - FLAG_FOR_KHAMMOUANE_LA_KH = "\U0001f3f4\U000e006c\U000e0061\U000e006b\U000e0068\U000e007f" - FLAG_FOR_DADRA_AND_NAGAR_HAVELI_IN_DN = "\U0001f3f4\U000e0069\U000e006e\U000e0064\U000e006e\U000e007f" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f469\U0001f3fd\u200d\U0001f466\U0001f3fd\u200d\U0001f466\U0001f3fd" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f468\U0001f3fc\u200d\U0001f466\U0001f3fc\u200d\U0001f476\U0001f3fc" - FLAG_FOR_SOUTHERN_RED_SEA_ER_DK = "\U0001f3f4\U000e0065\U000e0072\U000e0064\U000e006b\U000e007f" - COUPLE_WITH_HEART_WOMAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_DARK_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3ff" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f467\U0001f3fd\u200d\U0001f466\U0001f3fd" - FAMILY_MAN_LIGHT_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f469\U0001f3fb\u200d\U0001f467\U0001f3fb\u200d\U0001f467\U0001f3fb" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f469\U0001f3fd\u200d\U0001f467\U0001f3fd" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f468\U0001f3fc\u200d\U0001f466\U0001f3fc\u200d\U0001f466\U0001f3fc" - FLAG_FOR_VOJVODINA_RS_VO = "\U0001f3f4\U000e0072\U000e0073\U000e0076\U000e006f\U000e007f" - COUPLE_WITH_HEART_MAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fe" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f468\U0001f3fe\u200d\U0001f466\U0001f3fe" - FLAG_FOR_ATLANTICO_SUR_NI_AS = "\U0001f3f4\U000e006e\U000e0069\U000e0061\U000e0073\U000e007f" - COUPLE_WITH_HEART_WOMAN_MAN_MEDIUM_SKIN_TONE = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fd" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f469\U0001f3fc\u200d\U0001f467\U0001f3fc\u200d\U0001f466\U0001f3fc" - FAMILY_MAN_LIGHT_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f469\U0001f3fb\u200d\U0001f476\U0001f3fb" - FLAG_FOR_KERMAN_IR_15 = "\U0001f3f4\U000e0069\U000e0072\U000e0031\U000e0035\U000e007f" - KISS_WOMAN_MEDIUM_DARK_SKIN_TONE_MAN = "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468" - FAMILY_MAN_DARK_SKIN_TONE_WOMAN_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f469\U0001f3ff\u200d\U0001f466\U0001f3ff" - FLAG_FOR_KALMAR_SE_H = "\U0001f3f4\U000e0073\U000e0065\U000e0068\U000e007f" - COUPLE_WITH_HEART_MAN_LIGHT_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fe" - FLAG_FOR_ALMATY_REGION_KZ_ALM = "\U0001f3f4\U000e006b\U000e007a\U000e0061\U000e006c\U000e006d\U000e007f" - FLAG_FOR_ZLINSKY_KRAJ_CZ_72 = "\U0001f3f4\U000e0063\U000e007a\U000e0037\U000e0032\U000e007f" - FLAG_FOR_SANGRE_GRANDE_TT_SGE = "\U0001f3f4\U000e0074\U000e0074\U000e0073\U000e0067\U000e0065\U000e007f" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f469\U0001f3fc\u200d\U0001f467\U0001f3fc\u200d\U0001f466\U0001f3fc" - FLAG_FOR_PUERTO_RICO_US_PR = "\U0001f3f4\U000e0075\U000e0073\U000e0070\U000e0072\U000e007f" - FLAG_FOR_ALO_WF_AL = "\U0001f3f4\U000e0077\U000e0066\U000e0061\U000e006c\U000e007f" - FLAG_FOR_WASHINGTON_DC_US_DC = "\U0001f3f4\U000e0075\U000e0073\U000e0064\U000e0063\U000e007f" - FLAG_FOR_LA_REUNION_FR_LRE = "\U0001f3f4\U000e0066\U000e0072\U000e006c\U000e0072\U000e0065\U000e007f" - FAMILY_MAN_DARK_SKIN_TONE_WOMAN_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f469\U0001f3ff\u200d\U0001f467\U0001f3ff\u200d\U0001f466\U0001f3ff" - FLAG_FOR_GRAD_SI_158 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0035\U000e0038\U000e007f" - FLAG_FOR_TEXAS_US_TX = "\U0001f3f4\U000e0075\U000e0073\U000e0074\U000e0078\U000e007f" - MAN_ZOMBIE_MEDIUM_DARK_SKIN_TONE = "\U0001f9df\U0001f3fe\u200d\u2642\ufe0f" - FLAG_FOR_PARAIBA_BR_PB = "\U0001f3f4\U000e0062\U000e0072\U000e0070\U000e0062\U000e007f" - FLAG_FOR_VARGAS_VE_X = "\U0001f3f4\U000e0076\U000e0065\U000e0078\U000e007f" - TAG_LATIN_CAPITAL_LETTER_H = "\U000e0048" - FLAG_FOR_BERN_CH_BE = "\U0001f3f4\U000e0063\U000e0068\U000e0062\U000e0065\U000e007f" - FLAG_FOR_MARA_TZ_13 = "\U0001f3f4\U000e0074\U000e007a\U000e0031\U000e0033\U000e007f" - FLAG_FOR_THURINGIA_DE_TH = "\U0001f3f4\U000e0064\U000e0065\U000e0074\U000e0068\U000e007f" - COUPLE_WITH_HEART_WOMAN_MAN_MEDIUM_DARK_SKIN_TONE = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fe" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f469\U0001f3fe\u200d\U0001f467\U0001f3fe\u200d\U0001f467\U0001f3fe" - FAMILY_WOMAN_LIGHT_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f469\U0001f3fb\u200d\U0001f466\U0001f3fb\u200d\U0001f466\U0001f3fb" - TAG_LATIN_CAPITAL_LETTER_R = "\U000e0052" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f466\U0001f3fe" - FLAG_FOR_GOTLAND_SE_I = "\U0001f3f4\U000e0073\U000e0065\U000e0069\U000e007f" - FLAG_FOR_ANDAMAN_AND_NICOBAR_ISLANDS_IN_AN = "\U0001f3f4\U000e0069\U000e006e\U000e0061\U000e006e\U000e007f" - FLAG_FOR_SAN_ANDRES_ANDAMP_PROVIDENCIA_CO_SAP = "\U0001f3f4\U000e0063\U000e006f\U000e0073\U000e0061\U000e0070\U000e007f" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f469\U0001f3fe\u200d\U0001f476\U0001f3fe\u200d\U0001f476\U0001f3fe" - FLAG_FOR_SOUTHERN_DENMARK_DK_83 = "\U0001f3f4\U000e0064\U000e006b\U000e0038\U000e0033\U000e007f" - FLAG_FOR_AMUR_RU_AMU = "\U0001f3f4\U000e0072\U000e0075\U000e0061\U000e006d\U000e0075\U000e007f" - FAMILY_MAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f469\U0001f3fd\u200d\U0001f476\U0001f3fd\u200d\U0001f466\U0001f3fd" - FLAG_FOR_NAGASAKI_JP_42 = "\U0001f3f4\U000e006a\U000e0070\U000e0034\U000e0032\U000e007f" - FLAG_FOR_NEWFOUNDLAND_AND_LABRADOR_CA_NL = "\U0001f3f4\U000e0063\U000e0061\U000e006e\U000e006c\U000e007f" - FAMILY_MAN_DARK_SKIN_TONE_MAN_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f468\U0001f3ff\u200d\U0001f466\U0001f3ff\u200d\U0001f467\U0001f3ff" - TAG_GREATER_THAN_SIGN = "\U000e003e" - FLAG_FOR_RAMALLAH_AND_AL_BIREH_PS_RBH = "\U0001f3f4\U000e0070\U000e0073\U000e0072\U000e0062\U000e0068\U000e007f" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f469\U0001f3fd\u200d\U0001f476\U0001f3fd\u200d\U0001f467\U0001f3fd" - KISS_MAN_MAN_MEDIUM_SKIN_TONE = "\U0001f468\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fd" - FLAG_FOR_ANTOFAGASTA_CL_AN = "\U0001f3f4\U000e0063\U000e006c\U000e0061\U000e006e\U000e007f" - FAMILY_MAN_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f476\U0001f3fb\u200d\U0001f466\U0001f3fb" - FAMILY_MAN_LIGHT_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f469\U0001f3fb\u200d\U0001f467\U0001f3fb\u200d\U0001f466\U0001f3fb" - COUPLE_WITH_HEART_WOMAN_MEDIUM_DARK_SKIN_TONE_MAN_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fb" - KISS_MAN_MEDIUM_SKIN_TONE_MAN_DARK_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3ff" - FLAG_FOR_CHUVASH_RU_CU = "\U0001f3f4\U000e0072\U000e0075\U000e0063\U000e0075\U000e007f" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f469\U0001f3fc\u200d\U0001f476\U0001f3fc\u200d\U0001f466\U0001f3fc" - FAMILY_MAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f469\U0001f3fd\u200d\U0001f467\U0001f3fd\u200d\U0001f467\U0001f3fd" - FAMILY_WOMAN_WOMAN_BOY_GIRL = "\U0001f469\u200d\U0001f469\u200d\U0001f466\u200d\U0001f467" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f466\U0001f3fe\u200d\U0001f466\U0001f3fe" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f468\U0001f3fe\u200d\U0001f467\U0001f3fe\u200d\U0001f467\U0001f3fe" - FLAG_FOR_ULSTER_IE_U = "\U0001f3f4\U000e0069\U000e0065\U000e0075\U000e007f" - FAMILY_WOMAN_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f476\U0001f3ff\u200d\U0001f467\U0001f3ff" - FLAG_FOR_NABEUL_TN_21 = "\U0001f3f4\U000e0074\U000e006e\U000e0032\U000e0031\U000e007f" - FAMILY_MAN_BABY_BABY = "\U0001f468\u200d\U0001f476\u200d\U0001f476" - FLAG_FOR_TAIPEI_TW_TPE = "\U0001f3f4\U000e0074\U000e0077\U000e0074\U000e0070\U000e0065\U000e007f" - COUPLE_WITH_HEART_MAN_WOMAN_DARK_SKIN_TONE = "\U0001f468\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3ff" - FAMILY_MAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f468\U0001f3fd\u200d\U0001f466\U0001f3fd\u200d\U0001f466\U0001f3fd" - FLAG_FOR_ZEELAND_NL_ZE = "\U0001f3f4\U000e006e\U000e006c\U000e007a\U000e0065\U000e007f" - FLAG_FOR_JAVA_ID_JW = "\U0001f3f4\U000e0069\U000e0064\U000e006a\U000e0077\U000e007f" - FLAG_FOR_AZAD_KASHMIR_PK_JK = "\U0001f3f4\U000e0070\U000e006b\U000e006a\U000e006b\U000e007f" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f476\U0001f3fc\u200d\U0001f466\U0001f3fc" - FLAG_FOR_UNITY_SS_UY = "\U0001f3f4\U000e0073\U000e0073\U000e0075\U000e0079\U000e007f" - FLAG_FOR_CENTRAL_LUZON_PH_03 = "\U0001f3f4\U000e0070\U000e0068\U000e0030\U000e0033\U000e007f" - FAMILY_WOMAN_DARK_SKIN_TONE_MAN_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f468\U0001f3ff\u200d\U0001f466\U0001f3ff\u200d\U0001f466\U0001f3ff" - FAMILY_WOMAN_LIGHT_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f469\U0001f3fb\u200d\U0001f467\U0001f3fb" - FLAG_FOR_SOUTHERN_LK_3 = "\U0001f3f4\U000e006c\U000e006b\U000e0033\U000e007f" - FLAG_FOR_SAO_PAULO_BR_SP = "\U0001f3f4\U000e0062\U000e0072\U000e0073\U000e0070\U000e007f" - FAMILY_MAN_LIGHT_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f469\U0001f3fb\u200d\U0001f466\U0001f3fb\u200d\U0001f467\U0001f3fb" - FLAG_FOR_JAMMU_AND_KASHMIR_IN_JK = "\U0001f3f4\U000e0069\U000e006e\U000e006a\U000e006b\U000e007f" - FAMILY_MAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f468\U0001f3fd\u200d\U0001f476\U0001f3fd" - FLAG_FOR_CAT_ISLAND_BS_CI = "\U0001f3f4\U000e0062\U000e0073\U000e0063\U000e0069\U000e007f" - FLAG_FOR_LAGHOUAT_DZ_03 = "\U0001f3f4\U000e0064\U000e007a\U000e0030\U000e0033\U000e007f" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f469\U0001f3fc\u200d\U0001f466\U0001f3fc\u200d\U0001f467\U0001f3fc" - FLAG_FOR_MARJ_LY_MJ = "\U0001f3f4\U000e006c\U000e0079\U000e006d\U000e006a\U000e007f" - FAMILY_WOMAN_LIGHT_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f469\U0001f3fb\u200d\U0001f466\U0001f3fb" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f468\U0001f3fc\u200d\U0001f466\U0001f3fc\u200d\U0001f476\U0001f3fc" - COUPLE_WITH_HEART_WOMAN_MEDIUM_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fb" - FLAG_FOR_TRUJILLO_VE_T = "\U0001f3f4\U000e0076\U000e0065\U000e0074\U000e007f" - WOMAN_WITH_HEADSCARF_MEDIUM_DARK_SKIN_TONE = "\U0001f9d5\U0001f3fe\u200d\u2640\ufe0f" - FLAG_FOR_SAARLAND_DE_SL = "\U0001f3f4\U000e0064\U000e0065\U000e0073\U000e006c\U000e007f" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f469\U0001f3fe\u200d\U0001f467\U0001f3fe\u200d\U0001f466\U0001f3fe" - FLAG_FOR_TRENTINO_SOUTH_TYROL_IT_32 = "\U0001f3f4\U000e0069\U000e0074\U000e0033\U000e0032\U000e007f" - FLAG_FOR_KHARTOUM_SD_KH = "\U0001f3f4\U000e0073\U000e0064\U000e006b\U000e0068\U000e007f" - TAG_EXCLAMATION_MARK = "\U000e0021" - FAMILY_WOMAN_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f466\U0001f3fb\u200d\U0001f466\U0001f3fb" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f468\U0001f3fe\u200d\U0001f466\U0001f3fe\u200d\U0001f467\U0001f3fe" - FLAG_FOR_TRANSNISTRIA_MD_SN = "\U0001f3f4\U000e006d\U000e0064\U000e0073\U000e006e\U000e007f" - FLAG_FOR_PINAR_DEL_RIO_CU_01 = "\U0001f3f4\U000e0063\U000e0075\U000e0030\U000e0031\U000e007f" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f468\U0001f3fc\u200d\U0001f476\U0001f3fc\u200d\U0001f466\U0001f3fc" - FAMILY_WOMAN_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f467\U0001f3fb\u200d\U0001f467\U0001f3fb" - FLAG_FOR_KEDAH_MY_02 = "\U0001f3f4\U000e006d\U000e0079\U000e0030\U000e0032\U000e007f" - FLAG_FOR_JILIN_CN_22 = "\U0001f3f4\U000e0063\U000e006e\U000e0032\U000e0032\U000e007f" - ZERO_WIDTH_JOINER = "\u200d" - FAMILY_WOMAN_DARK_SKIN_TONE_MAN_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f468\U0001f3ff\u200d\U0001f467\U0001f3ff\u200d\U0001f467\U0001f3ff" - FLAG_FOR_MANUFAHI_TL_MF = "\U0001f3f4\U000e0074\U000e006c\U000e006d\U000e0066\U000e007f" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f469\U0001f3fd\u200d\U0001f467\U0001f3fd\u200d\U0001f467\U0001f3fd" - FLAG_FOR_STUDENICANI_MK_74 = "\U0001f3f4\U000e006d\U000e006b\U000e0037\U000e0034\U000e007f" - FLAG_FOR_ILUKSTE_LV_036 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0033\U000e0036\U000e007f" - FLAG_FOR_MAZANDARAN_IR_21 = "\U0001f3f4\U000e0069\U000e0072\U000e0032\U000e0031\U000e007f" - FLAG_FOR_VISEU_PT_18 = "\U0001f3f4\U000e0070\U000e0074\U000e0031\U000e0038\U000e007f" - FLAG_FOR_ESTUAIRE_GA_1 = "\U0001f3f4\U000e0067\U000e0061\U000e0031\U000e007f" - FLAG_FOR_GANSU_CN_62 = "\U0001f3f4\U000e0063\U000e006e\U000e0036\U000e0032\U000e007f" - FLAG_FOR_SOUTH_KHORASAN_IR_29 = "\U0001f3f4\U000e0069\U000e0072\U000e0032\U000e0039\U000e007f" - FLAG_FOR_DIOURBEL_SN_DB = "\U0001f3f4\U000e0073\U000e006e\U000e0064\U000e0062\U000e007f" - FLAG_FOR_MORELOS_MX_MOR = "\U0001f3f4\U000e006d\U000e0078\U000e006d\U000e006f\U000e0072\U000e007f" - FLAG_FOR_DELAWARE_US_DE = "\U0001f3f4\U000e0075\U000e0073\U000e0064\U000e0065\U000e007f" - FLAG_FOR_SIGAVE_WF_SG = "\U0001f3f4\U000e0077\U000e0066\U000e0073\U000e0067\U000e007f" - FLAG_FOR_RIVERA_UY_RV = "\U0001f3f4\U000e0075\U000e0079\U000e0072\U000e0076\U000e007f" - FLAG_FOR_TUNGURAHUA_EC_T = "\U0001f3f4\U000e0065\U000e0063\U000e0074\U000e007f" - BEAMED_DESCENDING_MUSICAL_NOTES = "\U0001f39d" - FLAG_FOR_KALININGRAD_RU_KGD = "\U0001f3f4\U000e0072\U000e0075\U000e006b\U000e0067\U000e0064\U000e007f" - TAG_LATIN_CAPITAL_LETTER_T = "\U000e0054" - FLAG_FOR_TOMBOUCTOU_ML_6 = "\U0001f3f4\U000e006d\U000e006c\U000e0036\U000e007f" - COUPLE_WITH_HEART_WOMAN_WOMAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fc" - FLAG_FOR_ENCAMP_AD_03 = "\U0001f3f4\U000e0061\U000e0064\U000e0030\U000e0033\U000e007f" - FLAG_FOR_MIAOLI_TW_MIA = "\U0001f3f4\U000e0074\U000e0077\U000e006d\U000e0069\U000e0061\U000e007f" - FLAG_FOR_KENTUCKY_US_KY = "\U0001f3f4\U000e0075\U000e0073\U000e006b\U000e0079\U000e007f" - FLAG_FOR_GUSINJE_ME_22 = "\U0001f3f4\U000e006d\U000e0065\U000e0032\U000e0032\U000e007f" - FLAG_FOR_SAATLY_AZ_SAT = "\U0001f3f4\U000e0061\U000e007a\U000e0073\U000e0061\U000e0074\U000e007f" - FLAG_FOR_NUNAVUT_CA_NU = "\U0001f3f4\U000e0063\U000e0061\U000e006e\U000e0075\U000e007f" - FLAG_FOR_SPANISH_WELLS_BS_SW = "\U0001f3f4\U000e0062\U000e0073\U000e0073\U000e0077\U000e007f" - FLAG_FOR_BITLIS_TR_13 = "\U0001f3f4\U000e0074\U000e0072\U000e0031\U000e0033\U000e007f" - FLAG_FOR_BADGHIS_AF_BDG = "\U0001f3f4\U000e0061\U000e0066\U000e0062\U000e0064\U000e0067\U000e007f" - KISS_MAN_DARK_SKIN_TONE_MAN_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fd" - FLAG_FOR_SAGA_JP_41 = "\U0001f3f4\U000e006a\U000e0070\U000e0034\U000e0031\U000e007f" - FLAG_FOR_ST_PAUL_S_BAY_MT_51 = "\U0001f3f4\U000e006d\U000e0074\U000e0035\U000e0031\U000e007f" - FLAG_FOR_TORBA_VU_TOB = "\U0001f3f4\U000e0076\U000e0075\U000e0074\U000e006f\U000e0062\U000e007f" - KISS_MAN_DARK_SKIN_TONE_WOMAN_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3ff" - FLAG_FOR_BERLIN_DE_BE = "\U0001f3f4\U000e0064\U000e0065\U000e0062\U000e0065\U000e007f" - FLAG_FOR_BABITE_LV_012 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0031\U000e0032\U000e007f" - FLAG_FOR_EL_VALLE_DO_37 = "\U0001f3f4\U000e0064\U000e006f\U000e0033\U000e0037\U000e007f" - FLAG_FOR_BENI_SUEF_EG_BNS = "\U0001f3f4\U000e0065\U000e0067\U000e0062\U000e006e\U000e0073\U000e007f" - FLAG_FOR_SFAX_TN_61 = "\U0001f3f4\U000e0074\U000e006e\U000e0036\U000e0031\U000e007f" - FLAG_FOR_MONTE_CARLO_MC_MC = "\U0001f3f4\U000e006d\U000e0063\U000e006d\U000e0063\U000e007f" - FLAG_FOR_YUNNAN_CN_53 = "\U0001f3f4\U000e0063\U000e006e\U000e0035\U000e0033\U000e007f" - FLAG_FOR_KLAIPEDOS_MUNICIPALITY_LT_20 = "\U0001f3f4\U000e006c\U000e0074\U000e0032\U000e0030\U000e007f" - FLAG_FOR_BARINGO_KE_01 = "\U0001f3f4\U000e006b\U000e0065\U000e0030\U000e0031\U000e007f" - FLAG_FOR_CENTRAL_RIVER_DIVISION_GM_M = "\U0001f3f4\U000e0067\U000e006d\U000e006d\U000e007f" - FLAG_FOR_AMANAT_AL_ASIMAH_YE_SA = "\U0001f3f4\U000e0079\U000e0065\U000e0073\U000e0061\U000e007f" - FLAG_FOR_HAUTS_BASSINS_BF_09 = "\U0001f3f4\U000e0062\U000e0066\U000e0030\U000e0039\U000e007f" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f466\U0001f3fe\u200d\U0001f476\U0001f3fe" - TAG_EQUALS_SIGN = "\U000e003d" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f468\U0001f3fe\u200d\U0001f467\U0001f3fe" - FLAG_FOR_SENGLEA_MT_20 = "\U0001f3f4\U000e006d\U000e0074\U000e0032\U000e0030\U000e007f" - ALTERNATE_ONE_WAY_LEFT_WAY_TRAFFIC = "\u26d5" - FLAG_FOR_G_ASRI_MT_16 = "\U0001f3f4\U000e006d\U000e0074\U000e0031\U000e0036\U000e007f" - WHITE_CHESS_PAWN = "\u2659" - FLAG_FOR_HAU_GIANG_VN_73 = "\U0001f3f4\U000e0076\U000e006e\U000e0037\U000e0033\U000e007f" - MAN_ZOMBIE_MEDIUM_SKIN_TONE = "\U0001f9df\U0001f3fd\u200d\u2642\ufe0f" - FLAG_FOR_TEARCE_MK_75 = "\U0001f3f4\U000e006d\U000e006b\U000e0037\U000e0035\U000e007f" - FLAG_FOR_FAMAGUSTA_CY_04 = "\U0001f3f4\U000e0063\U000e0079\U000e0030\U000e0034\U000e007f" - FLAG_FOR_COTOPAXI_EC_X = "\U0001f3f4\U000e0065\U000e0063\U000e0078\U000e007f" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f466\U0001f3fc" - FLAG_FOR_ANG_THONG_TH_15 = "\U0001f3f4\U000e0074\U000e0068\U000e0031\U000e0035\U000e007f" - RECYCLING_SYMBOL_FOR_TYPE_2_PLASTICS = "\u2674" - FLAG_FOR_SETUBAL_PT_15 = "\U0001f3f4\U000e0070\U000e0074\U000e0031\U000e0035\U000e007f" - BLACK_CIRCLE_WITH_WHITE_DOT_RIGHT = "\u2688" - WHITE_SPADE_SUIT = "\u2664" - FLAG_FOR_DAGDA_LV_024 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0032\U000e0034\U000e007f" - FLAG_FOR_BAR_ME_02 = "\U0001f3f4\U000e006d\U000e0065\U000e0030\U000e0032\U000e007f" - FLAG_FOR_CATALONIA_ES_CT = "\U0001f3f4\U000e0065\U000e0073\U000e0063\U000e0074\U000e007f" - COUPLE_WITH_HEART_MAN_MAN_MEDIUM_DARK_SKIN_TONE = "\U0001f468\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fe" - FLAG_FOR_WESTERN_SB_WE = "\U0001f3f4\U000e0073\U000e0062\U000e0077\U000e0065\U000e007f" - FLAG_FOR_BAYQONGYR_KZ_BAY = "\U0001f3f4\U000e006b\U000e007a\U000e0062\U000e0061\U000e0079\U000e007f" - NOTE_PAD = "\U0001f5ca" - BLACK_CHESS_KING = "\u265a" - FLAG_FOR_PORTO_PT_13 = "\U0001f3f4\U000e0070\U000e0074\U000e0031\U000e0033\U000e007f" - FLAG_FOR_KAMPONG_CHHNANG_KH_4 = "\U0001f3f4\U000e006b\U000e0068\U000e0034\U000e007f" - FLAG_FOR_DAKAR_SN_DK = "\U0001f3f4\U000e0073\U000e006e\U000e0064\U000e006b\U000e007f" - FLAG_FOR_FEDERAL_CAPITAL_TERRITORY_PL_PM = "\U0001f3f4\U000e0070\U000e006c\U000e0070\U000e006d\U000e007f" - WHITE_TOUCHTONE_TELEPHONE = "\U0001f57e" - FLAG_FOR_TAMIL_NADU_IN_TN = "\U0001f3f4\U000e0069\U000e006e\U000e0074\U000e006e\U000e007f" - FLAG_FOR_BRAGA_PT_03 = "\U0001f3f4\U000e0070\U000e0074\U000e0030\U000e0033\U000e007f" - FLAG_FOR_SPELUGUES_MC_SP = "\U0001f3f4\U000e006d\U000e0063\U000e0073\U000e0070\U000e007f" - FLAG_FOR_JHARKHAND_IN_JH = "\U0001f3f4\U000e0069\U000e006e\U000e006a\U000e0068\U000e007f" - FLAG_FOR_BOCAS_DEL_TORO_PA_1 = "\U0001f3f4\U000e0070\U000e0061\U000e0031\U000e007f" - FLAG_FOR_CANAR_EC_F = "\U0001f3f4\U000e0065\U000e0063\U000e0066\U000e007f" - FLAG_FOR_AICHI_JP_23 = "\U0001f3f4\U000e006a\U000e0070\U000e0032\U000e0033\U000e007f" - FLAG_FOR_OMAHEKE_NA_OH = "\U0001f3f4\U000e006e\U000e0061\U000e006f\U000e0068\U000e007f" - FLAG_FOR_MERSIN_TR_33 = "\U0001f3f4\U000e0074\U000e0072\U000e0033\U000e0033\U000e007f" - FLAG_FOR_OSH_REGION_KG_O = "\U0001f3f4\U000e006b\U000e0067\U000e006f\U000e007f" - FLAG_FOR_MIYAZAKI_JP_45 = "\U0001f3f4\U000e006a\U000e0070\U000e0034\U000e0035\U000e007f" - WHITE_RIGHT_POINTING_INDEX = "\u261e" - COUPLE_WITH_HEART_WOMAN_MEDIUM_SKIN_TONE_MAN_DARK_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3ff" - FLAG_FOR_PAIJANNE_TAVASTIA_FI_16 = "\U0001f3f4\U000e0066\U000e0069\U000e0031\U000e0036\U000e007f" - FLAG_FOR_TARANAKI_NZ_TKI = "\U0001f3f4\U000e006e\U000e007a\U000e0074\U000e006b\U000e0069\U000e007f" - FLAG_FOR_ILLINOIS_US_IL = "\U0001f3f4\U000e0075\U000e0073\U000e0069\U000e006c\U000e007f" - FLAG_FOR_BAMYAN_AF_BAM = "\U0001f3f4\U000e0061\U000e0066\U000e0062\U000e0061\U000e006d\U000e007f" - FLAG_FOR_FLORIDA_UY_FD = "\U0001f3f4\U000e0075\U000e0079\U000e0066\U000e0064\U000e007f" - UP_POINTING_AIRPLANE = "\U0001f6e7" - FLAG_FOR_THAI_NGUYEN_VN_69 = "\U0001f3f4\U000e0076\U000e006e\U000e0036\U000e0039\U000e007f" - FLAG_FOR_VIROVITICA_PODRAVINA_HR_10 = "\U0001f3f4\U000e0068\U000e0072\U000e0031\U000e0030\U000e007f" - KISS_MAN_LIGHT_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fe" - WHITE_LEFT_POINTING_INDEX = "\u261c" - FLAG_FOR_KIGOMA_TZ_08 = "\U0001f3f4\U000e0074\U000e007a\U000e0030\U000e0038\U000e007f" - FLAG_FOR_SUL_GW_S = "\U0001f3f4\U000e0067\U000e0077\U000e0073\U000e007f" - FLAG_FOR_BELIZE_BZ_BZ = "\U0001f3f4\U000e0062\U000e007a\U000e0062\U000e007a\U000e007f" - FLAG_FOR_HHOHHO_SZ_HH = "\U0001f3f4\U000e0073\U000e007a\U000e0068\U000e0068\U000e007f" - FLAG_FOR_AKTOBE_KZ_AKT = "\U0001f3f4\U000e006b\U000e007a\U000e0061\U000e006b\U000e0074\U000e007f" - FLAG_FOR_ZHEJIANG_CN_33 = "\U0001f3f4\U000e0063\U000e006e\U000e0033\U000e0033\U000e007f" - FLAG_FOR_EL_PARAISO_HN_EP = "\U0001f3f4\U000e0068\U000e006e\U000e0065\U000e0070\U000e007f" - MONOGRAM_FOR_YIN = "\u268b" - FLAG_FOR_NUGAL_SO_NU = "\U0001f3f4\U000e0073\U000e006f\U000e006e\U000e0075\U000e007f" - MAHJONG_TILE_NINE_OF_CHARACTERS = "\U0001f00f" - FLAG_FOR_ATTICA_GR_I = "\U0001f3f4\U000e0067\U000e0072\U000e0069\U000e007f" - NOTE_PAGE = "\U0001f5c9" - FLAG_FOR_DURBE_LV_028 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0032\U000e0038\U000e007f" - FLAG_FOR_CAYO_BZ_CY = "\U0001f3f4\U000e0062\U000e007a\U000e0063\U000e0079\U000e007f" - SOFT_SHELL_FLOPPY_DISK = "\U0001f5ac" - FLAG_FOR_SAKHA_RU_SA = "\U0001f3f4\U000e0072\U000e0075\U000e0073\U000e0061\U000e007f" - FLAG_FOR_SEGOU_ML_4 = "\U0001f3f4\U000e006d\U000e006c\U000e0034\U000e007f" - FLAG_FOR_BALKH_AF_BAL = "\U0001f3f4\U000e0061\U000e0066\U000e0062\U000e0061\U000e006c\U000e007f" - FLAG_FOR_WESTFJORDS_IS_4 = "\U0001f3f4\U000e0069\U000e0073\U000e0034\U000e007f" - FLAG_FOR_CALIFORNIA_US_CA = "\U0001f3f4\U000e0075\U000e0073\U000e0063\U000e0061\U000e007f" - FLAG_FOR_SETIF_DZ_19 = "\U0001f3f4\U000e0064\U000e007a\U000e0031\U000e0039\U000e007f" - TAG_COMMA = "\U000e002c" - FLAG_FOR_BAJA_VERAPAZ_GT_BV = "\U0001f3f4\U000e0067\U000e0074\U000e0062\U000e0076\U000e007f" - TAG_LATIN_CAPITAL_LETTER_E = "\U000e0045" - FLAG_FOR_WEST_CM_OU = "\U0001f3f4\U000e0063\U000e006d\U000e006f\U000e0075\U000e007f" - FLAG_FOR_SHAANXI_CN_61 = "\U0001f3f4\U000e0063\U000e006e\U000e0036\U000e0031\U000e007f" - FLAG_FOR_SOUTH_CM_SU = "\U0001f3f4\U000e0063\U000e006d\U000e0073\U000e0075\U000e007f" - FLAG_FOR_RAKHINE_MM_16 = "\U0001f3f4\U000e006d\U000e006d\U000e0031\U000e0036\U000e007f" - FLAG_FOR_BARIMA_WAINI_GY_BA = "\U0001f3f4\U000e0067\U000e0079\U000e0062\U000e0061\U000e007f" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f476\U0001f3fc" - FLAG_FOR_SANA_A_YE_SN = "\U0001f3f4\U000e0079\U000e0065\U000e0073\U000e006e\U000e007f" - FLAG_FOR_VILNIAUS_MUNICIPALITY_LT_57 = "\U0001f3f4\U000e006c\U000e0074\U000e0035\U000e0037\U000e007f" - FLAG_FOR_RANKOVCE_MK_65 = "\U0001f3f4\U000e006d\U000e006b\U000e0036\U000e0035\U000e007f" - FLAG_FOR_WEST_BENGAL_IN_WB = "\U0001f3f4\U000e0069\U000e006e\U000e0077\U000e0062\U000e007f" - TAG_LEFT_CURLY_BRACKET = "\U000e007b" - FLAG_FOR_KIROVOHRADSCHYNA_UA_35 = "\U0001f3f4\U000e0075\U000e0061\U000e0033\U000e0035\U000e007f" - LEFT_THOUGHT_BUBBLE = "\U0001f5ec" - FLAG_FOR_KAPOSVAR_HU_KV = "\U0001f3f4\U000e0068\U000e0075\U000e006b\U000e0076\U000e007f" - RIGHT_SPEAKER_WITH_THREE_SOUND_WAVES = "\U0001f56a" - FLAG_FOR_ARIMA_TT_ARI = "\U0001f3f4\U000e0074\U000e0074\U000e0061\U000e0072\U000e0069\U000e007f" - WHITE_CLUB_SUIT = "\u2667" - REVERSED_THUMBS_DOWN_SIGN = "\U0001f593" - BEAMED_EIGHTH_NOTES = "\u266b" - FLAG_FOR_JUFRA_LY_JU = "\U0001f3f4\U000e006c\U000e0079\U000e006a\U000e0075\U000e007f" - FLAG_FOR_BAGHLAN_AF_BGL = "\U0001f3f4\U000e0061\U000e0066\U000e0062\U000e0067\U000e006c\U000e007f" - TAG_RIGHT_PARENTHESIS = "\U000e0029" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f476\U0001f3fe\u200d\U0001f476\U0001f3fe" - FLAG_FOR_OHIO_US_OH = "\U0001f3f4\U000e0075\U000e0073\U000e006f\U000e0068\U000e007f" - CANCEL_TAG = "\U000e007f" - FLAG_FOR_ST_MARTIN_FR_MF = "\U0001f3f4\U000e0066\U000e0072\U000e006d\U000e0066\U000e007f" - RIGHT_HAND_TELEPHONE_RECEIVER = "\U0001f57d" - COUPLE_WITH_HEART_MAN_MEDIUM_DARK_SKIN_TONE_MAN = "\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f468" - FLAG_FOR_ASTANA_KZ_AST = "\U0001f3f4\U000e006b\U000e007a\U000e0061\U000e0073\U000e0074\U000e007f" - FLAG_FOR_EASTERN_SL_E = "\U0001f3f4\U000e0073\U000e006c\U000e0065\U000e007f" - FLAG_FOR_YALA_TH_95 = "\U0001f3f4\U000e0074\U000e0068\U000e0039\U000e0035\U000e007f" - FLAG_FOR_AHAL_TM_A = "\U0001f3f4\U000e0074\U000e006d\U000e0061\U000e007f" - KISS_MAN_MEDIUM_LIGHT_SKIN_TONE_MAN_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fb" - MAHJONG_TILE_SIX_OF_CIRCLES = "\U0001f01e" - TURNED_BLACK_SHOGI_PIECE = "\u26ca" - FLAG_FOR_WOQOOYI_GALBEED_SO_WO = "\U0001f3f4\U000e0073\U000e006f\U000e0077\U000e006f\U000e007f" - COUPLE_WITH_HEART_MAN_LIGHT_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fc" - FLAG_FOR_MAGWAY_MM_03 = "\U0001f3f4\U000e006d\U000e006d\U000e0030\U000e0033\U000e007f" - FLAG_FOR_SAVANNAKHET_LA_SV = "\U0001f3f4\U000e006c\U000e0061\U000e0073\U000e0076\U000e007f" - FLAG_FOR_CRETE_GR_M = "\U0001f3f4\U000e0067\U000e0072\U000e006d\U000e007f" - FLAG_FOR_BIRZAI_LT_06 = "\U0001f3f4\U000e006c\U000e0074\U000e0030\U000e0036\U000e007f" - FLAG_FOR_NGIWAL_PW_228 = "\U0001f3f4\U000e0070\U000e0077\U000e0032\U000e0032\U000e0038\U000e007f" - FLAG_FOR_RADOVIS_MK_64 = "\U0001f3f4\U000e006d\U000e006b\U000e0036\U000e0034\U000e007f" - FLOWER = "\u2698" - FLAG_FOR_PLANKEN_LI_05 = "\U0001f3f4\U000e006c\U000e0069\U000e0030\U000e0035\U000e007f" - FLAG_FOR_MANGROVE_CAY_BS_MC = "\U0001f3f4\U000e0062\U000e0073\U000e006d\U000e0063\U000e007f" - FLAG_FOR_EMBU_KE_06 = "\U0001f3f4\U000e006b\U000e0065\U000e0030\U000e0036\U000e007f" - FLAG_FOR_FARAH_AF_FRA = "\U0001f3f4\U000e0061\U000e0066\U000e0066\U000e0072\U000e0061\U000e007f" - THREE_RAYS_BELOW = "\U0001f5e5" - COUPLE_WITH_HEART_MAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fd" - FLAG_FOR_THURGAU_CH_TG = "\U0001f3f4\U000e0063\U000e0068\U000e0074\U000e0067\U000e007f" - FLAG_FOR_KAMPONG_CHAM_KH_3 = "\U0001f3f4\U000e006b\U000e0068\U000e0033\U000e007f" - FLAG_FOR_LESSER_SUNDA_ISLANDS_ID_NU = "\U0001f3f4\U000e0069\U000e0064\U000e006e\U000e0075\U000e007f" - FLAG_FOR_BERRY_ISLANDS_BS_BY = "\U0001f3f4\U000e0062\U000e0073\U000e0062\U000e0079\U000e007f" - TWO_SPEECH_BUBBLES = "\U0001f5ea" - FLAG_FOR_WEST_GREECE_GR_G = "\U0001f3f4\U000e0067\U000e0072\U000e0067\U000e007f" - WHITE_SUN = "\U0001f323" - TAG_ASTERISK = "\U000e002a" - PAGE_WITH_CIRCLED_TEXT = "\U0001f5df" - FAMILY_WOMAN_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f466\U0001f3fb\u200d\U0001f476\U0001f3fb" - FLAG_FOR_OGOOUE_MARITIME_GA_8 = "\U0001f3f4\U000e0067\U000e0061\U000e0038\U000e007f" - FLAG_FOR_MADEIRA_PT_30 = "\U0001f3f4\U000e0070\U000e0074\U000e0033\U000e0030\U000e007f" - FLAG_FOR_GURIA_GE_GU = "\U0001f3f4\U000e0067\U000e0065\U000e0067\U000e0075\U000e007f" - FLAG_FOR_PARA_BR_PA = "\U0001f3f4\U000e0062\U000e0072\U000e0070\U000e0061\U000e007f" - FAMILY_WOMAN_BABY_BABY = "\U0001f469\u200d\U0001f476\u200d\U0001f476" - FLAG_FOR_SCHAFFHAUSEN_CH_SH = "\U0001f3f4\U000e0063\U000e0068\U000e0073\U000e0068\U000e007f" - FLAG_FOR_GHOR_AF_GHO = "\U0001f3f4\U000e0061\U000e0066\U000e0067\U000e0068\U000e006f\U000e007f" - FLAG_FOR_ARKANSAS_US_AR = "\U0001f3f4\U000e0075\U000e0073\U000e0061\U000e0072\U000e007f" - FLAG_FOR_DRENTHE_NL_DR = "\U0001f3f4\U000e006e\U000e006c\U000e0064\U000e0072\U000e007f" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f468\U0001f3fc\u200d\U0001f466\U0001f3fc" - FLAG_FOR_GHAZNI_AF_GHA = "\U0001f3f4\U000e0061\U000e0066\U000e0067\U000e0068\U000e0061\U000e007f" - FLAG_FOR_OZAMA_DO_40 = "\U0001f3f4\U000e0064\U000e006f\U000e0034\U000e0030\U000e007f" - FLAG_FOR_HERAT_AF_HER = "\U0001f3f4\U000e0061\U000e0066\U000e0068\U000e0065\U000e0072\U000e007f" - TAG_DIGIT_FOUR = "\U000e0034" - FLAG_FOR_NEBRASKA_US_NE = "\U0001f3f4\U000e0075\U000e0073\U000e006e\U000e0065\U000e007f" - FLAG_FOR_ARICA_Y_PARINACOTA_CL_AP = "\U0001f3f4\U000e0063\U000e006c\U000e0061\U000e0070\U000e007f" - FLAG_FOR_ZANJAN_IR_11 = "\U0001f3f4\U000e0069\U000e0072\U000e0031\U000e0031\U000e007f" - FLAG_FOR_BRANDENBURG_DE_BB = "\U0001f3f4\U000e0064\U000e0065\U000e0062\U000e0062\U000e007f" - FLAG_FOR_BISTRICA_OB_SOTLI_SI_149 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0034\U000e0039\U000e007f" - MAHJONG_TILE_NORTH_WIND = "\U0001f003" - FLAG_FOR_ALABAMA_US_AL = "\U0001f3f4\U000e0075\U000e0073\U000e0061\U000e006c\U000e007f" - FLAG_FOR_AL_RAYYAN_QA_RA = "\U0001f3f4\U000e0071\U000e0061\U000e0072\U000e0061\U000e007f" - LOWER_RIGHT_PENCIL = "\u270e" - FLAG_FOR_SKRUNDA_LV_093 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0039\U000e0033\U000e007f" - NUMBER_SIGN = "#\ufe0f" - EMPTY_NOTE_PAGE = "\U0001f5c6" - DIGIT_FOUR = "4\ufe0f" - FLAG_FOR_KIDAL_ML_8 = "\U0001f3f4\U000e006d\U000e006c\U000e0038\U000e007f" - FLAG_FOR_BACAU_RO_BC = "\U0001f3f4\U000e0072\U000e006f\U000e0062\U000e0063\U000e007f" - BACK_OF_ENVELOPE = "\U0001f582" - MAN_ZOMBIE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9df\U0001f3fc\u200d\u2642\ufe0f" - FLAG_FOR_BURGENLAND_AT_1 = "\U0001f3f4\U000e0061\U000e0074\U000e0031\U000e007f" - FLAG_FOR_KAYANGEL_PW_100 = "\U0001f3f4\U000e0070\U000e0077\U000e0031\U000e0030\U000e0030\U000e007f" - FLAG_FOR_TOPLICA_RS_21 = "\U0001f3f4\U000e0072\U000e0073\U000e0032\U000e0031\U000e007f" - FLAG_FOR_KRASNODAR_KRAI_RU_KDA = "\U0001f3f4\U000e0072\U000e0075\U000e006b\U000e0064\U000e0061\U000e007f" - FLAG_FOR_LAUTEM_TL_LA = "\U0001f3f4\U000e0074\U000e006c\U000e006c\U000e0061\U000e007f" - UPPER_RIGHT_PENCIL = "\u2710" - FLAG_FOR_BANGKOK_TH_10 = "\U0001f3f4\U000e0074\U000e0068\U000e0031\U000e0030\U000e007f" - FLAG_FOR_BOUGAINVILLE_PG_NSB = "\U0001f3f4\U000e0070\U000e0067\U000e006e\U000e0073\U000e0062\U000e007f" - FLAG_FOR_GRENADINES_VC_06 = "\U0001f3f4\U000e0076\U000e0063\U000e0030\U000e0036\U000e007f" - WIRED_KEYBOARD = "\U0001f5ae" - FLAG_FOR_KABUL_AF_KAB = "\U0001f3f4\U000e0061\U000e0066\U000e006b\U000e0061\U000e0062\U000e007f" - TAG_LATIN_CAPITAL_LETTER_J = "\U000e004a" - FLAG_FOR_AMAZONAS_CO_AMA = "\U0001f3f4\U000e0063\U000e006f\U000e0061\U000e006d\U000e0061\U000e007f" - DIGIT_NINE = "9\ufe0f" - FLAG_FOR_TASMAN_NZ_TAS = "\U0001f3f4\U000e006e\U000e007a\U000e0074\U000e0061\U000e0073\U000e007f" - FLAG_FOR_STYRIA_AT_6 = "\U0001f3f4\U000e0061\U000e0074\U000e0036\U000e007f" - FLAG_FOR_GABORONE_BW_GA = "\U0001f3f4\U000e0062\U000e0077\U000e0067\U000e0061\U000e007f" - FLAG_FOR_SAINT_MARK_GD_05 = "\U0001f3f4\U000e0067\U000e0064\U000e0030\U000e0035\U000e007f" - FLAG_FOR_VALLE_DEL_CAUCA_CO_VAC = "\U0001f3f4\U000e0063\U000e006f\U000e0076\U000e0061\U000e0063\U000e007f" - FLAG_FOR_KHAKASSIA_RU_KK = "\U0001f3f4\U000e0072\U000e0075\U000e006b\U000e006b\U000e007f" - FLAG_FOR_MISRATA_LY_MI = "\U0001f3f4\U000e006c\U000e0079\U000e006d\U000e0069\U000e007f" - FLAG_FOR_QUEENSLAND_AU_QLD = "\U0001f3f4\U000e0061\U000e0075\U000e0071\U000e006c\U000e0064\U000e007f" - FAMILY_MAN_LIGHT_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f469\U0001f3fb\u200d\U0001f466\U0001f3fb\u200d\U0001f476\U0001f3fb" - FLAG_FOR_OSAKA_JP_27 = "\U0001f3f4\U000e006a\U000e0070\U000e0032\U000e0037\U000e007f" - FLAG_FOR_MICHOACAN_MX_MIC = "\U0001f3f4\U000e006d\U000e0078\U000e006d\U000e0069\U000e0063\U000e007f" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f467\U0001f3fe\u200d\U0001f467\U0001f3fe" - FAMILY_MAN_WOMAN_BOY_GIRL = "\U0001f468\u200d\U0001f469\u200d\U0001f466\u200d\U0001f467" - KISS_WOMAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fe" - BLACK_UP_POINTING_BACKHAND_INDEX = "\U0001f5a2" - RIGHT_SPEAKER = "\U0001f568" - COMBINING_ENCLOSING_KEYCAP = "\u20e3" - FLAG_FOR_AZUAY_EC_A = "\U0001f3f4\U000e0065\U000e0063\U000e0061\U000e007f" - FLAG_FOR_LUHANSHCHYNA_UA_09 = "\U0001f3f4\U000e0075\U000e0061\U000e0030\U000e0039\U000e007f" - FLAG_FOR_MAHARASHTRA_IN_MH = "\U0001f3f4\U000e0069\U000e006e\U000e006d\U000e0068\U000e007f" - UPPER_RIGHT_SHADOWED_WHITE_CIRCLE = "\U0001f53f" - FLAG_FOR_INSULAR_GQ_I = "\U0001f3f4\U000e0067\U000e0071\U000e0069\U000e007f" - JUPITER = "\u2643" - FLAG_FOR_AKKAR_LB_AK = "\U0001f3f4\U000e006c\U000e0062\U000e0061\U000e006b\U000e007f" - FLAG_FOR_SOHAG_EG_SHG = "\U0001f3f4\U000e0065\U000e0067\U000e0073\U000e0068\U000e0067\U000e007f" - FLAG_FOR_MOHELI_KM_M = "\U0001f3f4\U000e006b\U000e006d\U000e006d\U000e007f" - FLAG_FOR_AL_AHMADI_KW_AH = "\U0001f3f4\U000e006b\U000e0077\U000e0061\U000e0068\U000e007f" - WOMAN_IN_TUXEDO_LIGHT_SKIN_TONE = "\U0001f935\U0001f3fb\u200d\u2640\ufe0f" - FLAG_FOR_GUADALCANAL_SB_GU = "\U0001f3f4\U000e0073\U000e0062\U000e0067\U000e0075\U000e007f" - FLAG_FOR_CIBITOKE_BI_CI = "\U0001f3f4\U000e0062\U000e0069\U000e0063\U000e0069\U000e007f" - WOMAN_ZOMBIE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9df\U0001f3fc\u200d\u2640\ufe0f" - FLAG_FOR_BAS_UELE_CD_BU = "\U0001f3f4\U000e0063\U000e0064\U000e0062\U000e0075\U000e007f" - FLAG_FOR_NEVIS_KN_N = "\U0001f3f4\U000e006b\U000e006e\U000e006e\U000e007f" - FLAG_FOR_BONAIRE_BQ_BO = "\U0001f3f4\U000e0062\U000e0071\U000e0062\U000e006f\U000e007f" - FLAG_FOR_ANCASH_PE_ANC = "\U0001f3f4\U000e0070\U000e0065\U000e0061\U000e006e\U000e0063\U000e007f" - CAR_SLIDING = "\u26d0" - DIGRAM_FOR_LESSER_YANG = "\u268e" - THREE_NETWORKED_COMPUTERS = "\U0001f5a7" - FLAG_FOR_ASUNCION_PY_ASU = "\U0001f3f4\U000e0070\U000e0079\U000e0061\U000e0073\U000e0075\U000e007f" - CROSS_POMMEE_WITH_HALF_CIRCLE_BELOW = "\U0001f541" - FLAG_FOR_TOLIMA_CO_TOL = "\U0001f3f4\U000e0063\U000e006f\U000e0074\U000e006f\U000e006c\U000e007f" - TAG_DIGIT_ONE = "\U000e0031" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f468\U0001f3fe\u200d\U0001f476\U0001f3fe\u200d\U0001f466\U0001f3fe" - FLAG_FOR_JEWISH_RU_YEV = "\U0001f3f4\U000e0072\U000e0075\U000e0079\U000e0065\U000e0076\U000e007f" - FLAG_FOR_LA_PAZ_HN_LP = "\U0001f3f4\U000e0068\U000e006e\U000e006c\U000e0070\U000e007f" - FLAG_FOR_RIO_SAN_JUAN_NI_SJ = "\U0001f3f4\U000e006e\U000e0069\U000e0073\U000e006a\U000e007f" - FLAG_FOR_PAYS_DE_LA_LOIRE_FR_PDL = "\U0001f3f4\U000e0066\U000e0072\U000e0070\U000e0064\U000e006c\U000e007f" - FLAG_FOR_RIVERS_NG_RI = "\U0001f3f4\U000e006e\U000e0067\U000e0072\U000e0069\U000e007f" - OLD_PERSONAL_COMPUTER = "\U0001f5b3" - FLAG_FOR_DAEGU_KR_27 = "\U0001f3f4\U000e006b\U000e0072\U000e0032\U000e0037\U000e007f" - EMPTY_NOTE_PAD = "\U0001f5c7" - FLAG_FOR_SHAN_MM_17 = "\U0001f3f4\U000e006d\U000e006d\U000e0031\U000e0037\U000e007f" - BLACK_FLAG_2 = "\u2691" - MAHJONG_TILE_SEVEN_OF_CHARACTERS = "\U0001f00d" - FAMILY_MAN_BOY_GIRL = "\U0001f468\u200d\U0001f466\u200d\U0001f467" - FLAG_FOR_ADANA_TR_01 = "\U0001f3f4\U000e0074\U000e0072\U000e0030\U000e0031\U000e007f" - TAG_LATIN_CAPITAL_LETTER_C = "\U000e0043" - TWO_BUTTON_MOUSE = "\U0001f5b0" - ASTERISK = "*\ufe0f" - MAHJONG_TILE_FOUR_OF_CIRCLES = "\U0001f01c" - FLAG_FOR_KAPISA_AF_KAP = "\U0001f3f4\U000e0061\U000e0066\U000e006b\U000e0061\U000e0070\U000e007f" - FLAG_FOR_MON_MM_15 = "\U0001f3f4\U000e006d\U000e006d\U000e0031\U000e0035\U000e007f" - FLAG_FOR_AL_BURAIMI_OM_BU = "\U0001f3f4\U000e006f\U000e006d\U000e0062\U000e0075\U000e007f" - FLAG_FOR_AGALEGA_MU_AG = "\U0001f3f4\U000e006d\U000e0075\U000e0061\U000e0067\U000e007f" - ONE_BUTTON_MOUSE = "\U0001f5af" - FLAG_FOR_LOGAR_AF_LOG = "\U0001f3f4\U000e0061\U000e0066\U000e006c\U000e006f\U000e0067\U000e007f" - FLAG_FOR_BEL_OMBRE_SC_10 = "\U0001f3f4\U000e0073\U000e0063\U000e0031\U000e0030\U000e007f" - FLAG_FOR_ORANGE_WALK_BZ_OW = "\U0001f3f4\U000e0062\U000e007a\U000e006f\U000e0077\U000e007f" - MALE_WITH_STROKE_AND_MALE_AND_FEMALE_SIGN = "\u26a7" - FLAG_FOR_KANDAHAR_AF_KAN = "\U0001f3f4\U000e0061\U000e0066\U000e006b\U000e0061\U000e006e\U000e007f" - TAPE_CARTRIDGE = "\U0001f5ad" - FLAG_FOR_ULAANBAATAR_MN_1 = "\U0001f3f4\U000e006d\U000e006e\U000e0031\U000e007f" - FLAG_FOR_FARYAB_AF_FYB = "\U0001f3f4\U000e0061\U000e0066\U000e0066\U000e0079\U000e0062\U000e007f" - TAG_LATIN_SMALL_LETTER_F = "\U000e0066" - FLAG_FOR_PARWAN_AF_PAR = "\U0001f3f4\U000e0061\U000e0066\U000e0070\U000e0061\U000e0072\U000e007f" - FLAG_FOR_NIMRUZ_AF_NIM = "\U0001f3f4\U000e0061\U000e0066\U000e006e\U000e0069\U000e006d\U000e007f" - FLAG_FOR_HIIU_EE_39 = "\U0001f3f4\U000e0065\U000e0065\U000e0033\U000e0039\U000e007f" - FLAG_FOR_EXTREMADURA_ES_EX = "\U0001f3f4\U000e0065\U000e0073\U000e0065\U000e0078\U000e007f" - FLAG_FOR_SABA_BQ_SA = "\U0001f3f4\U000e0062\U000e0071\U000e0073\U000e0061\U000e007f" - FLAG_FOR_KARLOVAC_HR_04 = "\U0001f3f4\U000e0068\U000e0072\U000e0030\U000e0034\U000e007f" - FLAG_FOR_BROD_POSAVINA_HR_12 = "\U0001f3f4\U000e0068\U000e0072\U000e0031\U000e0032\U000e007f" - FLAG_FOR_NANGARHAR_AF_NAN = "\U0001f3f4\U000e0061\U000e0066\U000e006e\U000e0061\U000e006e\U000e007f" - MAHJONG_TILE_SEVEN_OF_CIRCLES = "\U0001f01f" - TRIGRAM_FOR_HEAVEN = "\u2630" - DIGIT_SEVEN = "7\ufe0f" - COUPLE_WITH_HEART_WOMAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fc" - RECYCLING_SYMBOL_FOR_TYPE_1_PLASTICS = "\u2673" - FLAG_FOR_CORSE_FR_COR = "\U0001f3f4\U000e0066\U000e0072\U000e0063\U000e006f\U000e0072\U000e007f" - MAN_WITH_HEADSCARF_MEDIUM_SKIN_TONE = "\U0001f9d5\U0001f3fd\u200d\u2642\ufe0f" - FLAG_FOR_HAMADAN_IR_24 = "\U0001f3f4\U000e0069\U000e0072\U000e0032\U000e0034\U000e007f" - FLAG_FOR_FARS_IR_14 = "\U0001f3f4\U000e0069\U000e0072\U000e0031\U000e0034\U000e007f" - FLAG_FOR_FRENCH_GUIANA_FR_GF = "\U0001f3f4\U000e0066\U000e0072\U000e0067\U000e0066\U000e007f" - FLAG_FOR_TICINO_CH_TI = "\U0001f3f4\U000e0063\U000e0068\U000e0074\U000e0069\U000e007f" - FLAG_FOR_BOR_RS_14 = "\U0001f3f4\U000e0072\U000e0073\U000e0031\U000e0034\U000e007f" - FLAG_FOR_UTTARADIT_TH_53 = "\U0001f3f4\U000e0074\U000e0068\U000e0035\U000e0033\U000e007f" - TAG_LATIN_SMALL_LETTER_M = "\U000e006d" - FLAG_FOR_BAMAKO_ML_BKO = "\U0001f3f4\U000e006d\U000e006c\U000e0062\U000e006b\U000e006f\U000e007f" - FLAG_FOR_ESPIRITO_SANTO_BR_ES = "\U0001f3f4\U000e0062\U000e0072\U000e0065\U000e0073\U000e007f" - TAG_LATIN_SMALL_LETTER_R = "\U000e0072" - FLAG_FOR_BOLIKHAMSAI_LA_BL = "\U0001f3f4\U000e006c\U000e0061\U000e0062\U000e006c\U000e007f" - BULLHORN_WITH_SOUND_WAVES = "\U0001f56c" - FLAG_FOR_HALLAND_SE_N = "\U0001f3f4\U000e0073\U000e0065\U000e006e\U000e007f" - FLAG_FOR_UPPER_DEMERARA_BERBICE_GY_UD = "\U0001f3f4\U000e0067\U000e0079\U000e0075\U000e0064\U000e007f" - WOMAN_ZOMBIE_MEDIUM_SKIN_TONE = "\U0001f9df\U0001f3fd\u200d\u2640\ufe0f" - FLAG_FOR_ZIROVNICA_SI_192 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0039\U000e0032\U000e007f" - FLAG_FOR_TAITUNG_TW_TTT = "\U0001f3f4\U000e0074\U000e0077\U000e0074\U000e0074\U000e0074\U000e007f" - FLAG_FOR_NANUMANGA_TV_NMG = "\U0001f3f4\U000e0074\U000e0076\U000e006e\U000e006d\U000e0067\U000e007f" - FLAG_FOR_TUNAPUNA_PIARCO_TT_TUP = "\U0001f3f4\U000e0074\U000e0074\U000e0074\U000e0075\U000e0070\U000e007f" - TAG_DIGIT_SEVEN = "\U000e0037" - COUPLE_WITH_HEART_WOMAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fe" - KEYBOARD_AND_MOUSE = "\U0001f5a6" - MAHJONG_TILE_GREEN_DRAGON = "\U0001f005" - FLAG_FOR_CEUTA_ES_CE = "\U0001f3f4\U000e0065\U000e0073\U000e0063\U000e0065\U000e007f" - KISS_MAN_MEDIUM_SKIN_TONE_WOMAN = "\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469" - FLAG_FOR_SABARAGAMUWA_LK_9 = "\U0001f3f4\U000e006c\U000e006b\U000e0039\U000e007f" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f468\U0001f3fe\u200d\U0001f476\U0001f3fe\u200d\U0001f466\U0001f3fe" - COUPLE_WITH_HEART_MAN_LIGHT_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fd" - FLAG_FOR_BUTNAN_LY_BU = "\U0001f3f4\U000e006c\U000e0079\U000e0062\U000e0075\U000e007f" - FLAG_FOR_COLIMA_MX_COL = "\U0001f3f4\U000e006d\U000e0078\U000e0063\U000e006f\U000e006c\U000e007f" - FLAG_FOR_QUEBEC_CA_QC = "\U0001f3f4\U000e0063\U000e0061\U000e0071\U000e0063\U000e007f" - WHITE_DOWN_POINTING_INDEX = "\u261f" - FLAG_FOR_CUNDINAMARCA_CO_CUN = "\U0001f3f4\U000e0063\U000e006f\U000e0063\U000e0075\U000e006e\U000e007f" - TAG_LATIN_CAPITAL_LETTER_D = "\U000e0044" - FLAG_FOR_DAMASCUS_SY_DI = "\U0001f3f4\U000e0073\U000e0079\U000e0064\U000e0069\U000e007f" - FLAG_FOR_DUBAI_AE_DU = "\U0001f3f4\U000e0061\U000e0065\U000e0064\U000e0075\U000e007f" - SHIBUYA = "\ue50a" - FLAG_FOR_KAYES_ML_1 = "\U0001f3f4\U000e006d\U000e006c\U000e0031\U000e007f" - FLAG_FOR_CEARA_BR_CE = "\U0001f3f4\U000e0062\U000e0072\U000e0063\U000e0065\U000e007f" - FLAG_FOR_SHARJAH_AE_SH = "\U0001f3f4\U000e0061\U000e0065\U000e0073\U000e0068\U000e007f" - FLAG_FOR_EASTERN_IS_7 = "\U0001f3f4\U000e0069\U000e0073\U000e0037\U000e007f" - WHITE_CHESS_BISHOP = "\u2657" - FLAG_FOR_AFAR_ET_AF = "\U0001f3f4\U000e0065\U000e0074\U000e0061\U000e0066\U000e007f" - TAG_QUESTION_MARK = "\U000e003f" - FAMILY_WOMAN_GIRL_BABY = "\U0001f469\u200d\U0001f467\u200d\U0001f476" - FLAG_FOR_RADENCI_SI_100 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0030\U000e0030\U000e007f" - FLAG_FOR_LIMA_REGION_PE_LIM = "\U0001f3f4\U000e0070\U000e0065\U000e006c\U000e0069\U000e006d\U000e007f" - FLAG_FOR_NORTH_CENTRAL_PROVINCE_MV_NC = "\U0001f3f4\U000e006d\U000e0076\U000e006e\U000e0063\U000e007f" - DOCUMENT_WITH_PICTURE = "\U0001f5bb" - FLAG_FOR_VALAIS_CH_VS = "\U0001f3f4\U000e0063\U000e0068\U000e0076\U000e0073\U000e007f" - FLAG_FOR_BAY_OF_PLENTY_NZ_BOP = "\U0001f3f4\U000e006e\U000e007a\U000e0062\U000e006f\U000e0070\U000e007f" - TAG_LATIN_CAPITAL_LETTER_P = "\U000e0050" - FLAG_FOR_BASSE_KOTTO_CF_BK = "\U0001f3f4\U000e0063\U000e0066\U000e0062\U000e006b\U000e007f" - DOCUMENT_WITH_TEXT_AND_PICTURE = "\U0001f5ba" - TAG_DIGIT_NINE = "\U000e0039" - WOMAN_IN_TUXEDO_MEDIUM_DARK_SKIN_TONE = "\U0001f935\U0001f3fe\u200d\u2640\ufe0f" - FLAG_FOR_ZURICH_CH_ZH = "\U0001f3f4\U000e0063\U000e0068\U000e007a\U000e0068\U000e007f" - MAHJONG_TILE_THREE_OF_CIRCLES = "\U0001f01b" - FLAG_FOR_LOG_DRAGOMER_SI_208 = "\U0001f3f4\U000e0073\U000e0069\U000e0032\U000e0030\U000e0038\U000e007f" - MAN_ZOMBIE_DARK_SKIN_TONE = "\U0001f9df\U0001f3ff\u200d\u2642\ufe0f" - FLAG_FOR_ARIZONA_US_AZ = "\U0001f3f4\U000e0075\U000e0073\U000e0061\U000e007a\U000e007f" - FLAG_FOR_PAKTIKA_AF_PKA = "\U0001f3f4\U000e0061\U000e0066\U000e0070\U000e006b\U000e0061\U000e007f" - PROHIBITED_SIGN = "\U0001f6c7" - COUPLE_WITH_HEART_WOMAN_MEDIUM_LIGHT_SKIN_TONE_MAN_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fb" - FLAG_FOR_ALASKA_US_AK = "\U0001f3f4\U000e0075\U000e0073\U000e0061\U000e006b\U000e007f" - FLAG_FOR_SOOL_SO_SO = "\U0001f3f4\U000e0073\U000e006f\U000e0073\U000e006f\U000e007f" - FLAG_FOR_LAGHMAN_AF_LAG = "\U0001f3f4\U000e0061\U000e0066\U000e006c\U000e0061\U000e0067\U000e007f" - BLACK_CIRCLE_WITH_TWO_WHITE_DOTS = "\u2689" - FLAG_FOR_NUKULAELAE_TV_NKL = "\U0001f3f4\U000e0074\U000e0076\U000e006e\U000e006b\U000e006c\U000e007f" - FLAG_FOR_ABKHAZIA_GE_AB = "\U0001f3f4\U000e0067\U000e0065\U000e0061\U000e0062\U000e007f" - FLAG_FOR_ESCH_SUR_ALZETTE_LU_ES = "\U0001f3f4\U000e006c\U000e0075\U000e0065\U000e0073\U000e007f" - FLAG_FOR_MANIPUR_IN_MN = "\U0001f3f4\U000e0069\U000e006e\U000e006d\U000e006e\U000e007f" - FLAG_FOR_HELMAND_AF_HEL = "\U0001f3f4\U000e0061\U000e0066\U000e0068\U000e0065\U000e006c\U000e007f" - FLAG_FOR_VICHADA_CO_VID = "\U0001f3f4\U000e0063\U000e006f\U000e0076\U000e0069\U000e0064\U000e007f" - DOCUMENT = "\U0001f5ce" - FLAG_FOR_ISLAMABAD_PK_IS = "\U0001f3f4\U000e0070\U000e006b\U000e0069\U000e0073\U000e007f" - FAMILY_MAN_LIGHT_SKIN_TONE_MAN_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f468\U0001f3fb\u200d\U0001f466\U0001f3fb" - FLAG_FOR_RAS_AL_KHAIMAH_AE_RK = "\U0001f3f4\U000e0061\U000e0065\U000e0072\U000e006b\U000e007f" - FAMILY_MAN_BABY = "\U0001f468\u200d\U0001f476" - FLAG_FOR_KONYA_TR_42 = "\U0001f3f4\U000e0074\U000e0072\U000e0034\U000e0032\U000e007f" - FLAG_FOR_CANKIRI_TR_18 = "\U0001f3f4\U000e0074\U000e0072\U000e0031\U000e0038\U000e007f" - FLAG_FOR_GANJA_AZ_GA = "\U0001f3f4\U000e0061\U000e007a\U000e0067\U000e0061\U000e007f" - MAHJONG_TILE_SIX_OF_CHARACTERS = "\U0001f00c" - TAG_LATIN_CAPITAL_LETTER_M = "\U000e004d" - FLAG_FOR_OITA_JP_44 = "\U0001f3f4\U000e006a\U000e0070\U000e0034\U000e0034\U000e007f" - FLAG_FOR_GUATEMALA_GT_GU = "\U0001f3f4\U000e0067\U000e0074\U000e0067\U000e0075\U000e007f" - FLAG_FOR_NORTH_BRABANT_NL_NB = "\U0001f3f4\U000e006e\U000e006c\U000e006e\U000e0062\U000e007f" - FAMILY_MAN_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f476\U0001f3fb\u200d\U0001f476\U0001f3fb" - KISS_WOMAN_LIGHT_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fb" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f469\U0001f3fe\u200d\U0001f466\U0001f3fe\u200d\U0001f467\U0001f3fe" - FLAG_FOR_GILBERT_ISLANDS_KI_G = "\U0001f3f4\U000e006b\U000e0069\U000e0067\U000e007f" - FLAG_FOR_PAKTIA_AF_PIA = "\U0001f3f4\U000e0061\U000e0066\U000e0070\U000e0069\U000e0061\U000e007f" - FLAG_FOR_PARDUBICKY_KRAJ_CZ_53 = "\U0001f3f4\U000e0063\U000e007a\U000e0035\U000e0033\U000e007f" - FLAG_FOR_SALCININKAI_LT_42 = "\U0001f3f4\U000e006c\U000e0074\U000e0034\U000e0032\U000e007f" - BALLOT_BOX_WITH_BOLD_CHECK = "\U0001f5f9" - FLAG_FOR_CORUM_TR_19 = "\U0001f3f4\U000e0074\U000e0072\U000e0031\U000e0039\U000e007f" - FLAG_FOR_SAMANGAN_AF_SAM = "\U0001f3f4\U000e0061\U000e0066\U000e0073\U000e0061\U000e006d\U000e007f" - FLAG_FOR_MASAYA_NI_MS = "\U0001f3f4\U000e006e\U000e0069\U000e006d\U000e0073\U000e007f" - FLAG_FOR_CSONGRAD_HU_CS = "\U0001f3f4\U000e0068\U000e0075\U000e0063\U000e0073\U000e007f" - FLAG_FOR_AKSARAY_TR_68 = "\U0001f3f4\U000e0074\U000e0072\U000e0036\U000e0038\U000e007f" - COUPLE_WITH_HEART_WOMAN_MEDIUM_SKIN_TONE_WOMAN_DARK_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3ff" - FLAG_FOR_SAINT_MARY_AG_05 = "\U0001f3f4\U000e0061\U000e0067\U000e0030\U000e0035\U000e007f" - WOMAN_IN_BUSINESS_SUIT_LEVITATING_DARK_SKIN_TONE = "\U0001f574\U0001f3ff\u200d\u2640\ufe0f" - TAG_COMMERCIAL_AT = "\U000e0040" - FLAG_FOR_BERANE_ME_03 = "\U0001f3f4\U000e006d\U000e0065\U000e0030\U000e0033\U000e007f" - FLAG_FOR_MARYLAND_LR_MY = "\U0001f3f4\U000e006c\U000e0072\U000e006d\U000e0079\U000e007f" - FLAG_FOR_KARA_TG_K = "\U0001f3f4\U000e0074\U000e0067\U000e006b\U000e007f" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f467\U0001f3fe\u200d\U0001f466\U0001f3fe" - OPTICAL_DISC_ICON = "\U0001f5b8" - FLAG_FOR_MASCARA_DZ_29 = "\U0001f3f4\U000e0064\U000e007a\U000e0032\U000e0039\U000e007f" - THREE_RAYS_LEFT = "\U0001f5e6" - RECYCLING_SYMBOL_FOR_TYPE_3_PLASTICS = "\u2675" - FLAG_FOR_REDONDA_AG_11 = "\U0001f3f4\U000e0061\U000e0067\U000e0031\U000e0031\U000e007f" - FAX_ICON = "\U0001f5b7" - FLAG_FOR_GAGAUZIA_MD_GA = "\U0001f3f4\U000e006d\U000e0064\U000e0067\U000e0061\U000e007f" - THREE_SPEECH_BUBBLES = "\U0001f5eb" - TAG_LATIN_SMALL_LETTER_S = "\U000e0073" - FLAG_FOR_WAKAYAMA_JP_30 = "\U0001f3f4\U000e006a\U000e0070\U000e0033\U000e0030\U000e007f" - FLAG_FOR_SZEKSZARD_HU_SS = "\U0001f3f4\U000e0068\U000e0075\U000e0073\U000e0073\U000e007f" - FAMILY_WOMAN_LIGHT_SKIN_TONE_MAN_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f468\U0001f3fb\u200d\U0001f466\U0001f3fb\u200d\U0001f466\U0001f3fb" - OVERLAP = "\U0001f5d7" - FLAG_FOR_HSINCHU_COUNTY_TW_HSQ = "\U0001f3f4\U000e0074\U000e0077\U000e0068\U000e0073\U000e0071\U000e007f" - MAHJONG_TILE_EAST_WIND = "\U0001f000" - FLAG_FOR_GOMBE_NG_GO = "\U0001f3f4\U000e006e\U000e0067\U000e0067\U000e006f\U000e007f" - FLAG_FOR_VILNIUS_LT_58 = "\U0001f3f4\U000e006c\U000e0074\U000e0035\U000e0038\U000e007f" - TAG_LATIN_SMALL_LETTER_O = "\U000e006f" - PAGE = "\U0001f5cf" - STOCK_CHART = "\U0001f5e0" - FLAG_FOR_SAINT_JOHN_AG_04 = "\U0001f3f4\U000e0061\U000e0067\U000e0030\U000e0034\U000e007f" - FLAG_FOR_VERMONT_US_VT = "\U0001f3f4\U000e0075\U000e0073\U000e0076\U000e0074\U000e007f" - FLAG_FOR_MURCIA_REGION_ES_MC = "\U0001f3f4\U000e0065\U000e0073\U000e006d\U000e0063\U000e007f" - FLAG_FOR_PWANI_TZ_19 = "\U0001f3f4\U000e0074\U000e007a\U000e0031\U000e0039\U000e007f" - FLAG_FOR_SOMALI_ET_SO = "\U0001f3f4\U000e0065\U000e0074\U000e0073\U000e006f\U000e007f" - MAN_WITH_HEADSCARF_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d5\U0001f3fc\u200d\u2642\ufe0f" - DESKTOP_WINDOW = "\U0001f5d4" - FLAG_FOR_MADONA_LV_059 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0035\U000e0039\U000e007f" - FLAG_FOR_SING_BURI_TH_17 = "\U0001f3f4\U000e0074\U000e0068\U000e0031\U000e0037\U000e007f" - FLAG_FOR_MOKA_MU_MO = "\U0001f3f4\U000e006d\U000e0075\U000e006d\U000e006f\U000e007f" - KISS_MAN_LIGHT_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fe" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f469\U0001f3fc\u200d\U0001f466\U0001f3fc" - BLACK_LEFT_POINTING_BACKHAND_INDEX = "\U0001f59c" - REVERSED_VICTORY_HAND = "\U0001f594" - FAMILY_WOMAN_WOMAN_BABY = "\U0001f469\u200d\U0001f469\u200d\U0001f476" - FLAG_FOR_MICOUD_LC_08 = "\U0001f3f4\U000e006c\U000e0063\U000e0030\U000e0038\U000e007f" - TRIGRAM_FOR_MOUNTAIN = "\u2636" - WOMAN_WITH_HEADSCARF_2 = "\U0001f9d5\u200d\u2640\ufe0f" - FLAG_FOR_MARCHE_IT_57 = "\U0001f3f4\U000e0069\U000e0074\U000e0035\U000e0037\U000e007f" - MAHJONG_TILE_FIVE_OF_CIRCLES = "\U0001f01d" - FLAG_FOR_COVA_LIMA_TL_CO = "\U0001f3f4\U000e0074\U000e006c\U000e0063\U000e006f\U000e007f" - KISS_MAN_MEDIUM_LIGHT_SKIN_TONE_MAN = "\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468" - FAMILY_MAN_DARK_SKIN_TONE_MAN_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f468\U0001f3ff\u200d\U0001f466\U0001f3ff\u200d\U0001f476\U0001f3ff" - FLAG_FOR_BEL_AIR_SC_09 = "\U0001f3f4\U000e0073\U000e0063\U000e0030\U000e0039\U000e007f" - FLAG_FOR_VILA_REAL_PT_17 = "\U0001f3f4\U000e0070\U000e0074\U000e0031\U000e0037\U000e007f" - FLAG_FOR_MACAU_SAR_CHINA_CN_92 = "\U0001f3f4\U000e0063\U000e006e\U000e0039\U000e0032\U000e007f" - FLAG_FOR_SOUTH_DAKOTA_US_SD = "\U0001f3f4\U000e0075\U000e0073\U000e0073\U000e0064\U000e007f" - FLAG_FOR_WEST_PANAMA_PA_10 = "\U0001f3f4\U000e0070\U000e0061\U000e0031\U000e0030\U000e007f" - FLAG_FOR_CHIAPAS_MX_CHP = "\U0001f3f4\U000e006d\U000e0078\U000e0063\U000e0068\U000e0070\U000e007f" - FLAG_FOR_KUKES_COUNTY_AL_07 = "\U0001f3f4\U000e0061\U000e006c\U000e0030\U000e0037\U000e007f" - PAGES = "\U0001f5d0" - TAG_LATIN_CAPITAL_LETTER_L = "\U000e004c" - EMPTY_PAGES = "\U0001f5cd" - FLAG_FOR_SVETA_ANA_SI_181 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0038\U000e0031\U000e007f" - FLAG_FOR_SKRIVERI_LV_092 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0039\U000e0032\U000e007f" - FLAG_FOR_KITUI_KE_18 = "\U0001f3f4\U000e006b\U000e0065\U000e0031\U000e0038\U000e007f" - FIRST_QUARTER_MOON_2 = "\u263d" - FLAG_FOR_PELELIU_PW_350 = "\U0001f3f4\U000e0070\U000e0077\U000e0033\U000e0035\U000e0030\U000e007f" - FLAG_FOR_MARY_TM_M = "\U0001f3f4\U000e0074\U000e006d\U000e006d\U000e007f" - FLAG_FOR_UPPER_SOUTH_PROVINCE_MV_US = "\U0001f3f4\U000e006d\U000e0076\U000e0075\U000e0073\U000e007f" - FLAG_FOR_MAKKAH_SA_02 = "\U0001f3f4\U000e0073\U000e0061\U000e0030\U000e0032\U000e007f" - FLAG_FOR_ZEBBUG_GOZO_MT_65 = "\U0001f3f4\U000e006d\U000e0074\U000e0036\U000e0035\U000e007f" - FLAG_FOR_OSMANIYE_TR_80 = "\U0001f3f4\U000e0074\U000e0072\U000e0038\U000e0030\U000e007f" - BLACK_TWO_WAY_LEFT_WAY_TRAFFIC = "\u26d6" - FLAG_FOR_LAGOS_NG_LA = "\U0001f3f4\U000e006e\U000e0067\U000e006c\U000e0061\U000e007f" - MAHJONG_TILE_JOKER = "\U0001f02a" - FLAG_FOR_KARS_TR_36 = "\U0001f3f4\U000e0074\U000e0072\U000e0033\U000e0036\U000e007f" - FLAG_FOR_NAYARIT_MX_NAY = "\U0001f3f4\U000e006d\U000e0078\U000e006e\U000e0061\U000e0079\U000e007f" - CUP_ON_BLACK_SQUARE = "\u26fe" - FLAG_FOR_RIVERCESS_LR_RI = "\U0001f3f4\U000e006c\U000e0072\U000e0072\U000e0069\U000e007f" - THREE_RAYS_ABOVE = "\U0001f5e4" - FLAG_FOR_UABOE_NR_13 = "\U0001f3f4\U000e006e\U000e0072\U000e0031\U000e0033\U000e007f" - TAG_LATIN_SMALL_LETTER_P = "\U000e0070" - FLAG_FOR_MGARR_MT_31 = "\U0001f3f4\U000e006d\U000e0074\U000e0033\U000e0031\U000e007f" - FLAG_FOR_SALAVAN_LA_SL = "\U0001f3f4\U000e006c\U000e0061\U000e0073\U000e006c\U000e007f" - FLAG_FOR_AJMAN_AE_AJ = "\U0001f3f4\U000e0061\U000e0065\U000e0061\U000e006a\U000e007f" - FLAG_FOR_DEMIR_KAPIJA_MK_24 = "\U0001f3f4\U000e006d\U000e006b\U000e0032\U000e0034\U000e007f" - FLAG_FOR_KHABAROVSK_KRAI_RU_KHA = "\U0001f3f4\U000e0072\U000e0075\U000e006b\U000e0068\U000e0061\U000e007f" - COUPLE_WITH_HEART_MAN_LIGHT_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fc" - FLAG_FOR_WEST_KAZAKHSTAN_KZ_ZAP = "\U0001f3f4\U000e006b\U000e007a\U000e007a\U000e0061\U000e0070\U000e007f" - TAG_LATIN_SMALL_LETTER_W = "\U000e0077" - ADI_SHAKTI = "\u262c" - BOYS_SYMBOL = "\U0001f6c9" - FLAG_FOR_NORTHERN_BAHR_EL_GHAZAL_SS_BN = "\U0001f3f4\U000e0073\U000e0073\U000e0062\U000e006e\U000e007f" - FAMILY_MAN_DARK_SKIN_TONE_MAN_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f468\U0001f3ff\u200d\U0001f476\U0001f3ff\u200d\U0001f467\U0001f3ff" - FLAG_FOR_LINDI_TZ_12 = "\U0001f3f4\U000e0074\U000e007a\U000e0031\U000e0032\U000e007f" - FLAG_FOR_KUNAR_AF_KNR = "\U0001f3f4\U000e0061\U000e0066\U000e006b\U000e006e\U000e0072\U000e007f" - FOLDER = "\U0001f5c0" - COUPLE_WITH_HEART_MAN_MAN_LIGHT_SKIN_TONE = "\U0001f468\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fb" - TAG_NUMBER_SIGN = "\U000e0023" - FLAG_FOR_VALENCIAN_COMMUNITY_ES_VC = "\U0001f3f4\U000e0065\U000e0073\U000e0076\U000e0063\U000e007f" - TAG_RIGHT_CURLY_BRACKET = "\U000e007d" - FLAG_FOR_SAINT_PAUL_AG_06 = "\U0001f3f4\U000e0061\U000e0067\U000e0030\U000e0036\U000e007f" - WOMAN_IN_TUXEDO = "\U0001f935\u200d\u2640\ufe0f" - FLAG_FOR_SICILY_IT_82 = "\U0001f3f4\U000e0069\U000e0074\U000e0038\U000e0032\U000e007f" - FLAG_FOR_LEZHE_COUNTY_AL_08 = "\U0001f3f4\U000e0061\U000e006c\U000e0030\U000e0038\U000e007f" - FLAG_FOR_CHIHUAHUA_MX_CHH = "\U0001f3f4\U000e006d\U000e0078\U000e0063\U000e0068\U000e0068\U000e007f" - BLACK_RIGHT_POINTING_BACKHAND_INDEX = "\U0001f59d" - WHITE_LATIN_CROSS = "\U0001f546" - FLAG_FOR_NORD_OUEST_HT_NO = "\U0001f3f4\U000e0068\U000e0074\U000e006e\U000e006f\U000e007f" - DIGIT_ONE = "1\ufe0f" - PORTABLE_STEREO = "\U0001f4fe" - FLAG_FOR_MADINAT_ASH_SHAMAL_QA_MS = "\U0001f3f4\U000e0071\U000e0061\U000e006d\U000e0073\U000e007f" - BULLHORN = "\U0001f56b" - TAG_DIGIT_SIX = "\U000e0036" - FLAG_FOR_SIKKIM_IN_SK = "\U0001f3f4\U000e0069\U000e006e\U000e0073\U000e006b\U000e007f" - CANCELLATION_X = "\U0001f5d9" - LEFT_WRITING_HAND = "\U0001f58e" - MAHJONG_TILE_FOUR_OF_CHARACTERS = "\U0001f00a" - RINGING_BELL = "\U0001f56d" - FLAG_FOR_TIRANA_COUNTY_AL_11 = "\U0001f3f4\U000e0061\U000e006c\U000e0031\U000e0031\U000e007f" - FLAG_FOR_TRAT_TH_23 = "\U0001f3f4\U000e0074\U000e0068\U000e0032\U000e0033\U000e007f" - TAG_LATIN_SMALL_LETTER_D = "\U000e0064" - FLAG_FOR_SAMARA_RU_SAM = "\U0001f3f4\U000e0072\U000e0075\U000e0073\U000e0061\U000e006d\U000e007f" - FLAG_FOR_NGOUNIE_GA_4 = "\U0001f3f4\U000e0067\U000e0061\U000e0034\U000e007f" - BLACK_ROSETTE = "\U0001f3f6" - TAG_CIRCUMFLEX_ACCENT = "\U000e005e" - FLAG_FOR_IASI_RO_IS = "\U0001f3f4\U000e0072\U000e006f\U000e0069\U000e0073\U000e007f" - FLAG_FOR_GEORGIA_US_GA = "\U0001f3f4\U000e0075\U000e0073\U000e0067\U000e0061\U000e007f" - FLAG_FOR_DURRES_COUNTY_AL_02 = "\U0001f3f4\U000e0061\U000e006c\U000e0030\U000e0032\U000e007f" - KISS_MAN_LIGHT_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fd" - FLAG_FOR_TAZA_AL_HOCEIMA_TAOUNATE_MA_03 = "\U0001f3f4\U000e006d\U000e0061\U000e0030\U000e0033\U000e007f" - FLAG_FOR_LOMBARDY_IT_25 = "\U0001f3f4\U000e0069\U000e0074\U000e0032\U000e0035\U000e007f" - FLAG_FOR_TRABZON_TR_61 = "\U0001f3f4\U000e0074\U000e0072\U000e0036\U000e0031\U000e007f" - FLAG_FOR_UROZGAN_AF_URU = "\U0001f3f4\U000e0061\U000e0066\U000e0075\U000e0072\U000e0075\U000e007f" - FLAG_FOR_CENTRAL_GH_CP = "\U0001f3f4\U000e0067\U000e0068\U000e0063\U000e0070\U000e007f" - WOMAN_ZOMBIE_LIGHT_SKIN_TONE = "\U0001f9df\U0001f3fb\u200d\u2640\ufe0f" - MAHJONG_TILE_SUMMER = "\U0001f027" - FLAG_FOR_KIDRICEVO_SI_045 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0034\U000e0035\U000e007f" - FLAG_FOR_RAZAVI_KHORASAN_IR_30 = "\U0001f3f4\U000e0069\U000e0072\U000e0033\U000e0030\U000e007f" - FLAG_FOR_KOTAYK_AM_KT = "\U0001f3f4\U000e0061\U000e006d\U000e006b\U000e0074\U000e007f" - TELEPHONE_RECEIVER_WITH_PAGE = "\U0001f57c" - FLAG_FOR_SHKODER_COUNTY_AL_10 = "\U0001f3f4\U000e0061\U000e006c\U000e0031\U000e0030\U000e007f" - FLAG_FOR_AL_WAKRAH_QA_WA = "\U0001f3f4\U000e0071\U000e0061\U000e0077\U000e0061\U000e007f" - TAG_LATIN_SMALL_LETTER_U = "\U000e0075" - FLAG_FOR_CHACO_AR_H = "\U0001f3f4\U000e0061\U000e0072\U000e0068\U000e007f" - BLACK_HARD_SHELL_FLOPPY_DISK = "\U0001f5aa" - FAMILY_MAN_LIGHT_SKIN_TONE_MAN_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f468\U0001f3fb\u200d\U0001f476\U0001f3fb\u200d\U0001f466\U0001f3fb" - FLAG_FOR_HOKKAIDO_JP_01 = "\U0001f3f4\U000e006a\U000e0070\U000e0030\U000e0031\U000e007f" - FLAG_FOR_GEGHARKUNIK_AM_GR = "\U0001f3f4\U000e0061\U000e006d\U000e0067\U000e0072\U000e007f" - FLAG_FOR_BLACK_POINT_BS_BP = "\U0001f3f4\U000e0062\U000e0073\U000e0062\U000e0070\U000e007f" - FLAG_FOR_LUXOR_EG_LX = "\U0001f3f4\U000e0065\U000e0067\U000e006c\U000e0078\U000e007f" - FLAG_FOR_WISCONSIN_US_WI = "\U0001f3f4\U000e0075\U000e0073\U000e0077\U000e0069\U000e007f" - FLAG_FOR_MONACO_VILLE_MC_MO = "\U0001f3f4\U000e006d\U000e0063\U000e006d\U000e006f\U000e007f" - FLAG_FOR_ARMAVIR_AM_AV = "\U0001f3f4\U000e0061\U000e006d\U000e0061\U000e0076\U000e007f" - MAHJONG_TILE_EIGHT_OF_BAMBOOS = "\U0001f017" - TRIGRAM_FOR_EARTH = "\u2637" - FLAG_FOR_THUA_THIEN_HUE_VN_26 = "\U0001f3f4\U000e0076\U000e006e\U000e0032\U000e0036\U000e007f" - FLAG_FOR_POINTE_NOIRE_CG_16 = "\U0001f3f4\U000e0063\U000e0067\U000e0031\U000e0036\U000e007f" - FLAG_FOR_SAKHALIN_RU_SAK = "\U0001f3f4\U000e0072\U000e0075\U000e0073\U000e0061\U000e006b\U000e007f" - TAG_LATIN_SMALL_LETTER_Z = "\U000e007a" - FLAG_FOR_PAKRUOJIS_LT_30 = "\U0001f3f4\U000e006c\U000e0074\U000e0033\U000e0030\U000e007f" - FLAG_FOR_APE_LV_009 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0030\U000e0039\U000e007f" - LOWER_RIGHT_SHADOWED_WHITE_CIRCLE = "\U0001f53e" - FLAG_FOR_ARUBA_NL_AW = "\U0001f3f4\U000e006e\U000e006c\U000e0061\U000e0077\U000e007f" - COUPLE_WITH_HEART_WOMAN_DARK_SKIN_TONE_WOMAN_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3ff" - FLAG_FOR_KIEV_UA_30 = "\U0001f3f4\U000e0075\U000e0061\U000e0033\U000e0030\U000e007f" - FLAG_FOR_PORT_SAID_EG_PTS = "\U0001f3f4\U000e0065\U000e0067\U000e0070\U000e0074\U000e0073\U000e007f" - MAXIMIZE = "\U0001f5d6" - FLAG_FOR_TRA_VINH_VN_51 = "\U0001f3f4\U000e0076\U000e006e\U000e0035\U000e0031\U000e007f" - FLAG_FOR_RIYADH_SA_01 = "\U0001f3f4\U000e0073\U000e0061\U000e0030\U000e0031\U000e007f" - GIRLS_SYMBOL = "\U0001f6ca" - FLAG_FOR_SKIKDA_DZ_21 = "\U0001f3f4\U000e0064\U000e007a\U000e0032\U000e0031\U000e007f" - FLAG_FOR_ABU_DHABI_AE_AZ = "\U0001f3f4\U000e0061\U000e0065\U000e0061\U000e007a\U000e007f" - MAHJONG_TILE_ONE_OF_CIRCLES = "\U0001f019" - BLACK_DOWN_POINTING_BACKHAND_INDEX = "\U0001f5a3" - MAHJONG_TILE_NINE_OF_CIRCLES = "\U0001f021" - FLAG_FOR_RIGA_LV_RIX = "\U0001f3f4\U000e006c\U000e0076\U000e0072\U000e0069\U000e0078\U000e007f" - FLAG_FOR_MAKIRA_ULAWA_SB_MK = "\U0001f3f4\U000e0073\U000e0062\U000e006d\U000e006b\U000e007f" - FLAG_FOR_JUTIAPA_GT_JU = "\U0001f3f4\U000e0067\U000e0074\U000e006a\U000e0075\U000e007f" - FLAG_FOR_LORESTAN_IR_20 = "\U0001f3f4\U000e0069\U000e0072\U000e0032\U000e0030\U000e007f" - FLAG_FOR_BASHKORTOSTAN_RU_BA = "\U0001f3f4\U000e0072\U000e0075\U000e0062\U000e0061\U000e007f" - FLAG_FOR_BUSAN_KR_26 = "\U0001f3f4\U000e006b\U000e0072\U000e0032\U000e0036\U000e007f" - FLAG_FOR_MWARO_BI_MW = "\U0001f3f4\U000e0062\U000e0069\U000e006d\U000e0077\U000e007f" - FLAG_FOR_ASTURIAS_ES_AS = "\U0001f3f4\U000e0065\U000e0073\U000e0061\U000e0073\U000e007f" - FLAG_FOR_ALTA_VERAPAZ_GT_AV = "\U0001f3f4\U000e0067\U000e0074\U000e0061\U000e0076\U000e007f" - FLAG_FOR_BRUNEI_MUARA_BN_BM = "\U0001f3f4\U000e0062\U000e006e\U000e0062\U000e006d\U000e007f" - FLAG_FOR_LOVRENC_NA_POHORJU_SI_167 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0036\U000e0037\U000e007f" - FLAG_FOR_WALLIS_ANDAMP_FUTUNA_FR_WF = "\U0001f3f4\U000e0066\U000e0072\U000e0077\U000e0066\U000e007f" - KISS_MAN_WOMAN_DARK_SKIN_TONE = "\U0001f468\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3ff" - FLAG_FOR_LORI_AM_LO = "\U0001f3f4\U000e0061\U000e006d\U000e006c\U000e006f\U000e007f" - KISS_MAN_DARK_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fb" - FLAG_FOR_GIZA_EG_GZ = "\U0001f3f4\U000e0065\U000e0067\U000e0067\U000e007a\U000e007f" - FLAG_FOR_SAINT_MARY_JM_05 = "\U0001f3f4\U000e006a\U000e006d\U000e0030\U000e0035\U000e007f" - DIGIT_FIVE = "5\ufe0f" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f469\U0001f3fc\u200d\U0001f467\U0001f3fc" - FLAG_FOR_MAGADAN_RU_MAG = "\U0001f3f4\U000e0072\U000e0075\U000e006d\U000e0061\U000e0067\U000e007f" - LEFT_ANGER_BUBBLE = "\U0001f5ee" - DIGIT_EIGHT = "8\ufe0f" - SIDEWAYS_BLACK_UP_POINTING_INDEX = "\U0001f5a0" - FLAG_FOR_SOUTHERN_HIGHLANDS_PG_SHM = "\U0001f3f4\U000e0070\U000e0067\U000e0073\U000e0068\U000e006d\U000e007f" - FLAG_FOR_NURISTAN_AF_NUR = "\U0001f3f4\U000e0061\U000e0066\U000e006e\U000e0075\U000e0072\U000e007f" - FLAG_FOR_LOUISIANA_US_LA = "\U0001f3f4\U000e0075\U000e0073\U000e006c\U000e0061\U000e007f" - FLAG_FOR_MAULE_CL_ML = "\U0001f3f4\U000e0063\U000e006c\U000e006d\U000e006c\U000e007f" - FAMILY_MAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f469\U0001f3fd\u200d\U0001f466\U0001f3fd\u200d\U0001f466\U0001f3fd" - FAMILY_WOMAN_LIGHT_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f469\U0001f3fb\u200d\U0001f466\U0001f3fb\u200d\U0001f476\U0001f3fb" - FLAG_FOR_PANAMA_PA_8 = "\U0001f3f4\U000e0070\U000e0061\U000e0038\U000e007f" - FLAG_FOR_IZMIR_TR_35 = "\U0001f3f4\U000e0074\U000e0072\U000e0033\U000e0035\U000e007f" - FLAG_FOR_CABINDA_AO_CAB = "\U0001f3f4\U000e0061\U000e006f\U000e0063\U000e0061\U000e0062\U000e007f" - OPEN_FOLDER = "\U0001f5c1" - FLAG_FOR_VAUD_CH_VD = "\U0001f3f4\U000e0063\U000e0068\U000e0076\U000e0064\U000e007f" - LIGHTNING_MOOD = "\U0001f5f2" - KISS_WOMAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fc" - FLAG_FOR_SUCRE_CO_SUC = "\U0001f3f4\U000e0063\U000e006f\U000e0073\U000e0075\U000e0063\U000e007f" - MAHJONG_TILE_WHITE_DRAGON = "\U0001f006" - FLAG_FOR_MADRE_DE_DIOS_PE_MDD = "\U0001f3f4\U000e0070\U000e0065\U000e006d\U000e0064\U000e0064\U000e007f" - FLAG_FOR_BENGUELA_AO_BGU = "\U0001f3f4\U000e0061\U000e006f\U000e0062\U000e0067\U000e0075\U000e007f" - FLAG_FOR_ST_JULIAN_S_MT_48 = "\U0001f3f4\U000e006d\U000e0074\U000e0034\U000e0038\U000e007f" - FLAG_FOR_TIMIS_RO_TM = "\U0001f3f4\U000e0072\U000e006f\U000e0074\U000e006d\U000e007f" - FAMILY_MAN_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f467\U0001f3fd\u200d\U0001f476\U0001f3fd" - FLAG_FOR_BIE_AO_BIE = "\U0001f3f4\U000e0061\U000e006f\U000e0062\U000e0069\U000e0065\U000e007f" - MAHJONG_TILE_WINTER = "\U0001f029" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f469\U0001f3fe\u200d\U0001f476\U0001f3fe" - FLAG_FOR_BLUE_NILE_SD_NB = "\U0001f3f4\U000e0073\U000e0064\U000e006e\U000e0062\U000e007f" - EMPTY_DOCUMENT = "\U0001f5cb" - MINIMIZE = "\U0001f5d5" - FLAG_FOR_KWALE_KE_19 = "\U0001f3f4\U000e006b\U000e0065\U000e0031\U000e0039\U000e007f" - FLAG_FOR_GRAND_PORT_MU_GP = "\U0001f3f4\U000e006d\U000e0075\U000e0067\U000e0070\U000e007f" - FLAG_FOR_CHIMBORAZO_EC_H = "\U0001f3f4\U000e0065\U000e0063\U000e0068\U000e007f" - FLAG_FOR_HUAMBO_AO_HUA = "\U0001f3f4\U000e0061\U000e006f\U000e0068\U000e0075\U000e0061\U000e007f" - FLAG_FOR_CUANZA_SUL_AO_CUS = "\U0001f3f4\U000e0061\U000e006f\U000e0063\U000e0075\U000e0073\U000e007f" - FLAG_FOR_BREMEN_DE_HB = "\U0001f3f4\U000e0064\U000e0065\U000e0068\U000e0062\U000e007f" - LIPS = "\U0001f5e2" - FLAG_FOR_ALTO_PARAGUAY_PY_16 = "\U0001f3f4\U000e0070\U000e0079\U000e0031\U000e0036\U000e007f" - FLAG_FOR_NEW_YORK_US_NY = "\U0001f3f4\U000e0075\U000e0073\U000e006e\U000e0079\U000e007f" - KISS_WOMAN_MEDIUM_LIGHT_SKIN_TONE_MAN_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fb" - FLAG_FOR_NORTH_CAROLINA_US_NC = "\U0001f3f4\U000e0075\U000e0073\U000e006e\U000e0063\U000e007f" - COUPLE_WITH_HEART_WOMAN_DARK_SKIN_TONE_MAN_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3ff" - WHITE_CIRCLE_WITH_TWO_DOTS = "\u2687" - KISS_MAN_MEDIUM_SKIN_TONE_MAN = "\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468" - MAN_ZOMBIE_LIGHT_SKIN_TONE = "\U0001f9df\U0001f3fb\u200d\u2642\ufe0f" - FLAG_FOR_PERAK_MY_08 = "\U0001f3f4\U000e006d\U000e0079\U000e0030\U000e0038\U000e007f" - FLAG_FOR_SISAK_MOSLAVINA_HR_03 = "\U0001f3f4\U000e0068\U000e0072\U000e0030\U000e0033\U000e007f" - FLAG_FOR_ARDABIL_IR_03 = "\U0001f3f4\U000e0069\U000e0072\U000e0030\U000e0033\U000e007f" - FLAG_FOR_KARNATAKA_IN_KA = "\U0001f3f4\U000e0069\U000e006e\U000e006b\U000e0061\U000e007f" - LEFT_HAND_TELEPHONE_RECEIVER = "\U0001f57b" - FLAG_FOR_NEVADA_US_NV = "\U0001f3f4\U000e0075\U000e0073\U000e006e\U000e0076\U000e007f" - FLAG_FOR_DIBER_COUNTY_AL_09 = "\U0001f3f4\U000e0061\U000e006c\U000e0030\U000e0039\U000e007f" - FLAG_FOR_LUNDA_SUL_AO_LSU = "\U0001f3f4\U000e0061\U000e006f\U000e006c\U000e0073\U000e0075\U000e007f" - KISS_WOMAN_WOMAN_LIGHT_SKIN_TONE = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fb" - SIDEWAYS_WHITE_DOWN_POINTING_INDEX = "\U0001f59f" - FLAG_FOR_NAKHON_SI_THAMMARAT_TH_80 = "\U0001f3f4\U000e0074\U000e0068\U000e0038\U000e0030\U000e007f" - FLAG_FOR_KYRENIA_CY_06 = "\U0001f3f4\U000e0063\U000e0079\U000e0030\U000e0036\U000e007f" - FLAG_FOR_GUNA_YALA_PA_KY = "\U0001f3f4\U000e0070\U000e0061\U000e006b\U000e0079\U000e007f" - FLAG_FOR_HUILA_AO_HUI = "\U0001f3f4\U000e0061\U000e006f\U000e0068\U000e0075\U000e0069\U000e007f" - FLAG_FOR_NAAMA_DZ_45 = "\U0001f3f4\U000e0064\U000e007a\U000e0034\U000e0035\U000e007f" - FLAG_FOR_SAR_E_POL_AF_SAR = "\U0001f3f4\U000e0061\U000e0066\U000e0073\U000e0061\U000e0072\U000e007f" - COUPLE_WITH_HEART_WOMAN_LIGHT_SKIN_TONE_MAN_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fd" - FLAG_FOR_META_CO_MET = "\U0001f3f4\U000e0063\U000e006f\U000e006d\U000e0065\U000e0074\U000e007f" - FLAG_FOR_NORTHWEST_TERRITORIES_CA_NT = "\U0001f3f4\U000e0063\U000e0061\U000e006e\U000e0074\U000e007f" - FLAG_FOR_PENGHU_TW_PEN = "\U0001f3f4\U000e0074\U000e0077\U000e0070\U000e0065\U000e006e\U000e007f" - FLAG_FOR_TOKYO_JP_13 = "\U0001f3f4\U000e006a\U000e0070\U000e0031\U000e0033\U000e007f" - COUPLE_WITH_HEART_WOMAN_DARK_SKIN_TONE_MAN_LIGHT_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fb" - TAG_LATIN_SMALL_LETTER_X = "\U000e0078" - FLAG_FOR_SAINT_GEORGE_AG_03 = "\U0001f3f4\U000e0061\U000e0067\U000e0030\U000e0033\U000e007f" - REVERSED_RAISED_HAND_WITH_FINGERS_SPLAYED = "\U0001f591" - JAPANESE_BANK_SYMBOL = "\u26fb" - FLAG_FOR_CUANZA_NORTE_AO_CNO = "\U0001f3f4\U000e0061\U000e006f\U000e0063\U000e006e\U000e006f\U000e007f" - FLAG_FOR_NANA_GREBIZI_CF_KB = "\U0001f3f4\U000e0063\U000e0066\U000e006b\U000e0062\U000e007f" - FLAG_FOR_MALANJE_AO_MAL = "\U0001f3f4\U000e0061\U000e006f\U000e006d\U000e0061\U000e006c\U000e007f" - ROTATED_HEAVY_BLACK_HEART_BULLET = "\u2765" - REVERSED_THUMBS_UP_SIGN = "\U0001f592" - CELTIC_CROSS = "\U0001f548" - KISS_WOMAN_WOMAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fc" - SYMBOL_FOR_MARKS_CHAPTER = "\U0001f545" - TAG_LATIN_SMALL_LETTER_E = "\U000e0065" - FLAG_FOR_NIGER_NG_NI = "\U0001f3f4\U000e006e\U000e0067\U000e006e\U000e0069\U000e007f" - TAG_DIGIT_EIGHT = "\U000e0038" - MAHJONG_TILE_ORCHID = "\U0001f023" - TAG_LATIN_CAPITAL_LETTER_B = "\U000e0042" - COUPLE_WITH_HEART_MAN_MEDIUM_DARK_SKIN_TONE_MAN_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fb" - FLAG_FOR_BEIRUT_LB_BA = "\U0001f3f4\U000e006c\U000e0062\U000e0062\U000e0061\U000e007f" - BLACK_DROPLET = "\U0001f322" - FLAG_FOR_KALBAJAR_AZ_KAL = "\U0001f3f4\U000e0061\U000e007a\U000e006b\U000e0061\U000e006c\U000e007f" - FLAG_FOR_BINH_DUONG_VN_57 = "\U0001f3f4\U000e0076\U000e006e\U000e0035\U000e0037\U000e007f" - FLAG_FOR_NAMIBE_AO_NAM = "\U0001f3f4\U000e0061\U000e006f\U000e006e\U000e0061\U000e006d\U000e007f" - FLAG_FOR_CALARASI_RO_CL = "\U0001f3f4\U000e0072\U000e006f\U000e0063\U000e006c\U000e007f" - CIRCLED_CROSS_POMMEE = "\U0001f540" - FLAG_FOR_A_ANA_WS_AA = "\U0001f3f4\U000e0077\U000e0073\U000e0061\U000e0061\U000e007f" - FAMILY_WOMAN_LIGHT_SKIN_TONE_MAN_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f468\U0001f3fb\u200d\U0001f476\U0001f3fb\u200d\U0001f467\U0001f3fb" - BALLOT_SCRIPT_X = "\U0001f5f4" - MAHJONG_TILE_WEST_WIND = "\U0001f002" - MAHJONG_TILE_EIGHT_OF_CIRCLES = "\U0001f020" - FLAG_FOR_MOXICO_AO_MOX = "\U0001f3f4\U000e0061\U000e006f\U000e006d\U000e006f\U000e0078\U000e007f" - EMPTY_NOTE = "\U0001f5c5" - FLAG_FOR_MISSOURI_US_MO = "\U0001f3f4\U000e0075\U000e0073\U000e006d\U000e006f\U000e007f" - FLAG_FOR_PENANG_MY_07 = "\U0001f3f4\U000e006d\U000e0079\U000e0030\U000e0037\U000e007f" - FLAG_FOR_WAJIR_KE_46 = "\U0001f3f4\U000e006b\U000e0065\U000e0034\U000e0036\U000e007f" - FLAG_FOR_CANILLO_AD_02 = "\U0001f3f4\U000e0061\U000e0064\U000e0030\U000e0032\U000e007f" - FLAG_FOR_VIRGINIA_US_VA = "\U0001f3f4\U000e0075\U000e0073\U000e0076\U000e0061\U000e007f" - CROSS_POMMEE = "\U0001f542" - FLAG_FOR_FIER_COUNTY_AL_04 = "\U0001f3f4\U000e0061\U000e006c\U000e0030\U000e0034\U000e007f" - FLAG_FOR_SCHLESWIG_HOLSTEIN_DE_SH = "\U0001f3f4\U000e0064\U000e0065\U000e0073\U000e0068\U000e007f" - COUPLE_WITH_HEART_MAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fe" - FLAG_FOR_LUALABA_CD_LU = "\U0001f3f4\U000e0063\U000e0064\U000e006c\U000e0075\U000e007f" - NORTHEAST_POINTING_AIRPLANE = "\U0001f6ea" - FAMILY_WOMAN_WOMAN_GIRL_BABY = "\U0001f469\u200d\U0001f469\u200d\U0001f467\u200d\U0001f476" - MAHJONG_TILE_SEVEN_OF_BAMBOOS = "\U0001f016" - FLAG_FOR_AGSTAFA_AZ_AGA = "\U0001f3f4\U000e0061\U000e007a\U000e0061\U000e0067\U000e0061\U000e007f" - FLAG_FOR_AMNAT_CHAROEN_TH_37 = "\U0001f3f4\U000e0074\U000e0068\U000e0033\U000e0037\U000e007f" - FLAG_FOR_QUANG_TRI_VN_25 = "\U0001f3f4\U000e0076\U000e006e\U000e0032\U000e0035\U000e007f" - STAMPED_ENVELOPE = "\U0001f583" - BEATS_1_LOGO = "\uf79c" - FLAG_FOR_AFYONKARAHISAR_TR_03 = "\U0001f3f4\U000e0074\U000e0072\U000e0030\U000e0033\U000e007f" - FLAG_FOR_MASSACHUSETTS_US_MA = "\U0001f3f4\U000e0075\U000e0073\U000e006d\U000e0061\U000e007f" - FLAG_FOR_GULF_PG_GPK = "\U0001f3f4\U000e0070\U000e0067\U000e0067\U000e0070\U000e006b\U000e007f" - FLAG_FOR_UIGE_AO_UIG = "\U0001f3f4\U000e0061\U000e006f\U000e0075\U000e0069\U000e0067\U000e007f" - KISS_MAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fd" - FLAG_FOR_SAINT_KITTS_KN_K = "\U0001f3f4\U000e006b\U000e006e\U000e006b\U000e007f" - FLAG_FOR_CUANDO_CUBANGO_AO_CCU = "\U0001f3f4\U000e0061\U000e006f\U000e0063\U000e0063\U000e0075\U000e007f" - KISS_WOMAN_MEDIUM_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fb" - FLAG_FOR_SAN_LUIS_AR_D = "\U0001f3f4\U000e0061\U000e0072\U000e0064\U000e007f" - FLAG_FOR_ZHYTOMYRSHCHYNA_UA_18 = "\U0001f3f4\U000e0075\U000e0061\U000e0031\U000e0038\U000e007f" - MAHJONG_TILE_TWO_OF_BAMBOOS = "\U0001f011" - FLAG_FOR_SAINT_THOMAS_JM_03 = "\U0001f3f4\U000e006a\U000e006d\U000e0030\U000e0033\U000e007f" - TAG_LATIN_SMALL_LETTER_I = "\U000e0069" - VESTA = "\u26b6" - TAG_LATIN_SMALL_LETTER_Y = "\U000e0079" - TAG_LATIN_SMALL_LETTER_Q = "\U000e0071" - SIDEWAYS_WHITE_LEFT_POINTING_INDEX = "\U0001f598" - FLAG_FOR_IDAHO_US_ID = "\U0001f3f4\U000e0075\U000e0073\U000e0069\U000e0064\U000e007f" - TAG_LATIN_CAPITAL_LETTER_Z = "\U000e005a" - FLAG_FOR_SYUNIK_AM_SU = "\U0001f3f4\U000e0061\U000e006d\U000e0073\U000e0075\U000e007f" - FLAG_FOR_EAST_KAZAKHSTAN_KZ_VOS = "\U0001f3f4\U000e006b\U000e007a\U000e0076\U000e006f\U000e0073\U000e007f" - FLAG_FOR_JAMTLAND_SE_Z = "\U0001f3f4\U000e0073\U000e0065\U000e007a\U000e007f" - WOMAN_ZOMBIE_MEDIUM_DARK_SKIN_TONE = "\U0001f9df\U0001f3fe\u200d\u2640\ufe0f" - FLAG_FOR_NEVSEHIR_TR_50 = "\U0001f3f4\U000e0074\U000e0072\U000e0035\U000e0030\U000e007f" - FLAG_FOR_SKANE_SE_M = "\U0001f3f4\U000e0073\U000e0065\U000e006d\U000e007f" - KISS_WOMAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fc" - FRAME_WITH_AN_X = "\U0001f5be" - FLAG_FOR_SANTIAGO_DEL_ESTERO_AR_G = "\U0001f3f4\U000e0061\U000e0072\U000e0067\U000e007f" - FLAG_FOR_LA_MASSANA_AD_04 = "\U0001f3f4\U000e0061\U000e0064\U000e0030\U000e0034\U000e007f" - FLAG_FOR_STAVROPOL_KRAI_RU_STA = "\U0001f3f4\U000e0072\U000e0075\U000e0073\U000e0074\U000e0061\U000e007f" - FAMILY_MAN_BABY_GIRL = "\U0001f468\u200d\U0001f476\u200d\U0001f467" - NEUTER = "\u26b2" - FLAG_FOR_TRIPURA_IN_TR = "\U0001f3f4\U000e0069\U000e006e\U000e0074\U000e0072\U000e007f" - FAMILY_WOMAN_DARK_SKIN_TONE_MAN_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f468\U0001f3ff\u200d\U0001f476\U0001f3ff\u200d\U0001f467\U0001f3ff" - FLAG_FOR_ENUGU_NG_EN = "\U0001f3f4\U000e006e\U000e0067\U000e0065\U000e006e\U000e007f" - FLAG_FOR_EL_ORO_EC_O = "\U0001f3f4\U000e0065\U000e0063\U000e006f\U000e007f" - SQUARED_KEY = "\u26bf" - MAHJONG_TILE_NINE_OF_BAMBOOS = "\U0001f018" - PRINTER_ICON = "\U0001f5b6" - SESQUIQUADRATE = "\u26bc" - FAMILY_WOMAN_DARK_SKIN_TONE_WOMAN_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f469\U0001f3ff\u200d\U0001f476\U0001f3ff\u200d\U0001f466\U0001f3ff" - MAHJONG_TILE_SIX_OF_BAMBOOS = "\U0001f015" - WOMAN_IN_TUXEDO_DARK_SKIN_TONE = "\U0001f935\U0001f3ff\u200d\u2640\ufe0f" - FLAG_FOR_LA_RIOJA_AR_F = "\U0001f3f4\U000e0061\U000e0072\U000e0066\U000e007f" - FLAG_FOR_BUSHEHR_IR_06 = "\U0001f3f4\U000e0069\U000e0072\U000e0030\U000e0036\U000e007f" - DIGIT_THREE = "3\ufe0f" - FLAG_FOR_LUXEMBOURG_LU_LU = "\U0001f3f4\U000e006c\U000e0075\U000e006c\U000e0075\U000e007f" - FLAG_FOR_DAGESTAN_RU_DA = "\U0001f3f4\U000e0072\U000e0075\U000e0064\U000e0061\U000e007f" - BLACK_DRAUGHTS_KING = "\u26c3" - WHITE_DRAUGHTS_KING = "\u26c1" - FLAG_FOR_CATAMARCA_AR_K = "\U0001f3f4\U000e0061\U000e0072\U000e006b\U000e007f" - TAG_LATIN_SMALL_LETTER_B = "\U000e0062" - FLAG_FOR_BARBUDA_AG_10 = "\U0001f3f4\U000e0061\U000e0067\U000e0031\U000e0030\U000e007f" - FLAG_FOR_MARL_NZ_MBH = "\U0001f3f4\U000e006e\U000e007a\U000e006d\U000e0062\U000e0068\U000e007f" - FAMILY_MAN_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f476\U0001f3fb" - BLACK_SNOWMAN = "\u26c7" - ASTRONOMICAL_SYMBOL_FOR_URANUS = "\u26e2" - FLAG_FOR_LAO_CAI_VN_02 = "\U0001f3f4\U000e0076\U000e006e\U000e0030\U000e0032\U000e007f" - FLAG_FOR_JURA_CH_JU = "\U0001f3f4\U000e0063\U000e0068\U000e006a\U000e0075\U000e007f" - MAHJONG_TILE_FOUR_OF_BAMBOOS = "\U0001f013" - TAG_LATIN_CAPITAL_LETTER_N = "\U000e004e" - FAMILY_MAN_BABY_BOY = "\U0001f468\u200d\U0001f476\u200d\U0001f466" - FLAG_FOR_MISIONES_AR_N = "\U0001f3f4\U000e0061\U000e0072\U000e006e\U000e007f" - WOMAN_ZOMBIE_DARK_SKIN_TONE = "\U0001f9df\U0001f3ff\u200d\u2640\ufe0f" - FAMILY_WOMAN_BABY = "\U0001f469\u200d\U0001f476" - PALLAS = "\u26b4" - DIGIT_SIX = "6\ufe0f" - DOUBLED_MALE_SIGN = "\u26a3" - FLAG_FOR_JOWZJAN_AF_JOW = "\U0001f3f4\U000e0061\U000e0066\U000e006a\U000e006f\U000e0077\U000e007f" - FLAG_FOR_ESCALDES_ENGORDANY_AD_08 = "\U0001f3f4\U000e0061\U000e0064\U000e0030\U000e0038\U000e007f" - FLAG_FOR_MYKOLAYIVSCHYNA_UA_48 = "\U0001f3f4\U000e0075\U000e0061\U000e0034\U000e0038\U000e007f" - FLAG_FOR_DEIR_EZ_ZOR_SY_DY = "\U0001f3f4\U000e0073\U000e0079\U000e0064\U000e0079\U000e007f" - RIGHT_SPEECH_BUBBLE = "\U0001f5e9" - TAG_DIGIT_ZERO = "\U000e0030" - FLAG_FOR_FUJAIRAH_AE_FU = "\U0001f3f4\U000e0061\U000e0065\U000e0066\U000e0075\U000e007f" - LEFT_CLOSED_ENTRY = "\u26dc" - MAHJONG_TILE_BAMBOO = "\U0001f024" - SQUARED_SALTIRE = "\u26dd" - BLACK_MOON_LILITH = "\u26b8" - FLAG_FOR_LUNDA_NORTE_AO_LNO = "\U0001f3f4\U000e0061\U000e006f\U000e006c\U000e006e\U000e006f\U000e007f" - DRIVE_SLOW_SIGN = "\u26da" - FLAG_FOR_GRAND_CAPE_MOUNT_LR_CM = "\U0001f3f4\U000e006c\U000e0072\U000e0063\U000e006d\U000e007f" - FLAG_FOR_FORMOSA_AR_P = "\U0001f3f4\U000e0061\U000e0072\U000e0070\U000e007f" - FLAG_FOR_WEST_NEW_BRITAIN_PG_WBK = "\U0001f3f4\U000e0070\U000e0067\U000e0077\U000e0062\U000e006b\U000e007f" - BLACK_LEFT_LANE_MERGE = "\u26d8" - FLAG_FOR_ARARAT_AM_AR = "\U0001f3f4\U000e0061\U000e006d\U000e0061\U000e0072\U000e007f" - SEXTILE = "\u26b9" - FLAG_FOR_SAN_JUAN_AR_J = "\U0001f3f4\U000e0061\U000e0072\U000e006a\U000e007f" - FLAG_FOR_BLOKE_SI_150 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0035\U000e0030\U000e007f" - INCREASE_FONT_SIZE_SYMBOL = "\U0001f5da" - FLAG_FOR_ANHUI_CN_34 = "\U0001f3f4\U000e0063\U000e006e\U000e0033\U000e0034\U000e007f" - FLAG_FOR_NORTH_AEGEAN_GR_K = "\U0001f3f4\U000e0067\U000e0072\U000e006b\U000e007f" - FAMILY_MAN_WOMAN_BABY_GIRL = "\U0001f468\u200d\U0001f469\u200d\U0001f476\u200d\U0001f467" - FLAG_FOR_HADRAMAUT_YE_HD = "\U0001f3f4\U000e0079\U000e0065\U000e0068\U000e0064\U000e007f" - TURNED_WHITE_SHOGI_PIECE = "\u26c9" - MALE_WITH_STROKE_SIGN = "\u26a6" - RESTRICTED_LEFT_ENTRY_1 = "\u26e0" - FLAG_FOR_HAVANA_CU_03 = "\U0001f3f4\U000e0063\U000e0075\U000e0030\U000e0033\U000e007f" - LOWER_LEFT_PENCIL = "\U0001f589" - FLAG_FOR_CHUBUT_AR_U = "\U0001f3f4\U000e0061\U000e0072\U000e0075\U000e007f" - CLOCKWISE_RIGHT_AND_LEFT_SEMICIRCLE_ARROWS = "\U0001f5d8" - FLAG_FOR_SANTIAGO_METROPOLITAN_CL_RM = "\U0001f3f4\U000e0063\U000e006c\U000e0072\U000e006d\U000e007f" - CASTLE_2 = "\u26eb" - THREE_RAYS_RIGHT = "\U0001f5e7" - JUNO = "\u26b5" - FLAG_FOR_HAI_DUONG_VN_61 = "\U0001f3f4\U000e0076\U000e006e\U000e0036\U000e0031\U000e007f" - FLAG_FOR_ABSHERON_AZ_ABS = "\U0001f3f4\U000e0061\U000e007a\U000e0061\U000e0062\U000e0073\U000e007f" - FLAG_FOR_CORRIENTES_AR_W = "\U0001f3f4\U000e0061\U000e0072\U000e0077\U000e007f" - MAHJONG_TILE_SOUTH_WIND = "\U0001f001" - FLAG_FOR_GRAND_CASABLANCA_MA_08 = "\U0001f3f4\U000e006d\U000e0061\U000e0030\U000e0038\U000e007f" - FLAG_FOR_NGARCHELONG_PW_218 = "\U0001f3f4\U000e0070\U000e0077\U000e0032\U000e0031\U000e0038\U000e007f" - FLAG_FOR_CORDOBA_AR_X = "\U0001f3f4\U000e0061\U000e0072\U000e0078\U000e007f" - FLAG_FOR_CENTRAL_SINGAPORE_SG_01 = "\U0001f3f4\U000e0073\U000e0067\U000e0030\U000e0031\U000e007f" - FLAG_FOR_CAMPANIA_IT_72 = "\U0001f3f4\U000e0069\U000e0074\U000e0037\U000e0032\U000e007f" - FLAG_FOR_CIBAO_NORTE_DO_35 = "\U0001f3f4\U000e0064\U000e006f\U000e0033\U000e0035\U000e007f" - MAHJONG_TILE_THREE_OF_BAMBOOS = "\U0001f012" - FLAG_FOR_ARAGATSOTN_AM_AG = "\U0001f3f4\U000e0061\U000e006d\U000e0061\U000e0067\U000e007f" - FLAG_FOR_GRAND_GEDEH_LR_GG = "\U0001f3f4\U000e006c\U000e0072\U000e0067\U000e0067\U000e007f" - FLAG_FOR_ABRUZZO_IT_65 = "\U0001f3f4\U000e0069\U000e0074\U000e0036\U000e0035\U000e007f" - CERES = "\u26b3" - FAMILY_MAN_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f466\U0001f3fb" - FLAG_FOR_CARINTHIA_AT_2 = "\U0001f3f4\U000e0061\U000e0074\U000e0032\U000e007f" - MARRIAGE_SYMBOL = "\u26ad" - LIGHTNING = "\u2607" - SUN_2 = "\u2609" - FLAG_FOR_CENTRAL_GREECE_GR_H = "\U0001f3f4\U000e0067\U000e0072\U000e0068\U000e007f" - FAMILY_WOMAN_MAN_BOY_GIRL = "\U0001f469\u200d\U0001f468\u200d\U0001f466\u200d\U0001f467" - FLAG_FOR_BASEL_LANDSCHAFT_CH_BL = "\U0001f3f4\U000e0063\U000e0068\U000e0062\U000e006c\U000e007f" - FLAG_FOR_UTTARAKHAND_IN_UT = "\U0001f3f4\U000e0069\U000e006e\U000e0075\U000e0074\U000e007f" - UP_POINTING_SMALL_AIRPLANE = "\U0001f6e8" - FLAG_FOR_MENDOZA_AR_M = "\U0001f3f4\U000e0061\U000e0072\U000e006d\U000e007f" - FLAG_FOR_MAMOU_REGION_GN_M = "\U0001f3f4\U000e0067\U000e006e\U000e006d\U000e007f" - FLAG_FOR_ATTAPEU_LA_AT = "\U0001f3f4\U000e006c\U000e0061\U000e0061\U000e0074\U000e007f" - FLAG_FOR_SABA_NL_BQ2 = "\U0001f3f4\U000e006e\U000e006c\U000e0062\U000e0071\U000e0032\U000e007f" - FLYING_ENVELOPE = "\U0001f585" - FLAG_FOR_KIRKUK_IQ_KI = "\U0001f3f4\U000e0069\U000e0071\U000e006b\U000e0069\U000e007f" - BLACK_PUSHPIN = "\U0001f588" - CROSSING_LANES = "\u26cc" - FAMILY_WOMAN_BABY_BOY = "\U0001f469\u200d\U0001f476\u200d\U0001f466" - TAG_LATIN_SMALL_LETTER_T = "\U000e0074" - COUPLE_WITH_HEART_MAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN = "\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f469" - MOOD_BUBBLE = "\U0001f5f0" - MAHJONG_TILE_PLUM = "\U0001f022" - FLAG_FOR_NOUAKCHOTT_OUEST_MR_13 = "\U0001f3f4\U000e006d\U000e0072\U000e0031\U000e0033\U000e007f" - RIGHT_THOUGHT_BUBBLE = "\U0001f5ed" - DIGIT_ZERO = "0\ufe0f" - FLAG_FOR_SAXONY_DE_SN = "\U0001f3f4\U000e0064\U000e0065\U000e0073\U000e006e\U000e007f" - FLAG_FOR_SACATEPEQUEZ_GT_SA = "\U0001f3f4\U000e0067\U000e0074\U000e0073\U000e0061\U000e007f" - KISS_MAN_DARK_SKIN_TONE_MAN = "\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468" - DESCENDING_NODE = "\u260b" - FLAG_FOR_VIEUX_FORT_LC_11 = "\U0001f3f4\U000e006c\U000e0063\U000e0031\U000e0031\U000e007f" - ONCOMING_FIRE_ENGINE = "\U0001f6f1" - FLAG_FOR_QUANG_BINH_VN_24 = "\U0001f3f4\U000e0076\U000e006e\U000e0032\U000e0034\U000e007f" - FLAG_FOR_TYROL_AT_7 = "\U0001f3f4\U000e0061\U000e0074\U000e0037\U000e007f" - FLAG_FOR_FRANCISTOWN_BW_FR = "\U0001f3f4\U000e0062\U000e0077\U000e0066\U000e0072\U000e007f" - DISABLED_CAR = "\u26cd" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f469\U0001f3fc\u200d\U0001f467\U0001f3fc\u200d\U0001f467\U0001f3fc" - FLAG_FOR_MBOMOU_CF_MB = "\U0001f3f4\U000e0063\U000e0066\U000e006d\U000e0062\U000e007f" - ASCENDING_NODE = "\u260a" - FLAG_FOR_ILOCOS_PH_01 = "\U0001f3f4\U000e0070\U000e0068\U000e0030\U000e0031\U000e007f" - HEAVY_CIRCLE_WITH_STROKE_AND_TWO_DOTS_ABOVE = "\u26e3" - MAHJONG_TILE_FIVE_OF_BAMBOOS = "\U0001f014" - FLAG_FOR_SAINT_GEORGE_GD_03 = "\U0001f3f4\U000e0067\U000e0064\U000e0030\U000e0033\U000e007f" - COUPLE_WITH_HEART_WOMAN_LIGHT_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fe" - FLAG_FOR_TAIWAN_CN_71 = "\U0001f3f4\U000e0063\U000e006e\U000e0037\U000e0031\U000e007f" - FLAG_FOR_VENETO_IT_34 = "\U0001f3f4\U000e0069\U000e0074\U000e0033\U000e0034\U000e007f" - FLAG_FOR_LUANDA_AO_LUA = "\U0001f3f4\U000e0061\U000e006f\U000e006c\U000e0075\U000e0061\U000e007f" - FLAG_FOR_SAINT_LOUIS_SN_SL = "\U0001f3f4\U000e0073\U000e006e\U000e0073\U000e006c\U000e007f" - FAMILY_MAN_DARK_SKIN_TONE_MAN_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f468\U0001f3ff\u200d\U0001f467\U0001f3ff" - EMPTY_PAGE = "\U0001f5cc" - BOUQUET_OF_FLOWERS = "\U0001f395" - FLAG_FOR_XAISOMBOUN_LA_XS = "\U0001f3f4\U000e006c\U000e0061\U000e0078\U000e0073\U000e007f" - FLAG_FOR_TUCUMAN_AR_T = "\U0001f3f4\U000e0061\U000e0072\U000e0074\U000e007f" - RAIN = "\u26c6" - FAMILY_MAN_MAN_BOY_GIRL = "\U0001f468\u200d\U0001f468\u200d\U0001f466\u200d\U0001f467" - FLAG_FOR_ANKARA_TR_06 = "\U0001f3f4\U000e0074\U000e0072\U000e0030\U000e0036\U000e007f" - FLAG_FOR_GUIZHOU_CN_52 = "\U0001f3f4\U000e0063\U000e006e\U000e0035\U000e0032\U000e007f" - FLAG_FOR_SVALBARD_NO_21 = "\U0001f3f4\U000e006e\U000e006f\U000e0032\U000e0031\U000e007f" - FARSI_SYMBOL = "\u262b" - PENTAGRAM = "\u26e4" - HARD_DISK = "\U0001f5b4" - FLAG_FOR_VORARLBERG_AT_8 = "\U0001f3f4\U000e0061\U000e0074\U000e0038\U000e007f" - NO_PIRACY = "\U0001f572" - RECYCLING_SYMBOL_FOR_TYPE_6_PLASTICS = "\u2678" - OPPOSITION = "\u260d" - FLAG_FOR_ZAIRE_AO_ZAI = "\U0001f3f4\U000e0061\U000e006f\U000e007a\U000e0061\U000e0069\U000e007f" - FLAG_FOR_AMHARA_ET_AM = "\U0001f3f4\U000e0065\U000e0074\U000e0061\U000e006d\U000e007f" - MAHJONG_TILE_SPRING = "\U0001f026" - FLAG_FOR_RED_SEA_SD_RS = "\U0001f3f4\U000e0073\U000e0064\U000e0072\U000e0073\U000e007f" - FLAG_FOR_PIETA_MT_41 = "\U0001f3f4\U000e006d\U000e0074\U000e0034\U000e0031\U000e007f" - FLAG_FOR_ZABUL_AF_ZAB = "\U0001f3f4\U000e0061\U000e0066\U000e007a\U000e0061\U000e0062\U000e007f" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f467\U0001f3fd\u200d\U0001f467\U0001f3fd" - BLACK_SMILING_FACE = "\u263b" - FLAG_FOR_SANTA_CRUZ_BO_S = "\U0001f3f4\U000e0062\U000e006f\U000e0073\U000e007f" - KISS_MAN_DARK_SKIN_TONE_WOMAN = "\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469" - BLACK_FOLDER = "\U0001f5bf" - FLAG_FOR_SANTA_CATARINA_BR_SC = "\U0001f3f4\U000e0062\U000e0072\U000e0073\U000e0063\U000e007f" - KISS_MAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN = "\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469" - FAMILY_WOMAN_MAN_GIRL_BABY = "\U0001f469\u200d\U0001f468\u200d\U0001f467\u200d\U0001f476" - FLAG_FOR_BASQUE_COUNTRY_ES_PV = "\U0001f3f4\U000e0065\U000e0073\U000e0070\U000e0076\U000e007f" - TAG_LATIN_CAPITAL_LETTER_V = "\U000e0056" - FLAG_FOR_MANABI_EC_M = "\U0001f3f4\U000e0065\U000e0063\U000e006d\U000e007f" - LAST_QUARTER_MOON_2 = "\u263e" - FLAG_FOR_NAVARRA_CHARTERED_COMMUNITY_ES_NC = "\U0001f3f4\U000e0065\U000e0073\U000e006e\U000e0063\U000e007f" - INVERTED_PENTAGRAM = "\u26e7" - FAMILY_WOMAN_MAN_GIRL = "\U0001f469\u200d\U0001f468\u200d\U0001f467" - FLAG_FOR_DALARNA_SE_W = "\U0001f3f4\U000e0073\U000e0065\U000e0077\U000e007f" - MAHJONG_TILE_TWO_OF_CHARACTERS = "\U0001f008" - BITCOIN_SIGN = "\u20bf" - FLAG_FOR_CHUUK_FM_TRK = "\U0001f3f4\U000e0066\U000e006d\U000e0074\U000e0072\U000e006b\U000e007f" - FLAG_FOR_KERICHO_KE_12 = "\U0001f3f4\U000e006b\U000e0065\U000e0031\U000e0032\U000e007f" - BLACK_LEFT_POINTING_INDEX = "\u261a" - BLACK_RIGHT_POINTING_INDEX = "\u261b" - KISS_MAN_LIGHT_SKIN_TONE_WOMAN = "\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469" - FLAG_FOR_SARAWAK_MY_13 = "\U0001f3f4\U000e006d\U000e0079\U000e0031\U000e0033\U000e007f" - BLACK_DRAUGHTS_MAN = "\u26c2" - FLAG_FOR_BEAU_BASSIN_ROSE_HILL_MU_BR = "\U0001f3f4\U000e006d\U000e0075\U000e0062\U000e0072\U000e007f" - FLAG_FOR_GOA_IN_GA = "\U0001f3f4\U000e0069\U000e006e\U000e0067\U000e0061\U000e007f" - FLAG_FOR_PERNAMBUCO_BR_PE = "\U0001f3f4\U000e0062\U000e0072\U000e0070\U000e0065\U000e007f" - WOMAN_WITH_HEADSCARF_DARK_SKIN_TONE = "\U0001f9d5\U0001f3ff\u200d\u2640\ufe0f" - FLAG_FOR_YUKON_CA_YT = "\U0001f3f4\U000e0063\U000e0061\U000e0079\U000e0074\U000e007f" - FLAG_FOR_SICHUAN_CN_51 = "\U0001f3f4\U000e0063\U000e006e\U000e0035\U000e0031\U000e007f" - FLAG_FOR_LAKES_SS_LK = "\U0001f3f4\U000e0073\U000e0073\U000e006c\U000e006b\U000e007f" - HAMMER_AND_SICKLE = "\u262d" - FLAG_FOR_BEIJING_CN_11 = "\U0001f3f4\U000e0063\U000e006e\U000e0031\U000e0031\U000e007f" - FLAG_FOR_UPPER_AUSTRIA_AT_4 = "\U0001f3f4\U000e0061\U000e0074\U000e0034\U000e007f" - FLAG_FOR_LAPLAND_FI_10 = "\U0001f3f4\U000e0066\U000e0069\U000e0031\U000e0030\U000e007f" - FAMILY_MAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f469\U0001f3fd\u200d\U0001f466\U0001f3fd\u200d\U0001f476\U0001f3fd" - FLAG_FOR_HIMACHAL_PRADESH_IN_HP = "\U0001f3f4\U000e0069\U000e006e\U000e0068\U000e0070\U000e007f" - FLAG_FOR_VELIKA_POLANA_SI_187 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0038\U000e0037\U000e007f" - FLAG_FOR_MUSLIM_MINDANAO_PH_14 = "\U0001f3f4\U000e0070\U000e0068\U000e0031\U000e0034\U000e007f" - HEADSTONE_GRAVEYARD_SYMBOL = "\u26fc" - CLAMSHELL_MOBILE_PHONE = "\U0001f581" - FLAG_FOR_KOSOVO_METOHIJA_RS_KM = "\U0001f3f4\U000e0072\U000e0073\U000e006b\U000e006d\U000e007f" - DIGIT_TWO = "2\ufe0f" - FLAG_FOR_PUNJAB_IN_PB = "\U0001f3f4\U000e0069\U000e006e\U000e0070\U000e0062\U000e007f" - SIDEWAYS_BLACK_DOWN_POINTING_INDEX = "\U0001f5a1" - FLAG_FOR_DOHA_QA_DA = "\U0001f3f4\U000e0071\U000e0061\U000e0064\U000e0061\U000e007f" - FAMILY_WOMAN_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f466\U0001f3ff\u200d\U0001f476\U0001f3ff" - DOUBLED_FEMALE_SIGN = "\u26a2" - BLACK_SKULL_AND_CROSSBONES = "\U0001f571" - FAMILY_MAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f468\U0001f3fd\u200d\U0001f476\U0001f3fd\u200d\U0001f476\U0001f3fd" - FLAG_FOR_ZEALAND_DK_85 = "\U0001f3f4\U000e0064\U000e006b\U000e0038\U000e0035\U000e007f" - STAFF_OF_HERMES = "\u269a" - QUINCUNX = "\u26bb" - FLAG_FOR_SANTA_FE_AR_S = "\U0001f3f4\U000e0061\U000e0072\U000e0073\U000e007f" - FLAG_FOR_ADIYAMAN_TR_02 = "\U0001f3f4\U000e0074\U000e0072\U000e0030\U000e0032\U000e007f" - FLAG_FOR_KAFR_EL_SHEIKH_EG_KFS = "\U0001f3f4\U000e0065\U000e0067\U000e006b\U000e0066\U000e0073\U000e007f" - FLAG_FOR_ASTARA_AZ_AST = "\U0001f3f4\U000e0061\U000e007a\U000e0061\U000e0073\U000e0074\U000e007f" - FLAG_FOR_SAINT_PATRICK_DM_09 = "\U0001f3f4\U000e0064\U000e006d\U000e0030\U000e0039\U000e007f" - FLAG_FOR_AGDAM_AZ_AGM = "\U0001f3f4\U000e0061\U000e007a\U000e0061\U000e0067\U000e006d\U000e007f" - FLAG_FOR_TIERRA_DEL_FUEGO_AR_V = "\U0001f3f4\U000e0061\U000e0072\U000e0076\U000e007f" - SIDEWAYS_BLACK_LEFT_POINTING_INDEX = "\U0001f59a" - TAG_DIGIT_FIVE = "\U000e0035" - FLAG_FOR_KANSAS_US_KS = "\U0001f3f4\U000e0075\U000e0073\U000e006b\U000e0073\U000e007f" - MUSIC_FLAT_SIGN = "\u266d" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f467\U0001f3fc\u200d\U0001f476\U0001f3fc" - BALLOT_BOX_WITH_X = "\u2612" - TAG_LOW_LINE = "\U000e005f" - FLAG_FOR_ADYGEA_RU_AD = "\U0001f3f4\U000e0072\U000e0075\U000e0061\U000e0064\U000e007f" - TELEPHONE_ON_TOP_OF_MODEM = "\U0001f580" - FLAG_FOR_NINGXIA_CN_64 = "\U0001f3f4\U000e0063\U000e006e\U000e0036\U000e0034\U000e007f" - FLAG_FOR_GAVLEBORG_SE_X = "\U0001f3f4\U000e0073\U000e0065\U000e0078\U000e007f" - FLAG_FOR_AGSU_AZ_AGU = "\U0001f3f4\U000e0061\U000e007a\U000e0061\U000e0067\U000e0075\U000e007f" - HISTORIC_SITE = "\u26ec" - FLAG_FOR_KAGAWA_JP_37 = "\U0001f3f4\U000e006a\U000e0070\U000e0033\U000e0037\U000e007f" - FLAG_FOR_BOMI_LR_BM = "\U0001f3f4\U000e006c\U000e0072\U000e0062\U000e006d\U000e007f" - FLAG_FOR_MIDLANDS_ZW_MI = "\U0001f3f4\U000e007a\U000e0077\U000e006d\U000e0069\U000e007f" - FLAG_FOR_AGHJABADI_AZ_AGC = "\U0001f3f4\U000e0061\U000e007a\U000e0061\U000e0067\U000e0063\U000e007f" - SIDEWAYS_WHITE_UP_POINTING_INDEX = "\U0001f59e" - KISS_MAN_WOMAN = "\U0001f468\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469" - FLAG_FOR_NEUQUEN_AR_Q = "\U0001f3f4\U000e0061\U000e0072\U000e0071\U000e007f" - FLAG_FOR_AZORES_PT_20 = "\U0001f3f4\U000e0070\U000e0074\U000e0032\U000e0030\U000e007f" - FLAG_FOR_BAS_CONGO_CD_BC = "\U0001f3f4\U000e0063\U000e0064\U000e0062\U000e0063\U000e007f" - FLAG_FOR_BILASUVAR_AZ_BIL = "\U0001f3f4\U000e0061\U000e007a\U000e0062\U000e0069\U000e006c\U000e007f" - FLAG_FOR_KURSK_RU_KRS = "\U0001f3f4\U000e0072\U000e0075\U000e006b\U000e0072\U000e0073\U000e007f" - FLAG_FOR_BARDA_AZ_BAR = "\U0001f3f4\U000e0061\U000e007a\U000e0062\U000e0061\U000e0072\U000e007f" - TRIGRAM_FOR_LAKE = "\u2631" - FLAG_FOR_JABRAYIL_AZ_CAB = "\U0001f3f4\U000e0061\U000e007a\U000e0063\U000e0061\U000e0062\U000e007f" - NOTCHED_RIGHT_SEMICIRCLE_WITH_THREE_DOTS = "\U0001f544" - FLAG_FOR_FIZULI_AZ_FUZ = "\U0001f3f4\U000e0061\U000e007a\U000e0066\U000e0075\U000e007a\U000e007f" - FLAG_FOR_BEYLAGAN_AZ_BEY = "\U0001f3f4\U000e0061\U000e007a\U000e0062\U000e0065\U000e0079\U000e007f" - FLAG_FOR_NIARI_CG_9 = "\U0001f3f4\U000e0063\U000e0067\U000e0039\U000e007f" - FAMILY_WOMAN_MAN_BABY = "\U0001f469\u200d\U0001f468\u200d\U0001f476" - FAMILY_WOMAN_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f466\U0001f3ff\u200d\U0001f466\U0001f3ff" - WHITE_DRAUGHTS_MAN = "\u26c0" - FLAG_FOR_SAINT_JOSEPH_DM_06 = "\U0001f3f4\U000e0064\U000e006d\U000e0030\U000e0036\U000e007f" - DIGRAM_FOR_LESSER_YIN = "\u268d" - TAG_DIGIT_THREE = "\U000e0033" - FLAG_FOR_PAPUA_ISLANDS_ID_PP = "\U0001f3f4\U000e0069\U000e0064\U000e0070\U000e0070\U000e007f" - FLAG_FOR_HARBOUR_ISLAND_BS_HI = "\U0001f3f4\U000e0062\U000e0073\U000e0068\U000e0069\U000e007f" - FLAG_FOR_YEREVAN_AM_ER = "\U0001f3f4\U000e0061\U000e006d\U000e0065\U000e0072\U000e007f" - KISS_WOMAN_LIGHT_SKIN_TONE_WOMAN = "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469" - ANKH = "\u2625" - LIGHT_CHECK_MARK = "\U0001f5f8" - KISS_WOMAN_WOMAN_DARK_SKIN_TONE = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3ff" - FLAG_FOR_GOYCHAY_AZ_GOY = "\U0001f3f4\U000e0061\U000e007a\U000e0067\U000e006f\U000e0079\U000e007f" - TAG_LATIN_CAPITAL_LETTER_A = "\U000e0041" - FLAG_FOR_PORT_HERCULES_MC_PH = "\U0001f3f4\U000e006d\U000e0063\U000e0070\U000e0068\U000e007f" - CIRCLED_CROSSING_LANES = "\u26d2" - MONOGRAM_FOR_YANG = "\u268a" - FLAG_FOR_CESAR_CO_CES = "\U0001f3f4\U000e0063\U000e006f\U000e0063\U000e0065\U000e0073\U000e007f" - DECREASE_FONT_SIZE_SYMBOL = "\U0001f5db" - BALLOT_BOLD_SCRIPT_X = "\U0001f5f6" - FLAG_FOR_JERUSALEM_PS_JEM = "\U0001f3f4\U000e0070\U000e0073\U000e006a\U000e0065\U000e006d\U000e007f" - FLAG_FOR_UMM_SALAL_QA_US = "\U0001f3f4\U000e0071\U000e0061\U000e0075\U000e0073\U000e007f" - BLACK_CHESS_BISHOP = "\u265d" - FLAG_FOR_KALIMANTAN_ID_KA = "\U0001f3f4\U000e0069\U000e0064\U000e006b\U000e0061\U000e007f" - BALLOT_BOX = "\u2610" - FLAG_FOR_BRYANSK_RU_BRY = "\U0001f3f4\U000e0072\U000e0075\U000e0062\U000e0072\U000e0079\U000e007f" - KISS_MAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fe" - FLAG_FOR_AGDASH_AZ_AGS = "\U0001f3f4\U000e0061\U000e007a\U000e0061\U000e0067\U000e0073\U000e007f" - FLAG_FOR_GOYGOL_AZ_GYG = "\U0001f3f4\U000e0061\U000e007a\U000e0067\U000e0079\U000e0067\U000e007f" - FLAG_FOR_HAJIGABUL_AZ_HAC = "\U0001f3f4\U000e0061\U000e007a\U000e0068\U000e0061\U000e0063\U000e007f" - FLAG_FOR_KASTAMONU_TR_37 = "\U0001f3f4\U000e0074\U000e0072\U000e0033\U000e0037\U000e007f" - FLAG_FOR_REZEKNE_MUNICIPALITY_LV_077 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0037\U000e0037\U000e007f" - FLAG_FOR_BONAIRE_NL_BQ1 = "\U0001f3f4\U000e006e\U000e006c\U000e0062\U000e0071\U000e0031\U000e007f" - FLAG_FOR_TAVUSH_AM_TV = "\U0001f3f4\U000e0061\U000e006d\U000e0074\U000e0076\U000e007f" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f467\U0001f3fc\u200d\U0001f476\U0001f3fc" - WHITE_TELEPHONE = "\u260f" - FLAG_FOR_VEST_AGDER_NO_10 = "\U0001f3f4\U000e006e\U000e006f\U000e0031\U000e0030\U000e007f" - FLAG_FOR_GORANBOY_AZ_GOR = "\U0001f3f4\U000e0061\U000e007a\U000e0067\U000e006f\U000e0072\U000e007f" - FLAG_FOR_CROSS_RIVER_NG_CR = "\U0001f3f4\U000e006e\U000e0067\U000e0063\U000e0072\U000e007f" - FLAG_FOR_IMISHLI_AZ_IMI = "\U0001f3f4\U000e0061\U000e007a\U000e0069\U000e006d\U000e0069\U000e007f" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f469\U0001f3fc\u200d\U0001f467\U0001f3fc\u200d\U0001f476\U0001f3fc" - FLAG_FOR_TANGANYIKA_CD_TA = "\U0001f3f4\U000e0063\U000e0064\U000e0074\U000e0061\U000e007f" - BLACK_STAR = "\u2605" - CAUTION_SIGN = "\u2621" - FLAG_FOR_SHANGHAI_CN_31 = "\U0001f3f4\U000e0063\U000e006e\U000e0033\U000e0031\U000e007f" - CHI_RHO = "\u2627" - BALLOT_BOX_WITH_BOLD_SCRIPT_X = "\U0001f5f7" - TRIGRAM_FOR_FIRE = "\u2632" - FLAG_FOR_WEST_VIRGINIA_US_WV = "\U0001f3f4\U000e0075\U000e0073\U000e0077\U000e0076\U000e007f" - FLAG_FOR_CLIPPERTON_ISLAND_FR_CP = "\U0001f3f4\U000e0066\U000e0072\U000e0063\U000e0070\U000e007f" - EIGHTH_NOTE = "\u266a" - CROSS_OF_LORRAINE = "\u2628" - CADUCEUS = "\u2624" - FLAG_FOR_GORJ_RO_GJ = "\U0001f3f4\U000e0072\U000e006f\U000e0067\U000e006a\U000e007f" - OUTLINED_WHITE_STAR = "\u269d" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f469\U0001f3fe\u200d\U0001f466\U0001f3fe" - FLAG_FOR_ALTAI_KRAI_RU_ALT = "\U0001f3f4\U000e0072\U000e0075\U000e0061\U000e006c\U000e0074\U000e007f" - KISS_WOMAN_MEDIUM_SKIN_TONE_WOMAN_DARK_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3ff" - FLAG_FOR_LACHIN_AZ_LAC = "\U0001f3f4\U000e0061\U000e007a\U000e006c\U000e0061\U000e0063\U000e007f" - FLAG_FOR_TAKHAR_AF_TAK = "\U0001f3f4\U000e0061\U000e0066\U000e0074\U000e0061\U000e006b\U000e007f" - FLAG_FOR_LANKARAN_AZ_LA = "\U0001f3f4\U000e0061\U000e007a\U000e006c\U000e0061\U000e007f" - TAG_LATIN_CAPITAL_LETTER_S = "\U000e0053" - FLAG_FOR_CENTRAL_UG_C = "\U0001f3f4\U000e0075\U000e0067\U000e0063\U000e007f" - FLAG_FOR_ZUG_CH_ZG = "\U0001f3f4\U000e0063\U000e0068\U000e007a\U000e0067\U000e007f" - TAG_LATIN_CAPITAL_LETTER_I = "\U000e0049" - BEAMED_ASCENDING_MUSICAL_NOTES = "\U0001f39c" - FLAG_FOR_LERIK_AZ_LER = "\U0001f3f4\U000e0061\U000e007a\U000e006c\U000e0065\U000e0072\U000e007f" - PLUTO = "\u2647" - FLAG_FOR_AMERICAN_SAMOA_US_AS = "\U0001f3f4\U000e0075\U000e0073\U000e0061\U000e0073\U000e007f" - PARTIALLY_RECYCLED_PAPER_SYMBOL = "\u267d" - FLAG_FOR_OKINAWA_JP_47 = "\U0001f3f4\U000e006a\U000e0070\U000e0034\U000e0037\U000e007f" - FLAG_FOR_GADABAY_AZ_GAD = "\U0001f3f4\U000e0061\U000e007a\U000e0067\U000e0061\U000e0064\U000e007f" - FLAG_FOR_VASTERNORRLAND_SE_Y = "\U0001f3f4\U000e0073\U000e0065\U000e0079\U000e007f" - FLAG_FOR_ORDINO_AD_05 = "\U0001f3f4\U000e0061\U000e0064\U000e0030\U000e0035\U000e007f" - EARTH = "\u2641" - VERTICAL_MALE_WITH_STROKE_SIGN = "\u26a8" - TAG_VERTICAL_LINE = "\U000e007c" - COUPLE_WITH_HEART_MAN_LIGHT_SKIN_TONE_MAN = "\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f468" - FLAG_FOR_NAFTALAN_AZ_NA = "\U0001f3f4\U000e0061\U000e007a\U000e006e\U000e0061\U000e007f" - FLAG_FOR_MASALLY_AZ_MAS = "\U0001f3f4\U000e0061\U000e007a\U000e006d\U000e0061\U000e0073\U000e007f" - FLAG_FOR_MINGACHEVIR_AZ_MI = "\U0001f3f4\U000e0061\U000e007a\U000e006d\U000e0069\U000e007f" - FLAG_FOR_KVEMO_KARTLI_GE_KK = "\U0001f3f4\U000e0067\U000e0065\U000e006b\U000e006b\U000e007f" - TAG_LATIN_CAPITAL_LETTER_X = "\U000e0058" - HEART_WITH_TIP_ON_THE_LEFT = "\U0001f394" - TAG_PERCENT_SIGN = "\U000e0025" - SATURN = "\u2644" - NEPTUNE = "\u2646" - FLAG_FOR_LAS_TUNAS_CU_10 = "\U0001f3f4\U000e0063\U000e0075\U000e0031\U000e0030\U000e007f" - THREE_LINES_CONVERGING_RIGHT = "\u269e" - FAMILY_WOMAN_WOMAN_BOY_BABY = "\U0001f469\u200d\U0001f469\u200d\U0001f466\u200d\U0001f476" - REVERSED_ROTATED_FLORAL_HEART_BULLET = "\u2619" - FLAG_FOR_FAIYUM_EG_FYM = "\U0001f3f4\U000e0065\U000e0067\U000e0066\U000e0079\U000e006d\U000e007f" - FLAG_FOR_CELJE_SI_011 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0031\U000e0031\U000e007f" - FLAG_FOR_GILAN_IR_19 = "\U0001f3f4\U000e0069\U000e0072\U000e0031\U000e0039\U000e007f" - FLAG_FOR_PANEVEZIO_MUNICIPALITY_LT_32 = "\U0001f3f4\U000e006c\U000e0074\U000e0033\U000e0032\U000e007f" - FLAG_FOR_AYSEN_CL_AI = "\U0001f3f4\U000e0063\U000e006c\U000e0061\U000e0069\U000e007f" - SCREEN = "\U0001f5b5" - WHITE_CHESS_ROOK = "\u2656" - FLAG_FOR_WEST_COAST_NZ_WTC = "\U0001f3f4\U000e006e\U000e007a\U000e0077\U000e0074\U000e0063\U000e007f" - FLAG_FOR_TANGIER_TETOUAN_MA_01 = "\U0001f3f4\U000e006d\U000e0061\U000e0030\U000e0031\U000e007f" - FLAG_FOR_NEFTCHALA_AZ_NEF = "\U0001f3f4\U000e0061\U000e007a\U000e006e\U000e0065\U000e0066\U000e007f" - FLAG_FOR_MOSCOW_RU_MOW = "\U0001f3f4\U000e0072\U000e0075\U000e006d\U000e006f\U000e0077\U000e007f" - COUPLE_WITH_HEART_WOMAN_MEDIUM_DARK_SKIN_TONE_WOMAN_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3ff" - WHITE_CHESS_QUEEN = "\u2655" - FLAG_FOR_ISMAILLI_AZ_ISM = "\U0001f3f4\U000e0061\U000e007a\U000e0069\U000e0073\U000e006d\U000e007f" - BLACK_CROSS_ON_SHIELD = "\u26e8" - FLAG_FOR_TARACLIA_MD_TA = "\U0001f3f4\U000e006d\U000e0064\U000e0074\U000e0061\U000e007f" - FLAG_FOR_DROCHIA_MD_DR = "\U0001f3f4\U000e006d\U000e0064\U000e0064\U000e0072\U000e007f" - FLAG_FOR_ONTARIO_CA_ON = "\U0001f3f4\U000e0063\U000e0061\U000e006f\U000e006e\U000e007f" - EAST_SYRIAC_CROSS = "\u2671" - TAG_PLUS_SIGN = "\U000e002b" - DIGRAM_FOR_GREATER_YIN = "\u268f" - WHITE_PENNANT = "\U0001f3f1" - COUPLE_WITH_HEART_MAN_LIGHT_SKIN_TONE_WOMAN_DARK_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3ff" - FLAG_FOR_GOBUSTAN_AZ_QOB = "\U0001f3f4\U000e0061\U000e007a\U000e0071\U000e006f\U000e0062\U000e007f" - FLAG_FOR_NORTHERN_IRELAND_GB_NIR = "\U0001f3f4\U000e0067\U000e0062\U000e006e\U000e0069\U000e0072\U000e007f" - KISS_MAN_WOMAN_MEDIUM_SKIN_TONE = "\U0001f468\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fd" - COUPLE_WITH_HEART_WOMAN_MEDIUM_DARK_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fb" - FLAG_FOR_NAPO_EC_N = "\U0001f3f4\U000e0065\U000e0063\U000e006e\U000e007f" - FLAG_FOR_KORCE_COUNTY_AL_06 = "\U0001f3f4\U000e0061\U000e006c\U000e0030\U000e0036\U000e007f" - FLAG_FOR_QABALA_AZ_QAB = "\U0001f3f4\U000e0061\U000e007a\U000e0071\U000e0061\U000e0062\U000e007f" - TRIGRAM_FOR_WIND = "\u2634" - FLAG_FOR_NANA_MAMBERE_CF_NM = "\U0001f3f4\U000e0063\U000e0066\U000e006e\U000e006d\U000e007f" - FLAG_FOR_OGHUZ_AZ_OGU = "\U0001f3f4\U000e0061\U000e007a\U000e006f\U000e0067\U000e0075\U000e007f" - KISS_MAN_MEDIUM_DARK_SKIN_TONE_WOMAN_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3ff" - FLAG_FOR_SMARJESKE_TOPLICE_SI_206 = "\U0001f3f4\U000e0073\U000e0069\U000e0032\U000e0030\U000e0036\U000e007f" - FLAG_FOR_HO_CHI_MINH_CITY_VN_SG = "\U0001f3f4\U000e0076\U000e006e\U000e0073\U000e0067\U000e007f" - FLAG_FOR_QUBADLI_AZ_QBI = "\U0001f3f4\U000e0061\U000e007a\U000e0071\U000e0062\U000e0069\U000e007f" - FLAG_FOR_SUD_UBANGI_CD_SU = "\U0001f3f4\U000e0063\U000e0064\U000e0073\U000e0075\U000e007f" - KISS_WOMAN_LIGHT_SKIN_TONE_WOMAN_DARK_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3ff" - RECYCLING_SYMBOL_FOR_TYPE_5_PLASTICS = "\u2677" - MAHJONG_TILE_CHRYSANTHEMUM = "\U0001f025" - FLAG_FOR_BRASOV_RO_BV = "\U0001f3f4\U000e0072\U000e006f\U000e0062\U000e0076\U000e007f" - FLAG_FOR_MOGILA_MK_53 = "\U0001f3f4\U000e006d\U000e006b\U000e0035\U000e0033\U000e007f" - THUNDERSTORM = "\u2608" - FLAG_FOR_JALILABAD_AZ_CAL = "\U0001f3f4\U000e0061\U000e007a\U000e0063\U000e0061\U000e006c\U000e007f" - FLAG_FOR_QUSAR_AZ_QUS = "\U0001f3f4\U000e0061\U000e007a\U000e0071\U000e0075\U000e0073\U000e007f" - TAG_QUOTATION_MARK = "\U000e0022" - DIE_FACE_3 = "\u2682" - FAMILY_WOMAN_DARK_SKIN_TONE_MAN_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f468\U0001f3ff\u200d\U0001f476\U0001f3ff\u200d\U0001f466\U0001f3ff" - FLAG_FOR_QAZAKH_AZ_QAZ = "\U0001f3f4\U000e0061\U000e007a\U000e0071\U000e0061\U000e007a\U000e007f" - KISS_WOMAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fb" - FLAG_FOR_KAVANGO_EAST_NA_KE = "\U0001f3f4\U000e006e\U000e0061\U000e006b\U000e0065\U000e007f" - FLAG_FOR_QAKH_AZ_QAX = "\U0001f3f4\U000e0061\U000e007a\U000e0071\U000e0061\U000e0078\U000e007f" - FLAG_FOR_SAINT_PETER_AG_07 = "\U0001f3f4\U000e0061\U000e0067\U000e0030\U000e0037\U000e007f" - FLAG_FOR_BAJA_CALIFORNIA_MX_BCN = "\U0001f3f4\U000e006d\U000e0078\U000e0062\U000e0063\U000e006e\U000e007f" - KISS_WOMAN_DARK_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fd" - FLAG_FOR_ASGABAT_TM_S = "\U0001f3f4\U000e0074\U000e006d\U000e0073\U000e007f" - FLAG_FOR_SABIRABAD_AZ_SAB = "\U0001f3f4\U000e0061\U000e007a\U000e0073\U000e0061\U000e0062\U000e007f" - BLACK_SHOGI_PIECE = "\u2617" - THREE_LINES_CONVERGING_LEFT = "\u269f" - FAMILY_WOMAN_BOY_BABY = "\U0001f469\u200d\U0001f466\u200d\U0001f476" - MUSIC_NATURAL_SIGN = "\u266e" - FLAG_FOR_QUTHING_LS_G = "\U0001f3f4\U000e006c\U000e0073\U000e0067\U000e007f" - FLAG_FOR_JONGLEI_SS_JG = "\U0001f3f4\U000e0073\U000e0073\U000e006a\U000e0067\U000e007f" - FLAG_FOR_NELSON_NZ_NSN = "\U0001f3f4\U000e006e\U000e007a\U000e006e\U000e0073\U000e006e\U000e007f" - COUPLE_WITH_HEART_MAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fd" - FLAG_FOR_VIENNA_AT_9 = "\U0001f3f4\U000e0061\U000e0074\U000e0039\U000e007f" - FLAG_FOR_SHABRAN_AZ_SBN = "\U0001f3f4\U000e0061\U000e007a\U000e0073\U000e0062\U000e006e\U000e007f" - DIE_FACE_4 = "\u2683" - WHITE_SHOGI_PIECE = "\u2616" - RECYCLING_SYMBOL_FOR_TYPE_4_PLASTICS = "\u2676" - FLAG_FOR_MAKEDONSKI_BROD_MK_52 = "\U0001f3f4\U000e006d\U000e006b\U000e0035\U000e0032\U000e007f" - KISS_WOMAN_MAN_MEDIUM_SKIN_TONE = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fd" - FLAG_FOR_SHAKI_DISTRICT_AZ_SAK = "\U0001f3f4\U000e0061\U000e007a\U000e0073\U000e0061\U000e006b\U000e007f" - URANUS = "\u2645" - FLAG_FOR_QUBA_AZ_QBA = "\U0001f3f4\U000e0061\U000e007a\U000e0071\U000e0062\U000e0061\U000e007f" - FLAG_FOR_SAMUKH_AZ_SMX = "\U0001f3f4\U000e0061\U000e007a\U000e0073\U000e006d\U000e0078\U000e007f" - FLAG_FOR_FEDERAL_CAPITAL_TERRITORY_NG_FC = "\U0001f3f4\U000e006e\U000e0067\U000e0066\U000e0063\U000e007f" - FLAG_FOR_FRIULI_VENEZIA_GIULIA_IT_36 = "\U0001f3f4\U000e0069\U000e0074\U000e0033\U000e0036\U000e007f" - WHITE_FLAG_2 = "\u2690" - DIE_FACE_2 = "\u2681" - FLAG_FOR_SHAKI_AZ_SA = "\U0001f3f4\U000e0061\U000e007a\U000e0073\U000e0061\U000e007f" - FLAG_FOR_LA_PAMPA_AR_L = "\U0001f3f4\U000e0061\U000e0072\U000e006c\U000e007f" - FLAG_FOR_SIAZAN_AZ_SIY = "\U0001f3f4\U000e0061\U000e007a\U000e0073\U000e0069\U000e0079\U000e007f" - FLAG_FOR_NEW_IRELAND_PG_NIK = "\U0001f3f4\U000e0070\U000e0067\U000e006e\U000e0069\U000e006b\U000e007f" - FLAG_FOR_AKMENE_LT_01 = "\U0001f3f4\U000e006c\U000e0074\U000e0030\U000e0031\U000e007f" - FLAG_FOR_SAINT_LOUIS_SC_22 = "\U0001f3f4\U000e0073\U000e0063\U000e0032\U000e0032\U000e007f" - FLAG_FOR_UPPER_TAKUTU_UPPER_ESSEQUIBO_GY_UT = "\U0001f3f4\U000e0067\U000e0079\U000e0075\U000e0074\U000e007f" - FLAG_FOR_SHIRVAN_AZ_SR = "\U0001f3f4\U000e0061\U000e007a\U000e0073\U000e0072\U000e007f" - FLAG_FOR_HAUT_MBOMOU_CF_HM = "\U0001f3f4\U000e0063\U000e0066\U000e0068\U000e006d\U000e007f" - TRIANGLE_WITH_ROUNDED_CORNERS = "\U0001f6c6" - FLAG_FOR_SHAMAKHI_AZ_SMI = "\U0001f3f4\U000e0061\U000e007a\U000e0073\U000e006d\U000e0069\U000e007f" - KISS_WOMAN_DARK_SKIN_TONE_MAN = "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468" - FAMILY_MAN_DARK_SKIN_TONE_WOMAN_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f469\U0001f3ff\u200d\U0001f467\U0001f3ff\u200d\U0001f467\U0001f3ff" - KISS_WOMAN_DARK_SKIN_TONE_WOMAN = "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469" - DOCUMENT_WITH_TEXT = "\U0001f5b9" - FLAG_FOR_TOVUZ_AZ_TOV = "\U0001f3f4\U000e0061\U000e007a\U000e0074\U000e006f\U000e0076\U000e007f" - FLAG_FOR_PORT_MORESBY_PG_NCD = "\U0001f3f4\U000e0070\U000e0067\U000e006e\U000e0063\U000e0064\U000e007f" - WHITE_SUN_WITH_RAYS = "\u263c" - FAMILY_MAN_LIGHT_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f469\U0001f3fb\u200d\U0001f467\U0001f3fb" - FAMILY_MAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f469\U0001f3fd\u200d\U0001f466\U0001f3fd\u200d\U0001f467\U0001f3fd" - MAHJONG_TILE_AUTUMN = "\U0001f028" - FLAG_FOR_UJAR_AZ_UCA = "\U0001f3f4\U000e0061\U000e007a\U000e0075\U000e0063\U000e0061\U000e007f" - FLAG_FOR_STRUMICA_MK_73 = "\U0001f3f4\U000e006d\U000e006b\U000e0037\U000e0033\U000e007f" - FLAG_FOR_LVIVSHCHYNA_UA_46 = "\U0001f3f4\U000e0075\U000e0061\U000e0034\U000e0036\U000e007f" - TAG_LATIN_SMALL_LETTER_H = "\U000e0068" - KISS_MAN_DARK_SKIN_TONE_MAN_LIGHT_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fb" - FAMILY_MAN_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f467\U0001f3ff\u200d\U0001f466\U0001f3ff" - SEMISEXTILE = "\u26ba" - BLACK_TOUCHTONE_TELEPHONE = "\U0001f57f" - WHITE_CIRCLE_WITH_DOT_RIGHT = "\u2686" - FAMILY_MAN_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f466\U0001f3fd\u200d\U0001f467\U0001f3fd" - FLAG_FOR_LIPETSK_RU_LIP = "\U0001f3f4\U000e0072\U000e0075\U000e006c\U000e0069\U000e0070\U000e007f" - FLAG_FOR_DOL_PRI_LJUBLJANI_SI_022 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0032\U000e0032\U000e007f" - KISS_MAN_MAN_DARK_SKIN_TONE = "\U0001f468\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3ff" - WHITE_DIAMOND_IN_SQUARE = "\u26cb" - COUPLE_WITH_HEART_MAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fc" - FLAG_FOR_KHIZI_AZ_XIZ = "\U0001f3f4\U000e0061\U000e007a\U000e0078\U000e0069\U000e007a\U000e007f" - FAMILY_MAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f468\U0001f3fd\u200d\U0001f467\U0001f3fd\u200d\U0001f467\U0001f3fd" - KISS_WOMAN_DARK_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fb" - UP_POINTING_MILITARY_AIRPLANE = "\U0001f6e6" - FLAG_FOR_KHOJALI_AZ_XCI = "\U0001f3f4\U000e0061\U000e007a\U000e0078\U000e0063\U000e0069\U000e007f" - FLAG_FOR_YEVLAKH_DISTRICT_AZ_YEV = "\U0001f3f4\U000e0061\U000e007a\U000e0079\U000e0065\U000e0076\U000e007f" - FLAG_FOR_ZANZIBAR_CENTRAL_SOUTH_TZ_11 = "\U0001f3f4\U000e0074\U000e007a\U000e0031\U000e0031\U000e007f" - TAG_LEFT_SQUARE_BRACKET = "\U000e005b" - FAMILY_MAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f469\U0001f3fd\u200d\U0001f467\U0001f3fd\u200d\U0001f476\U0001f3fd" - FLAG_FOR_NORTHLAND_NZ_NTL = "\U0001f3f4\U000e006e\U000e007a\U000e006e\U000e0074\U000e006c\U000e007f" - DIVORCE_SYMBOL = "\u26ae" - FLAG_FOR_BALAKAN_AZ_BAL = "\U0001f3f4\U000e0061\U000e007a\U000e0062\U000e0061\U000e006c\U000e007f" - FLAG_FOR_PROVENCE_ALPES_COTE_D_AZUR_FR_PAC = "\U0001f3f4\U000e0066\U000e0072\U000e0070\U000e0061\U000e0063\U000e007f" - FLAG_FOR_SHUSHA_AZ_SUS = "\U0001f3f4\U000e0061\U000e007a\U000e0073\U000e0075\U000e0073\U000e007f" - COUPLE_WITH_HEART_WOMAN_DARK_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fe" - FLAG_FOR_MONTSERRADO_LR_MO = "\U0001f3f4\U000e006c\U000e0072\U000e006d\U000e006f\U000e007f" - FAMILY_MAN_MAN_GIRL_BABY = "\U0001f468\u200d\U0001f468\u200d\U0001f467\u200d\U0001f476" - FLAG_FOR_YARDYMLI_AZ_YAR = "\U0001f3f4\U000e0061\U000e007a\U000e0079\U000e0061\U000e0072\U000e007f" - FLAG_FOR_SALYAN_AZ_SAL = "\U0001f3f4\U000e0061\U000e007a\U000e0073\U000e0061\U000e006c\U000e007f" - MALE_AND_FEMALE_SIGN = "\u26a5" - FLAG_FOR_NEW_CALEDONIA_FR_NC = "\U0001f3f4\U000e0066\U000e0072\U000e006e\U000e0063\U000e007f" - FRAME_WITH_TILES = "\U0001f5bd" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f469\U0001f3fe\u200d\U0001f476\U0001f3fe\u200d\U0001f466\U0001f3fe" - FAMILY_MAN_DARK_SKIN_TONE_WOMAN_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f469\U0001f3ff\u200d\U0001f466\U0001f3ff\u200d\U0001f476\U0001f3ff" - FLAG_FOR_SAINT_THOMAS_BB_11 = "\U0001f3f4\U000e0062\U000e0062\U000e0031\U000e0031\U000e007f" - FLAG_FOR_CONSTANTINE_DZ_25 = "\U0001f3f4\U000e0064\U000e007a\U000e0032\U000e0035\U000e007f" - FLAG_FOR_YEVLAKH_AZ_YE = "\U0001f3f4\U000e0061\U000e007a\U000e0079\U000e0065\U000e007f" - FAMILY_MAN_DARK_SKIN_TONE_WOMAN_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f469\U0001f3ff\u200d\U0001f467\U0001f3ff\u200d\U0001f476\U0001f3ff" - COUPLE_WITH_HEART_MAN_WOMAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fc" - COUPLE_WITH_HEART_MAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fc" - FLAG_FOR_BOTOSANI_RO_BT = "\U0001f3f4\U000e0072\U000e006f\U000e0062\U000e0074\U000e007f" - FLAG_FOR_ZAQATALA_AZ_ZAQ = "\U0001f3f4\U000e0061\U000e007a\U000e007a\U000e0061\U000e0071\U000e007f" - FLAG_FOR_CHIN_MM_14 = "\U0001f3f4\U000e006d\U000e006d\U000e0031\U000e0034\U000e007f" - FLAG_FOR_KHOST_AF_KHO = "\U0001f3f4\U000e0061\U000e0066\U000e006b\U000e0068\U000e006f\U000e007f" - FAMILY_MAN_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f466\U0001f3fb\u200d\U0001f476\U0001f3fb" - QUARTER_NOTE = "\u2669" - HEAVY_LATIN_CROSS = "\U0001f547" - FLAG_FOR_QUINDIO_CO_QUI = "\U0001f3f4\U000e0063\U000e006f\U000e0071\U000e0075\U000e0069\U000e007f" - FLAG_FOR_CUSCO_PE_CUS = "\U0001f3f4\U000e0070\U000e0065\U000e0063\U000e0075\U000e0073\U000e007f" - COUPLE_WITH_HEART_WOMAN_WOMAN_DARK_SKIN_TONE = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3ff" - MAHJONG_TILE_BACK = "\U0001f02b" - FLAG_FOR_SAINT_ANDREW_BB_02 = "\U0001f3f4\U000e0062\U000e0062\U000e0030\U000e0032\U000e007f" - FLAG_FOR_SOUTH_HOLLAND_NL_ZH = "\U0001f3f4\U000e006e\U000e006c\U000e007a\U000e0068\U000e007f" - FLAG_FOR_LAC_TD_LC = "\U0001f3f4\U000e0074\U000e0064\U000e006c\U000e0063\U000e007f" - FLAG_FOR_VELIKO_TARNOVO_BG_04 = "\U0001f3f4\U000e0062\U000e0067\U000e0030\U000e0034\U000e007f" - FLAG_FOR_EASTERN_FJ_E = "\U0001f3f4\U000e0066\U000e006a\U000e0065\U000e007f" - COUPLE_WITH_HEART_MAN_WOMAN_MEDIUM_SKIN_TONE = "\U0001f468\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fd" - FLAG_FOR_SHAMKIR_AZ_SKR = "\U0001f3f4\U000e0061\U000e007a\U000e0073\U000e006b\U000e0072\U000e007f" - PEN_OVER_STAMPED_ENVELOPE = "\U0001f586" - FLAG_FOR_BALEARIC_ISLANDS_ES_IB = "\U0001f3f4\U000e0065\U000e0073\U000e0069\U000e0062\U000e007f" - FLAG_FOR_BATKEN_KG_B = "\U0001f3f4\U000e006b\U000e0067\U000e0062\U000e007f" - FLAG_FOR_NAKHCHIVAN_AR_AZ_NX = "\U0001f3f4\U000e0061\U000e007a\U000e006e\U000e0078\U000e007f" - FLAG_FOR_SAINT_LUCY_BB_07 = "\U0001f3f4\U000e0062\U000e0062\U000e0030\U000e0037\U000e007f" - TAG_LATIN_SMALL_LETTER_N = "\U000e006e" - FLAG_FOR_INDIANA_US_IN = "\U0001f3f4\U000e0075\U000e0073\U000e0069\U000e006e\U000e007f" - FLAG_FOR_SAINT_JOSEPH_BB_06 = "\U0001f3f4\U000e0062\U000e0062\U000e0030\U000e0036\U000e007f" - FAMILY_MAN_WOMAN_BABY_BOY = "\U0001f468\u200d\U0001f469\u200d\U0001f476\u200d\U0001f466" - FLAG_FOR_TARTAR_AZ_TAR = "\U0001f3f4\U000e0061\U000e007a\U000e0074\U000e0061\U000e0072\U000e007f" - CONJUNCTION = "\u260c" - FLAG_FOR_SAINT_ROMAN_MC_SR = "\U0001f3f4\U000e006d\U000e0063\U000e0073\U000e0072\U000e007f" - FLAG_FOR_BREST_BY_BR = "\U0001f3f4\U000e0062\U000e0079\U000e0062\U000e0072\U000e007f" - WOMAN_IN_TUXEDO_MEDIUM_SKIN_TONE = "\U0001f935\U0001f3fd\u200d\u2640\ufe0f" - FLAG_FOR_SASKATCHEWAN_CA_SK = "\U0001f3f4\U000e0063\U000e0061\U000e0073\U000e006b\U000e007f" - FLAG_FOR_SAINT_GEORGE_VC_04 = "\U0001f3f4\U000e0076\U000e0063\U000e0030\U000e0034\U000e007f" - FAMILY_WOMAN_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f476\U0001f3fb\u200d\U0001f466\U0001f3fb" - MAHJONG_TILE_THREE_OF_CHARACTERS = "\U0001f009" - FLAG_FOR_SAINT_PHILIP_BB_10 = "\U0001f3f4\U000e0062\U000e0062\U000e0031\U000e0030\U000e007f" - FAMILY_MAN_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f476\U0001f3fb\u200d\U0001f467\U0001f3fb" - FLAG_FOR_ZARDAB_AZ_ZAR = "\U0001f3f4\U000e0061\U000e007a\U000e007a\U000e0061\U000e0072\U000e007f" - FAMILY_MAN_MAN_BABY_BOY = "\U0001f468\u200d\U0001f468\u200d\U0001f476\u200d\U0001f466" - FLAG_FOR_BARISAL_BD_A = "\U0001f3f4\U000e0062\U000e0064\U000e0061\U000e007f" - FLAG_FOR_SANTO_DOMINGO_DE_LOS_TSACHILAS_EC_SD = "\U0001f3f4\U000e0065\U000e0063\U000e0073\U000e0064\U000e007f" - FLAG_FOR_ZANGILAN_AZ_ZAN = "\U0001f3f4\U000e0061\U000e007a\U000e007a\U000e0061\U000e006e\U000e007f" - INTERLOCKED_FEMALE_AND_MALE_SIGN = "\u26a4" - FLAG_FOR_WESTERN_LK_1 = "\U0001f3f4\U000e006c\U000e006b\U000e0031\U000e007f" - FLAG_FOR_KINGSTON_JM_01 = "\U0001f3f4\U000e006a\U000e006d\U000e0030\U000e0031\U000e007f" - DIESEL_LOCOMOTIVE = "\U0001f6f2" - NOTE = "\U0001f5c8" - MAHJONG_TILE_EIGHT_OF_CHARACTERS = "\U0001f00e" - SIDEWAYS_WHITE_RIGHT_POINTING_INDEX = "\U0001f599" - TAG_LESS_THAN_SIGN = "\U000e003c" - FLAG_FOR_RAJSHAHI_DIVISION_BD_E = "\U0001f3f4\U000e0062\U000e0064\U000e0065\U000e007f" - KISS_MAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fc" - FAMILY_WOMAN_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f476\U0001f3ff" - FLAG_FOR_SARDINIA_IT_88 = "\U0001f3f4\U000e0069\U000e0074\U000e0038\U000e0038\U000e007f" - HEAVY_WHITE_DOWN_POINTING_TRIANGLE = "\u26db" - FLAG_FOR_KHULNA_DIVISION_BD_D = "\U0001f3f4\U000e0062\U000e0064\U000e0064\U000e007f" - FLAG_FOR_SAINT_PETER_BB_09 = "\U0001f3f4\U000e0062\U000e0062\U000e0030\U000e0039\U000e007f" - MUSIC_SHARP_SIGN = "\u266f" - RIGHT_HANDED_INTERLACED_PENTAGRAM = "\u26e5" - TAG_LATIN_SMALL_LETTER_C = "\U000e0063" - FLAG_FOR_ADDIS_ABABA_ET_AA = "\U0001f3f4\U000e0065\U000e0074\U000e0061\U000e0061\U000e007f" - FLAG_FOR_PORTLAND_JM_04 = "\U0001f3f4\U000e006a\U000e006d\U000e0030\U000e0034\U000e007f" - FLAG_FOR_MYMENSINGH_DIVISION_BD_H = "\U0001f3f4\U000e0062\U000e0064\U000e0068\U000e007f" - FLAG_FOR_PORT_OF_SPAIN_TT_POS = "\U0001f3f4\U000e0074\U000e0074\U000e0070\U000e006f\U000e0073\U000e007f" - FLAG_FOR_CASANARE_CO_CAS = "\U0001f3f4\U000e0063\U000e006f\U000e0063\U000e0061\U000e0073\U000e007f" - TAG_LATIN_CAPITAL_LETTER_Q = "\U000e0051" - TRIGRAM_FOR_THUNDER = "\u2633" - POCKET_CALCULATOR = "\U0001f5a9" - RIGHT_SPEAKER_WITH_ONE_SOUND_WAVE = "\U0001f569" - FLAG_FOR_LOS_SANTOS_PA_7 = "\U0001f3f4\U000e0070\U000e0061\U000e0037\U000e007f" - FLAG_FOR_LOWER_AUSTRIA_AT_3 = "\U0001f3f4\U000e0061\U000e0074\U000e0033\U000e007f" - FLAG_FOR_SUMQAYIT_AZ_SM = "\U0001f3f4\U000e0061\U000e007a\U000e0073\U000e006d\U000e007f" - FLAG_FOR_CASCADES_BF_02 = "\U0001f3f4\U000e0062\U000e0066\U000e0030\U000e0032\U000e007f" - FLAG_FOR_BOUCLE_DU_MOUHOUN_BF_01 = "\U0001f3f4\U000e0062\U000e0066\U000e0030\U000e0031\U000e007f" - FLAG_FOR_AL_HUDAYDAH_YE_HU = "\U0001f3f4\U000e0079\U000e0065\U000e0068\U000e0075\U000e007f" - FLAG_FOR_SAINT_PATRICK_GD_06 = "\U0001f3f4\U000e0067\U000e0064\U000e0030\U000e0036\U000e007f" - FLAG_FOR_NOVA_SCOTIA_CA_NS = "\U0001f3f4\U000e0063\U000e0061\U000e006e\U000e0073\U000e007f" - FLAG_FOR_MINAS_GERAIS_BR_MG = "\U0001f3f4\U000e0062\U000e0072\U000e006d\U000e0067\U000e007f" - FLAG_FOR_CENTRE_SUD_BF_07 = "\U0001f3f4\U000e0062\U000e0066\U000e0030\U000e0037\U000e007f" - MUSICAL_KEYBOARD_WITH_JACKS = "\U0001f398" - FLAG_FOR_BENGO_AO_BGO = "\U0001f3f4\U000e0061\U000e006f\U000e0062\U000e0067\U000e006f\U000e007f" - COUPLE_WITH_HEART_MAN_MEDIUM_SKIN_TONE_WOMAN_DARK_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3ff" - FLAG_FOR_CENTRE_OUEST_BF_06 = "\U0001f3f4\U000e0062\U000e0066\U000e0030\U000e0036\U000e007f" - GEAR_WITHOUT_HUB = "\u26ed" - FAMILY_MAN_LIGHT_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f469\U0001f3fb\u200d\U0001f476\U0001f3fb\u200d\U0001f476\U0001f3fb" - FLAG_FOR_CENTRE_BF_03 = "\U0001f3f4\U000e0062\U000e0066\U000e0030\U000e0033\U000e007f" - FAMILY_WOMAN_MAN_GIRL_GIRL = "\U0001f469\u200d\U0001f468\u200d\U0001f467\u200d\U0001f467" - WHITE_TWO_WAY_LEFT_WAY_TRAFFIC = "\u26d7" - FLAG_FOR_HOWLAND_ISLAND_UM_84 = "\U0001f3f4\U000e0075\U000e006d\U000e0038\U000e0034\U000e007f" - FLAG_FOR_EST_BF_08 = "\U0001f3f4\U000e0062\U000e0066\U000e0030\U000e0038\U000e007f" - FLAG_FOR_ELBASAN_COUNTY_AL_03 = "\U0001f3f4\U000e0061\U000e006c\U000e0030\U000e0033\U000e007f" - FLAG_FOR_RATAK_CHAIN_MH_T = "\U0001f3f4\U000e006d\U000e0068\U000e0074\U000e007f" - FLAG_FOR_CANARY_ISLANDS_ES_CN = "\U0001f3f4\U000e0065\U000e0073\U000e0063\U000e006e\U000e007f" - FLAG_FOR_PLATEAU_CENTRAL_BF_11 = "\U0001f3f4\U000e0062\U000e0066\U000e0031\U000e0031\U000e007f" - FLAG_FOR_DOUKKALA_ABDA_MA_10 = "\U0001f3f4\U000e006d\U000e0061\U000e0031\U000e0030\U000e007f" - FLAG_FOR_TERENGGANU_MY_11 = "\U0001f3f4\U000e006d\U000e0079\U000e0031\U000e0031\U000e007f" - MAN_WITH_HEADSCARF_MEDIUM_DARK_SKIN_TONE = "\U0001f9d5\U0001f3fe\u200d\u2642\ufe0f" - COUPLE_WITH_HEART_WOMAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fd" - FLAG_FOR_SANTA_CRUZ_AR_Z = "\U0001f3f4\U000e0061\U000e0072\U000e007a\U000e007f" - FLAG_FOR_DHAKA_DIVISION_BD_C = "\U0001f3f4\U000e0062\U000e0064\U000e0063\U000e007f" - FLAG_FOR_JUJUY_AR_Y = "\U0001f3f4\U000e0061\U000e0072\U000e0079\U000e007f" - CIRCLED_INFORMATION_SOURCE = "\U0001f6c8" - FLAG_FOR_SYLHET_DIVISION_BD_G = "\U0001f3f4\U000e0062\U000e0064\U000e0067\U000e007f" - FLAG_FOR_VIDIN_BG_05 = "\U0001f3f4\U000e0062\U000e0067\U000e0030\U000e0035\U000e007f" - FLAG_FOR_ARKHANGELSK_RU_ARK = "\U0001f3f4\U000e0072\U000e0075\U000e0061\U000e0072\U000e006b\U000e007f" - MAHJONG_TILE_ONE_OF_CHARACTERS = "\U0001f007" - FLAG_FOR_BADAKHSHAN_AF_BDS = "\U0001f3f4\U000e0061\U000e0066\U000e0062\U000e0064\U000e0073\U000e007f" - FLAG_FOR_ALGIERS_DZ_16 = "\U0001f3f4\U000e0064\U000e007a\U000e0031\U000e0036\U000e007f" - FLAG_FOR_STEPANAKERT_AZ_XA = "\U0001f3f4\U000e0061\U000e007a\U000e0078\U000e0061\U000e007f" - FLAG_FOR_GABROVO_BG_07 = "\U0001f3f4\U000e0062\U000e0067\U000e0030\U000e0037\U000e007f" - FLAG_FOR_BUENOS_AIRES_PROVINCE_AR_B = "\U0001f3f4\U000e0061\U000e0072\U000e0062\U000e007f" - FLAG_FOR_DOBRICH_BG_08 = "\U0001f3f4\U000e0062\U000e0067\U000e0030\U000e0038\U000e007f" - BLACK_PENNANT = "\U0001f3f2" - FLAG_FOR_CENTRE_EST_BF_04 = "\U0001f3f4\U000e0062\U000e0066\U000e0030\U000e0034\U000e007f" - COUPLE_WITH_HEART_MAN_DARK_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fb" - KISS_MAN_LIGHT_SKIN_TONE_WOMAN_DARK_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3ff" - FLAG_FOR_BAHIA_BR_BA = "\U0001f3f4\U000e0062\U000e0072\U000e0062\U000e0061\U000e007f" - COUPLE_WITH_HEART_WOMAN_MEDIUM_SKIN_TONE_MAN_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fb" - MAHJONG_TILE_FIVE_OF_CHARACTERS = "\U0001f00b" - KISS_MAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fe" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f469\U0001f3fc\u200d\U0001f476\U0001f3fc\u200d\U0001f467\U0001f3fc" - FLAG_FOR_AKITA_JP_05 = "\U0001f3f4\U000e006a\U000e0070\U000e0030\U000e0035\U000e007f" - FLAG_FOR_KYUSTENDIL_BG_10 = "\U0001f3f4\U000e0062\U000e0067\U000e0031\U000e0030\U000e007f" - CHIRON = "\u26b7" - FLAG_FOR_RALIK_CHAIN_MH_L = "\U0001f3f4\U000e006d\U000e0068\U000e006c\U000e007f" - FLAG_FOR_LOVECH_BG_11 = "\U0001f3f4\U000e0062\U000e0067\U000e0031\U000e0031\U000e007f" - FLAG_FOR_VAYOTS_DZOR_AM_VD = "\U0001f3f4\U000e0061\U000e006d\U000e0076\U000e0064\U000e007f" - FLAG_FOR_BANTEAY_MEANCHEY_KH_1 = "\U0001f3f4\U000e006b\U000e0068\U000e0031\U000e007f" - FLAG_FOR_PAZARDZHIK_BG_13 = "\U0001f3f4\U000e0062\U000e0067\U000e0031\U000e0033\U000e007f" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f468\U0001f3fe\u200d\U0001f476\U0001f3fe\u200d\U0001f467\U0001f3fe" - DIE_FACE_5 = "\u2684" - FAMILY_WOMAN_WOMAN_BABY_GIRL = "\U0001f469\u200d\U0001f469\u200d\U0001f476\u200d\U0001f467" - FLAG_FOR_BLAGOEVGRAD_BG_01 = "\U0001f3f4\U000e0062\U000e0067\U000e0030\U000e0031\U000e007f" - FLAG_FOR_CARRIACOU_AND_PETITE_MARTINIQUE_GD_10 = "\U0001f3f4\U000e0067\U000e0064\U000e0031\U000e0030\U000e007f" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f469\U0001f3fc\u200d\U0001f466\U0001f3fc" - FLAG_FOR_PLOVDIV_BG_16 = "\U0001f3f4\U000e0062\U000e0067\U000e0031\U000e0036\U000e007f" - FLAG_FOR_PLEVEN_BG_15 = "\U0001f3f4\U000e0062\U000e0067\U000e0031\U000e0035\U000e007f" - FLAG_FOR_NORD_BF_10 = "\U0001f3f4\U000e0062\U000e0066\U000e0031\U000e0030\U000e007f" - FLAG_FOR_KURDISTAN_IR_16 = "\U0001f3f4\U000e0069\U000e0072\U000e0031\U000e0036\U000e007f" - FLAG_FOR_DASHKASAN_AZ_DAS = "\U0001f3f4\U000e0061\U000e007a\U000e0064\U000e0061\U000e0073\U000e007f" - COUPLE_WITH_HEART_MAN_DARK_SKIN_TONE_MAN = "\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f468" - FLAG_FOR_BORDJ_BOU_ARRERIDJ_DZ_34 = "\U0001f3f4\U000e0064\U000e007a\U000e0033\U000e0034\U000e007f" - TAG_AMPERSAND = "\U000e0026" - FLAG_FOR_VALLEE_DU_BANDAMA_CI_VB = "\U0001f3f4\U000e0063\U000e0069\U000e0076\U000e0062\U000e007f" - FLAG_FOR_RAZGRAD_BG_17 = "\U0001f3f4\U000e0062\U000e0067\U000e0031\U000e0037\U000e007f" - FAMILY_WOMAN_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f476\U0001f3ff\u200d\U0001f466\U0001f3ff" - FLAG_FOR_SOFIA_BG_22 = "\U0001f3f4\U000e0062\U000e0067\U000e0032\U000e0032\U000e007f" - FLAG_FOR_SOFIA_DISTRICT_BG_23 = "\U0001f3f4\U000e0062\U000e0067\U000e0032\U000e0033\U000e007f" - FLAG_FOR_TARGOVISHTE_BG_25 = "\U0001f3f4\U000e0062\U000e0067\U000e0032\U000e0035\U000e007f" - FLAG_FOR_LOPBURI_TH_16 = "\U0001f3f4\U000e0074\U000e0068\U000e0031\U000e0036\U000e007f" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f468\U0001f3fe\u200d\U0001f467\U0001f3fe\u200d\U0001f466\U0001f3fe" - FLAG_FOR_CUNENE_AO_CNN = "\U0001f3f4\U000e0061\U000e006f\U000e0063\U000e006e\U000e006e\U000e007f" - FLAG_FOR_ADJARA_GE_AJ = "\U0001f3f4\U000e0067\U000e0065\U000e0061\U000e006a\U000e007f" - COUPLE_WITH_HEART_MAN_DARK_SKIN_TONE_WOMAN_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3ff" - FLAG_FOR_VARNA_BG_03 = "\U0001f3f4\U000e0062\U000e0067\U000e0030\U000e0033\U000e007f" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f468\U0001f3fc\u200d\U0001f476\U0001f3fc\u200d\U0001f467\U0001f3fc" - FLAG_FOR_YOBE_NG_YO = "\U0001f3f4\U000e006e\U000e0067\U000e0079\U000e006f\U000e007f" - FLAG_FOR_PERNIK_BG_14 = "\U0001f3f4\U000e0062\U000e0067\U000e0031\U000e0034\U000e007f" - FLAG_FOR_BAY_SOMALIA_SO_BY = "\U0001f3f4\U000e0073\U000e006f\U000e0062\U000e0079\U000e007f" - FLAG_FOR_SAINT_GEORGE_BB_03 = "\U0001f3f4\U000e0062\U000e0062\U000e0030\U000e0033\U000e007f" - FAMILY_WOMAN_LIGHT_SKIN_TONE_MAN_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f468\U0001f3fb\u200d\U0001f466\U0001f3fb\u200d\U0001f476\U0001f3fb" - WOMAN_WITH_HEADSCARF_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d5\U0001f3fc\u200d\u2640\ufe0f" - FLAG_FOR_WESTERN_CAPE_ZA_WC = "\U0001f3f4\U000e007a\U000e0061\U000e0077\U000e0063\U000e007f" - FAMILY_MAN_LIGHT_SKIN_TONE_MAN_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f468\U0001f3fb\u200d\U0001f467\U0001f3fb\u200d\U0001f466\U0001f3fb" - FLAG_FOR_HASKOVO_BG_26 = "\U0001f3f4\U000e0062\U000e0067\U000e0032\U000e0036\U000e007f" - FLAG_FOR_YAMBOL_BG_28 = "\U0001f3f4\U000e0062\U000e0067\U000e0032\U000e0038\U000e007f" - COUPLE_WITH_HEART_MAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fd" - FLAG_FOR_SCHAAN_LI_07 = "\U0001f3f4\U000e006c\U000e0069\U000e0030\U000e0037\U000e007f" - FLAG_FOR_STREDOCESKY_KRAJ_CZ_20 = "\U0001f3f4\U000e0063\U000e007a\U000e0032\U000e0030\U000e007f" - FLAG_FOR_ZAVRC_SI_143 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0034\U000e0033\U000e007f" - COUPLE_WITH_HEART_WOMAN_LIGHT_SKIN_TONE_MAN_DARK_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3ff" - FLAG_FOR_TACHIRA_VE_S = "\U0001f3f4\U000e0076\U000e0065\U000e0073\U000e007f" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f466\U0001f3fd\u200d\U0001f467\U0001f3fd" - FLAG_FOR_SOUTHERN_BH_14 = "\U0001f3f4\U000e0062\U000e0068\U000e0031\U000e0034\U000e007f" - FLAG_FOR_SIBIU_RO_SB = "\U0001f3f4\U000e0072\U000e006f\U000e0073\U000e0062\U000e007f" - COUPLE_WITH_HEART_WOMAN_LIGHT_SKIN_TONE_WOMAN = "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f469" - DIE_FACE_1 = "\u2680" - FAMILY_WOMAN_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f476\U0001f3fb\u200d\U0001f467\U0001f3fb" - FLAG_FOR_SOUTHERN_IS_8 = "\U0001f3f4\U000e0069\U000e0073\U000e0038\U000e007f" - FLAG_FOR_MUHARRAQ_BH_15 = "\U0001f3f4\U000e0062\U000e0068\U000e0031\U000e0035\U000e007f" - FLAG_FOR_BRUSSELS_BE_BRU = "\U0001f3f4\U000e0062\U000e0065\U000e0062\U000e0072\U000e0075\U000e007f" - KISS_MAN_MEDIUM_LIGHT_SKIN_TONE_MAN_DARK_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3ff" - WOMAN_WITH_HEADSCARF_MEDIUM_SKIN_TONE = "\U0001f9d5\U0001f3fd\u200d\u2640\ufe0f" - FLAG_FOR_CENTRE_NORD_BF_05 = "\U0001f3f4\U000e0062\U000e0066\U000e0030\U000e0035\U000e007f" - ENVELOPE_WITH_LIGHTNING = "\U0001f584" - FLAG_FOR_COLORADO_US_CO = "\U0001f3f4\U000e0075\U000e0073\U000e0063\U000e006f\U000e007f" - FLAG_FOR_CAPITAL_BH_13 = "\U0001f3f4\U000e0062\U000e0068\U000e0031\U000e0033\U000e007f" - FAMILY_WOMAN_LIGHT_SKIN_TONE_MAN_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f468\U0001f3fb\u200d\U0001f467\U0001f3fb" - FLAG_FOR_TATARSTAN_RU_TA = "\U0001f3f4\U000e0072\U000e0075\U000e0074\U000e0061\U000e007f" - FLAG_FOR_NORTHERN_BH_17 = "\U0001f3f4\U000e0062\U000e0068\U000e0031\U000e0037\U000e007f" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f469\U0001f3fc\u200d\U0001f467\U0001f3fc\u200d\U0001f467\U0001f3fc" - FLAG_FOR_BUJUMBURA_BI_BM = "\U0001f3f4\U000e0062\U000e0069\U000e0062\U000e006d\U000e007f" - WEST_SYRIAC_CROSS = "\u2670" - FLAG_FOR_SA_DAH_YE_SD = "\U0001f3f4\U000e0079\U000e0065\U000e0073\U000e0064\U000e007f" - FLAG_FOR_AL_QASSIM_SA_05 = "\U0001f3f4\U000e0073\U000e0061\U000e0030\U000e0035\U000e007f" - FLAG_FOR_CANKUZO_BI_CA = "\U0001f3f4\U000e0062\U000e0069\U000e0063\U000e0061\U000e007f" - FAMILY_MAN_LIGHT_SKIN_TONE_MAN_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f468\U0001f3fb\u200d\U0001f466\U0001f3fb\u200d\U0001f476\U0001f3fb" - FLAG_FOR_NORTHERN_TERRITORY_AU_NT = "\U0001f3f4\U000e0061\U000e0075\U000e006e\U000e0074\U000e007f" - CROSS_OF_JERUSALEM = "\u2629" - FLAG_FOR_CHUKOTKA_OKRUG_RU_CHU = "\U0001f3f4\U000e0072\U000e0075\U000e0063\U000e0068\U000e0075\U000e007f" - FLAG_FOR_KARDZHALI_BG_09 = "\U0001f3f4\U000e0062\U000e0067\U000e0030\U000e0039\U000e007f" - FLAG_FOR_SALA_LV_085 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0038\U000e0035\U000e007f" - FLAG_FOR_CENTRAL_DENMARK_DK_82 = "\U0001f3f4\U000e0064\U000e006b\U000e0038\U000e0032\U000e007f" - FLAG_FOR_BURURI_BI_BR = "\U0001f3f4\U000e0062\U000e0069\U000e0062\U000e0072\U000e007f" - FLAG_FOR_WALLONIA_BE_WAL = "\U0001f3f4\U000e0062\U000e0065\U000e0077\U000e0061\U000e006c\U000e007f" - KISS_MAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fd" - TAG_LATIN_SMALL_LETTER_G = "\U000e0067" - FLAG_FOR_MARTINIQUE_FR_MQ = "\U0001f3f4\U000e0066\U000e0072\U000e006d\U000e0071\U000e007f" - FLAG_FOR_KARUZI_BI_KR = "\U0001f3f4\U000e0062\U000e0069\U000e006b\U000e0072\U000e007f" - FLAG_FOR_BABYLON_IQ_BB = "\U0001f3f4\U000e0069\U000e0071\U000e0062\U000e0062\U000e007f" - FLAG_FOR_KIRUNDO_BI_KI = "\U0001f3f4\U000e0062\U000e0069\U000e006b\U000e0069\U000e007f" - FLAG_FOR_LANKARAN_DISTRICT_AZ_LAN = "\U0001f3f4\U000e0061\U000e007a\U000e006c\U000e0061\U000e006e\U000e007f" - FLAG_FOR_MURAMVYA_BI_MU = "\U0001f3f4\U000e0062\U000e0069\U000e006d\U000e0075\U000e007f" - FLAG_FOR_MUYINGA_BI_MY = "\U0001f3f4\U000e0062\U000e0069\U000e006d\U000e0079\U000e007f" - FLAG_FOR_TIANJIN_CN_12 = "\U0001f3f4\U000e0063\U000e006e\U000e0031\U000e0032\U000e007f" - FLAG_FOR_LAAYOUNE_BOUJDOUR_SAKIA_EL_HAMRA_MA_15 = "\U0001f3f4\U000e006d\U000e0061\U000e0031\U000e0035\U000e007f" - DIGRAM_FOR_GREATER_YANG = "\u268c" - FLAG_FOR_SHUMEN_BG_27 = "\U0001f3f4\U000e0062\U000e0067\U000e0032\U000e0037\U000e007f" - RECYCLED_PAPER_SYMBOL = "\u267c" - FLAG_FOR_NGOZI_BI_NG = "\U0001f3f4\U000e0062\U000e0069\U000e006e\U000e0067\U000e007f" - FAMILY_WOMAN_MAN_BOY_BOY = "\U0001f469\u200d\U0001f468\u200d\U0001f466\u200d\U0001f466" - KISS_WOMAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fd" - FLAG_FOR_SISTAN_AND_BALUCHESTAN_IR_13 = "\U0001f3f4\U000e0069\U000e0072\U000e0031\U000e0033\U000e007f" - FLAG_FOR_RUMONGE_BI_RM = "\U0001f3f4\U000e0062\U000e0069\U000e0072\U000e006d\U000e007f" - FLAG_FOR_AOSTA_VALLEY_IT_23 = "\U0001f3f4\U000e0069\U000e0074\U000e0032\U000e0033\U000e007f" - FLAG_FOR_NOVO_MESTO_SI_085 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0038\U000e0035\U000e007f" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f469\U0001f3fc\u200d\U0001f476\U0001f3fc" - BEAMED_SIXTEENTH_NOTES = "\u266c" - FAMILY_WOMAN_LIGHT_SKIN_TONE_MAN_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f468\U0001f3fb\u200d\U0001f466\U0001f3fb\u200d\U0001f467\U0001f3fb" - FLAG_FOR_HAUTS_DE_FRANCE_FR_HDF = "\U0001f3f4\U000e0066\U000e0072\U000e0068\U000e0064\U000e0066\U000e007f" - FLAG_FOR_RIO_NEGRO_AR_R = "\U0001f3f4\U000e0061\U000e0072\U000e0072\U000e007f" - WHITE_CHESS_KNIGHT = "\u2658" - FLAG_FOR_ALIBORI_BJ_AL = "\U0001f3f4\U000e0062\U000e006a\U000e0061\U000e006c\U000e007f" - FLAG_FOR_DONGA_BJ_DO = "\U0001f3f4\U000e0062\U000e006a\U000e0064\U000e006f\U000e007f" - FLAG_FOR_HAMBURG_DE_HH = "\U0001f3f4\U000e0064\U000e0065\U000e0068\U000e0068\U000e007f" - FLAG_FOR_SAHEL_BF_12 = "\U0001f3f4\U000e0062\U000e0066\U000e0031\U000e0032\U000e007f" - FLAG_FOR_ATAKORA_BJ_AK = "\U0001f3f4\U000e0062\U000e006a\U000e0061\U000e006b\U000e007f" - FLAG_FOR_RUYIGI_BI_RY = "\U0001f3f4\U000e0062\U000e0069\U000e0072\U000e0079\U000e007f" - COUPLE_WITH_HEART_WOMAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN = "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f469" - FLAG_FOR_MA_AN_JO_MN = "\U0001f3f4\U000e006a\U000e006f\U000e006d\U000e006e\U000e007f" - FLAG_FOR_SMOLYAN_BG_21 = "\U0001f3f4\U000e0062\U000e0067\U000e0032\U000e0031\U000e007f" - FLAG_FOR_LITTORAL_BJ_LI = "\U0001f3f4\U000e0062\U000e006a\U000e006c\U000e0069\U000e007f" - FAMILY_MAN_DARK_SKIN_TONE_MAN_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f468\U0001f3ff\u200d\U0001f476\U0001f3ff\u200d\U0001f476\U0001f3ff" - FLAG_FOR_ZOU_BJ_ZO = "\U0001f3f4\U000e0062\U000e006a\U000e007a\U000e006f\U000e007f" - FLAG_FOR_RED_SEA_EG_BA = "\U0001f3f4\U000e0065\U000e0067\U000e0062\U000e0061\U000e007f" - FLAG_FOR_VLORE_COUNTY_AL_12 = "\U0001f3f4\U000e0061\U000e006c\U000e0031\U000e0032\U000e007f" - FLAG_FOR_KOPER_SI_050 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0035\U000e0030\U000e007f" - KISS_WOMAN_DARK_SKIN_TONE_MAN_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fd" - FAMILY_WOMAN_DARK_SKIN_TONE_WOMAN_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f469\U0001f3ff\u200d\U0001f466\U0001f3ff\u200d\U0001f467\U0001f3ff" - FLAG_FOR_MANAWATU_WANGANUI_NZ_MWT = "\U0001f3f4\U000e006e\U000e007a\U000e006d\U000e0077\U000e0074\U000e007f" - FLAG_FOR_ENTRE_RIOS_AR_E = "\U0001f3f4\U000e0061\U000e0072\U000e0065\U000e007f" - FLAG_FOR_NORTH_DAKOTA_US_ND = "\U0001f3f4\U000e0075\U000e0073\U000e006e\U000e0064\U000e007f" - MEDIUM_SMALL_WHITE_CIRCLE = "\u26ac" - FLAG_FOR_BEQAA_LB_BI = "\U0001f3f4\U000e006c\U000e0062\U000e0062\U000e0069\U000e007f" - FLAG_FOR_SEKONG_LA_XE = "\U0001f3f4\U000e006c\U000e0061\U000e0078\U000e0065\U000e007f" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f468\U0001f3fc\u200d\U0001f466\U0001f3fc\u200d\U0001f467\U0001f3fc" - FLAG_FOR_OUEME_BJ_OU = "\U0001f3f4\U000e0062\U000e006a\U000e006f\U000e0075\U000e007f" - COUPLE_WITH_HEART_MAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fc" - BALLOT_BOX_WITH_SCRIPT_X = "\U0001f5f5" - FLAG_FOR_TUTONG_BN_TU = "\U0001f3f4\U000e0062\U000e006e\U000e0074\U000e0075\U000e007f" - COUPLE_WITH_HEART_WOMAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fc" - FLAG_FOR_VRATSA_BG_06 = "\U0001f3f4\U000e0062\U000e0067\U000e0030\U000e0036\U000e007f" - MAN_WITH_HEADSCARF = "\U0001f9d5\u200d\u2642\ufe0f" - COUPLE_WITH_HEART_WOMAN_MEDIUM_LIGHT_SKIN_TONE_MAN = "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f468" - FAMILY_MAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f469\U0001f3fd\u200d\U0001f476\U0001f3fd\u200d\U0001f467\U0001f3fd" - FLAG_FOR_KHENTII_MN_039 = "\U0001f3f4\U000e006d\U000e006e\U000e0030\U000e0033\U000e0039\U000e007f" - FLAG_FOR_DELTA_AMACURO_VE_Y = "\U0001f3f4\U000e0076\U000e0065\U000e0079\U000e007f" - RECYCLING_SYMBOL_FOR_GENERIC_MATERIALS = "\u267a" - FLAG_FOR_ASHANTI_GH_AH = "\U0001f3f4\U000e0067\U000e0068\U000e0061\U000e0068\U000e007f" - FLAG_FOR_DOLNENI_MK_27 = "\U0001f3f4\U000e006d\U000e006b\U000e0032\U000e0037\U000e007f" - BLACK_TRUCK = "\u26df" - KISS_WOMAN_WOMAN_MEDIUM_DARK_SKIN_TONE = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fe" - FLAG_FOR_BORGOU_BJ_BO = "\U0001f3f4\U000e0062\U000e006a\U000e0062\U000e006f\U000e007f" - FAMILY_MAN_DARK_SKIN_TONE_WOMAN_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f469\U0001f3ff\u200d\U0001f476\U0001f3ff\u200d\U0001f476\U0001f3ff" - FLAG_FOR_SAINT_JAMES_BB_04 = "\U0001f3f4\U000e0062\U000e0062\U000e0030\U000e0034\U000e007f" - FLAG_FOR_PANDO_BO_N = "\U0001f3f4\U000e0062\U000e006f\U000e006e\U000e007f" - FLAG_FOR_LA_PAZ_BO_L = "\U0001f3f4\U000e0062\U000e006f\U000e006c\U000e007f" - FAMILY_WOMAN_DARK_SKIN_TONE_WOMAN_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f469\U0001f3ff\u200d\U0001f466\U0001f3ff" - FLAG_FOR_STARA_ZAGORA_BG_24 = "\U0001f3f4\U000e0062\U000e0067\U000e0032\U000e0034\U000e007f" - FLAG_FOR_CHUQUISACA_BO_H = "\U0001f3f4\U000e0062\U000e006f\U000e0068\U000e007f" - FLAG_FOR_GUMUSHANE_TR_29 = "\U0001f3f4\U000e0074\U000e0072\U000e0032\U000e0039\U000e007f" - FLAG_FOR_PANJSHIR_AF_PAN = "\U0001f3f4\U000e0061\U000e0066\U000e0070\U000e0061\U000e006e\U000e007f" - FLAG_FOR_COCHABAMBA_BO_C = "\U0001f3f4\U000e0062\U000e006f\U000e0063\U000e007f" - FLAG_FOR_ROCHE_CAIMAN_SC_25 = "\U0001f3f4\U000e0073\U000e0063\U000e0032\U000e0035\U000e007f" - FAMILY_WOMAN_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f466\U0001f3ff\u200d\U0001f467\U0001f3ff" - COUPLE_WITH_HEART_WOMAN_MEDIUM_DARK_SKIN_TONE_MAN = "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f468" - COUPLE_WITH_HEART_WOMAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fd" - FLAG_FOR_BRCKO_DISTRICT_BA_BRC = "\U0001f3f4\U000e0062\U000e0061\U000e0062\U000e0072\U000e0063\U000e007f" - FLAG_FOR_BRETAGNE_FR_BRE = "\U0001f3f4\U000e0066\U000e0072\U000e0062\U000e0072\U000e0065\U000e007f" - COUPLE_WITH_HEART_WOMAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fe" - FLAG_FOR_CHECHEN_RU_CE = "\U0001f3f4\U000e0072\U000e0075\U000e0063\U000e0065\U000e007f" - FLAG_FOR_KICEVO_MK_40 = "\U0001f3f4\U000e006d\U000e006b\U000e0034\U000e0030\U000e007f" - FLAG_FOR_SAINT_JOHN_BB_05 = "\U0001f3f4\U000e0062\U000e0062\U000e0030\U000e0035\U000e007f" - FLAG_FOR_NORTHERN_MARIANA_ISLANDS_US_MP = "\U0001f3f4\U000e0075\U000e0073\U000e006d\U000e0070\U000e007f" - KISS_WOMAN_MAN_LIGHT_SKIN_TONE = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fb" - FLAG_FOR_KAUNAS_COUNTY_LT_KU = "\U0001f3f4\U000e006c\U000e0074\U000e006b\U000e0075\U000e007f" - FLAG_FOR_GJIROKASTER_COUNTY_AL_05 = "\U0001f3f4\U000e0061\U000e006c\U000e0030\U000e0035\U000e007f" - KISS_MAN_DARK_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fe" - KISS_WOMAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fc" - KISS_MAN_DARK_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fe" - COUPLE_WITH_HEART_WOMAN_DARK_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fd" - KISS_WOMAN_MAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fc" - FLAG_FOR_LENART_SI_058 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0035\U000e0038\U000e007f" - FLAG_FOR_TEMBURONG_BN_TE = "\U0001f3f4\U000e0062\U000e006e\U000e0074\U000e0065\U000e007f" - FLAG_FOR_BELAIT_BN_BE = "\U0001f3f4\U000e0062\U000e006e\U000e0062\U000e0065\U000e007f" - COUPLE_WITH_HEART_MAN_MEDIUM_LIGHT_SKIN_TONE_MAN_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fb" - KISS_WOMAN_LIGHT_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fd" - COUPLE_WITH_HEART_MAN_MEDIUM_SKIN_TONE_MAN_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fb" - FAMILY_MAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f469\U0001f3fd\u200d\U0001f467\U0001f3fd\u200d\U0001f466\U0001f3fd" - FLAG_FOR_POTOSI_BO_P = "\U0001f3f4\U000e0062\U000e006f\U000e0070\U000e007f" - KISS_MAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fd" - KISS_MAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fc" - MAN_WITH_HEADSCARF_DARK_SKIN_TONE = "\U0001f9d5\U0001f3ff\u200d\u2642\ufe0f" - KISS_WOMAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fe" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f466\U0001f3fe\u200d\U0001f467\U0001f3fe" - COUPLE_WITH_HEART_WOMAN_DARK_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fc" - SQUARE_FOUR_CORNERS = "\u26f6" - KISS_WOMAN_LIGHT_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fc" - KISS_MAN_MEDIUM_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fb" - MAHJONG_TILE_ONE_OF_BAMBOOS = "\U0001f010" - FLAG_FOR_TARIJA_BO_T = "\U0001f3f4\U000e0062\U000e006f\U000e0074\U000e007f" - BLACK_CHESS_ROOK = "\u265c" - FLAG_FOR_PREAH_VIHEAR_KH_13 = "\U0001f3f4\U000e006b\U000e0068\U000e0031\U000e0033\U000e007f" - TRIGRAM_FOR_WATER = "\u2635" - FLAG_FOR_VICTORIA_AU_VIC = "\U0001f3f4\U000e0061\U000e0075\U000e0076\U000e0069\U000e0063\U000e007f" - KISS_MAN_MEDIUM_DARK_SKIN_TONE_MAN_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3ff" - FLAG_FOR_ANDORRA_LA_VELLA_AD_07 = "\U0001f3f4\U000e0061\U000e0064\U000e0030\U000e0037\U000e007f" - KISS_WOMAN_MAN_MEDIUM_DARK_SKIN_TONE = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fe" - FLAG_FOR_ACRE_BR_AC = "\U0001f3f4\U000e0062\U000e0072\U000e0061\U000e0063\U000e007f" - KISS_MAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fc" - FLAG_FOR_URI_CH_UR = "\U0001f3f4\U000e0063\U000e0068\U000e0075\U000e0072\U000e007f" - KISS_MAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fd" - KISS_WOMAN_DARK_SKIN_TONE_MAN_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3ff" - COUPLE_WITH_HEART_MAN_LIGHT_SKIN_TONE_MAN_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fd" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f466\U0001f3fc\u200d\U0001f467\U0001f3fc" - KISS_WOMAN_DARK_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fc" - FLAG_FOR_SAN_SALVADOR_SV_SS = "\U0001f3f4\U000e0073\U000e0076\U000e0073\U000e0073\U000e007f" - KISS_MAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fe" - KISS_WOMAN_MEDIUM_DARK_SKIN_TONE_WOMAN_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3ff" - FLAG_FOR_MADRID_AUTONOMOUS_COMMUNITY_ES_MD = "\U0001f3f4\U000e0065\U000e0073\U000e006d\U000e0064\U000e007f" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f469\U0001f3fe\u200d\U0001f466\U0001f3fe" - FLAG_FOR_COLLINES_BJ_CO = "\U0001f3f4\U000e0062\U000e006a\U000e0063\U000e006f\U000e007f" - KISS_WOMAN_MEDIUM_DARK_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fb" - FLAG_FOR_KAYANZA_BI_KY = "\U0001f3f4\U000e0062\U000e0069\U000e006b\U000e0079\U000e007f" - FLAG_FOR_AOMORI_JP_02 = "\U0001f3f4\U000e006a\U000e0070\U000e0030\U000e0032\U000e007f" - TURNED_OK_HAND_SIGN = "\U0001f58f" - BOOK = "\U0001f56e" - COUPLE_WITH_HEART_MAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fc" - COUPLE_WITH_HEART_MAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fd" - KISS_WOMAN_DARK_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fe" - FLAG_FOR_FARO_PT_08 = "\U0001f3f4\U000e0070\U000e0074\U000e0030\U000e0038\U000e007f" - COUPLE_WITH_HEART_MAN_DARK_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fc" - COUPLE_WITH_HEART_MAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fe" - FALLING_DIAGONAL_IN_WHITE_CIRCLE_IN_BLACK_SQUARE = "\u26de" - FLAG_FOR_FRENCH_SOUTHERN_TERRITORIES_FR_TF = "\U0001f3f4\U000e0066\U000e0072\U000e0074\U000e0066\U000e007f" - COUPLE_WITH_HEART_MAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fc" - FLAG_FOR_ISTANBUL_TR_34 = "\U0001f3f4\U000e0074\U000e0072\U000e0033\U000e0034\U000e007f" - FAMILY_MAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f468\U0001f3fd\u200d\U0001f476\U0001f3fd\u200d\U0001f467\U0001f3fd" - FLAG_FOR_SUD_OUEST_BF_13 = "\U0001f3f4\U000e0062\U000e0066\U000e0031\U000e0033\U000e007f" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f469\U0001f3fc\u200d\U0001f466\U0001f3fc\u200d\U0001f476\U0001f3fc" - FLAG_FOR_FEDERAL_DEPENDENCIES_VE_W = "\U0001f3f4\U000e0076\U000e0065\U000e0077\U000e007f" - COUPLE_WITH_HEART_MAN_DARK_SKIN_TONE_MAN_LIGHT_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fb" - KISS_MAN_MEDIUM_DARK_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fb" - FAMILY_MAN_WOMAN_GIRL_BABY = "\U0001f468\u200d\U0001f469\u200d\U0001f467\u200d\U0001f476" - FLAG_FOR_LONG_ISLAND_BS_LI = "\U0001f3f4\U000e0062\U000e0073\U000e006c\U000e0069\U000e007f" - FLAG_FOR_RANGPUR_DIVISION_BD_F = "\U0001f3f4\U000e0062\U000e0064\U000e0066\U000e007f" - COUPLE_WITH_HEART_MAN_MEDIUM_DARK_SKIN_TONE_MAN_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3ff" - FLAG_FOR_ALAGOAS_BR_AL = "\U0001f3f4\U000e0062\U000e0072\U000e0061\U000e006c\U000e007f" - COUPLE_WITH_HEART_MAN_DARK_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fe" - KISS_WOMAN_MAN_DARK_SKIN_TONE = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3ff" - FLAG_FOR_MAIDAN_WARDAK_AF_WAR = "\U0001f3f4\U000e0061\U000e0066\U000e0077\U000e0061\U000e0072\U000e007f" - COUPLE_WITH_HEART_MAN_DARK_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fd" - COUPLE_WITH_HEART_MAN_MEDIUM_DARK_SKIN_TONE_WOMAN_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3ff" - FAMILY_MAN_LIGHT_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f469\U0001f3fb\u200d\U0001f476\U0001f3fb\u200d\U0001f467\U0001f3fb" - COUPLE_WITH_HEART_WOMAN_MAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fc" - FLAG_FOR_JALISCO_MX_JAL = "\U0001f3f4\U000e006d\U000e0078\U000e006a\U000e0061\U000e006c\U000e007f" - FLAG_FOR_LAZIO_IT_62 = "\U0001f3f4\U000e0069\U000e0074\U000e0036\U000e0032\U000e007f" - FLAG_FOR_SERGIPE_BR_SE = "\U0001f3f4\U000e0062\U000e0072\U000e0073\U000e0065\U000e007f" - KISS_MAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fd" - FLAG_FOR_KOUFFO_BJ_KO = "\U0001f3f4\U000e0062\U000e006a\U000e006b\U000e006f\U000e007f" - TAG_LATIN_SMALL_LETTER_L = "\U000e006c" - TAG_LATIN_SMALL_LETTER_J = "\U000e006a" - FLAG_FOR_BAKU_AZ_BA = "\U0001f3f4\U000e0061\U000e007a\U000e0062\U000e0061\U000e007f" - FLAG_FOR_DAUGAVPILS_MUNICIPALITY_LV_025 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0032\U000e0035\U000e007f" - KISS_WOMAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fd" - FLAG_FOR_VINH_LONG_VN_49 = "\U0001f3f4\U000e0076\U000e006e\U000e0034\U000e0039\U000e007f" - COUPLE_WITH_HEART_MAN_MEDIUM_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fb" - COUPLE_WITH_HEART_WOMAN_MEDIUM_DARK_SKIN_TONE_MAN_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3ff" - FLAG_FOR_STRASENI_MD_ST = "\U0001f3f4\U000e006d\U000e0064\U000e0073\U000e0074\U000e007f" - FLAG_FOR_RORAIMA_BR_RR = "\U0001f3f4\U000e0062\U000e0072\U000e0072\U000e0072\U000e007f" - FAMILY_MAN_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f467\U0001f3fb\u200d\U0001f466\U0001f3fb" - FLAG_FOR_BIMINI_BS_BI = "\U0001f3f4\U000e0062\U000e0073\U000e0062\U000e0069\U000e007f" - COUPLE_WITH_HEART_MAN_DARK_SKIN_TONE_MAN_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fd" - FLAG_FOR_NORTHERN_RED_SEA_ER_SK = "\U0001f3f4\U000e0065\U000e0072\U000e0073\U000e006b\U000e007f" - COUPLE_WITH_HEART_WOMAN_MEDIUM_LIGHT_SKIN_TONE_MAN_DARK_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3ff" - FLAG_FOR_TIARET_DZ_14 = "\U0001f3f4\U000e0064\U000e007a\U000e0031\U000e0034\U000e007f" - FLAG_FOR_SOGN_OG_FJORDANE_NO_14 = "\U0001f3f4\U000e006e\U000e006f\U000e0031\U000e0034\U000e007f" - WHITE_LEFT_LANE_MERGE = "\u26d9" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f468\U0001f3fc\u200d\U0001f467\U0001f3fc\u200d\U0001f467\U0001f3fc" - FLAG_FOR_KHACHMAZ_AZ_XAC = "\U0001f3f4\U000e0061\U000e007a\U000e0078\U000e0061\U000e0063\U000e007f" - COUPLE_WITH_HEART_MAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fb" - COUPLE_WITH_HEART_WOMAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fd" - FLAG_FOR_SVETI_ANDRAZ_V_SLOVENSKIH_GORICAH_SI_182 = "\U0001f3f4\U000e0073\U000e0069\U000e0031\U000e0038\U000e0032\U000e007f" - FLAG_FOR_BALOCHISTAN_PK_BA = "\U0001f3f4\U000e0070\U000e006b\U000e0062\U000e0061\U000e007f" - FAMILY_WOMAN_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f476\U0001f3fb" - FLAG_FOR_TOCANTINS_BR_TO = "\U0001f3f4\U000e0062\U000e0072\U000e0074\U000e006f\U000e007f" - FAMILY_WOMAN_MAN_BABY_GIRL = "\U0001f469\u200d\U0001f468\u200d\U0001f476\u200d\U0001f467" - TAG_SEMICOLON = "\U000e003b" - FLAG_FOR_EAST_GRAND_BAHAMA_BS_EG = "\U0001f3f4\U000e0062\U000e0073\U000e0065\U000e0067\U000e007f" - FLAG_FOR_SOUTH_AEGEAN_GR_L = "\U0001f3f4\U000e0067\U000e0072\U000e006c\U000e007f" - WOMAN_IN_BUSINESS_SUIT_LEVITATING = "\U0001f574\ufe0f\u200d\u2640\ufe0f" - FLAG_FOR_MAKAMBA_BI_MA = "\U0001f3f4\U000e0062\U000e0069\U000e006d\U000e0061\U000e007f" - COUPLE_WITH_HEART_WOMAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fc" - FLAG_FOR_CROOKED_ISLAND_BS_CK = "\U0001f3f4\U000e0062\U000e0073\U000e0063\U000e006b\U000e007f" - KISS_MAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fc" - TAG_LATIN_SMALL_LETTER_K = "\U000e006b" - FLAG_FOR_EXUMA_BS_EX = "\U0001f3f4\U000e0062\U000e0073\U000e0065\U000e0078\U000e007f" - COUPLE_WITH_HEART_MAN_DARK_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fe" - FAMILY_MAN_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f467\U0001f3fb" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f467\U0001f3fc" - FLAG_FOR_PLATEAU_BJ_PL = "\U0001f3f4\U000e0062\U000e006a\U000e0070\U000e006c\U000e007f" - KISS_WOMAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fc" - FLAG_FOR_FEDERAL_DISTRICT_BR_DF = "\U0001f3f4\U000e0062\U000e0072\U000e0064\U000e0066\U000e007f" - FLAG_FOR_NEW_SOUTH_WALES_AU_NSW = "\U0001f3f4\U000e0061\U000e0075\U000e006e\U000e0073\U000e0077\U000e007f" - FLAG_FOR_NORTH_ABACO_BS_NO = "\U0001f3f4\U000e0062\U000e0073\U000e006e\U000e006f\U000e007f" - FAMILY_WOMAN_DARK_SKIN_TONE_WOMAN_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f469\U0001f3ff\u200d\U0001f467\U0001f3ff\u200d\U0001f467\U0001f3ff" - FLAG_FOR_JERUSALEM_IL_JM = "\U0001f3f4\U000e0069\U000e006c\U000e006a\U000e006d\U000e007f" - FLAG_FOR_INAGUA_BS_IN = "\U0001f3f4\U000e0062\U000e0073\U000e0069\U000e006e\U000e007f" - FLAG_FOR_BOHINJ_SI_004 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0030\U000e0034\U000e007f" - FLAG_FOR_FREEPORT_BS_FP = "\U0001f3f4\U000e0062\U000e0073\U000e0066\U000e0070\U000e007f" - FAMILY_MAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f468\U0001f3fd\u200d\U0001f466\U0001f3fd\u200d\U0001f476\U0001f3fd" - FLAG_FOR_CENTRAL_ELEUTHERA_BS_CE = "\U0001f3f4\U000e0062\U000e0073\U000e0063\U000e0065\U000e007f" - FLAG_FOR_GRAND_CAY_BS_GC = "\U0001f3f4\U000e0062\U000e0073\U000e0067\U000e0063\U000e007f" - TAG_LATIN_SMALL_LETTER_A = "\U000e0061" - FLAG_FOR_MAYAGUANA_BS_MG = "\U0001f3f4\U000e0062\U000e0073\U000e006d\U000e0067\U000e007f" - FLAG_FOR_SOUTH_AUSTRALIA_AU_SA = "\U0001f3f4\U000e0061\U000e0075\U000e0073\U000e0061\U000e007f" - FAMILY_WOMAN_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f467\U0001f3fb\u200d\U0001f466\U0001f3fb" - COUPLE_WITH_HEART_WOMAN_DARK_SKIN_TONE_MAN = "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f468" - COUPLE_WITH_HEART_WOMAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fc" - FLAG_FOR_KAOHSIUNG_TW_KHH = "\U0001f3f4\U000e0074\U000e0077\U000e006b\U000e0068\U000e0068\U000e007f" - WOMAN_IN_BUSINESS_SUIT_LEVITATING_MEDIUM_DARK_SKIN_TONE = "\U0001f574\U0001f3fe\u200d\u2640\ufe0f" - FLAG_FOR_TRANS_NZOIA_KE_42 = "\U0001f3f4\U000e006b\U000e0065\U000e0034\U000e0032\U000e007f" - COUPLE_WITH_HEART_WOMAN_DARK_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fb" - FLAG_FOR_NORTH_ANDROS_BS_NS = "\U0001f3f4\U000e0062\U000e0073\U000e006e\U000e0073\U000e007f" - COUPLE_WITH_HEART_MAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fd" - FAMILY_WOMAN_MAN_BOY_BABY = "\U0001f469\u200d\U0001f468\u200d\U0001f466\u200d\U0001f476" - KISS_MAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fd" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f467\U0001f3fd\u200d\U0001f476\U0001f3fd" - FAMILY_MAN_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f466\U0001f3fd\u200d\U0001f476\U0001f3fd" - FLAG_FOR_OREGON_US_OR = "\U0001f3f4\U000e0075\U000e0073\U000e006f\U000e0072\U000e007f" - KISS_MAN_LIGHT_SKIN_TONE_MAN = "\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468" - FAMILY_MAN_BOY_BABY = "\U0001f468\u200d\U0001f466\u200d\U0001f476" - FAMILY_WOMAN_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f466\U0001f3fb\u200d\U0001f467\U0001f3fb" - KISS_MAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fe" - FLAG_FOR_OCCITANIE_FR_OCC = "\U0001f3f4\U000e0066\U000e0072\U000e006f\U000e0063\U000e0063\U000e007f" - FLAG_FOR_EAST_AZERBAIJAN_IR_01 = "\U0001f3f4\U000e0069\U000e0072\U000e0030\U000e0031\U000e007f" - FLAG_FOR_SOUTH_ANDROS_BS_SA = "\U0001f3f4\U000e0062\U000e0073\U000e0073\U000e0061\U000e007f" - FLAG_FOR_AMAZONAS_BR_AM = "\U0001f3f4\U000e0062\U000e0072\U000e0061\U000e006d\U000e007f" - FLAG_FOR_TANA_RIVER_KE_40 = "\U0001f3f4\U000e006b\U000e0065\U000e0034\U000e0030\U000e007f" - KISS_MAN_WOMAN_MEDIUM_DARK_SKIN_TONE = "\U0001f468\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fe" - GEAR_WITH_HANDLES = "\u26ee" - FLAG_FOR_SOUTH_ELEUTHERA_BS_SE = "\U0001f3f4\U000e0062\U000e0073\U000e0073\U000e0065\U000e007f" - COUPLE_WITH_HEART_WOMAN_MEDIUM_SKIN_TONE_WOMAN = "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f469" - KISS_WOMAN_MEDIUM_LIGHT_SKIN_TONE_MAN_DARK_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3ff" - KISS_MAN_MEDIUM_DARK_SKIN_TONE_MAN_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fb" - KISS_MAN_LIGHT_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fc" - KISS_MAN_LIGHT_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fb" - KISS_WOMAN_LIGHT_SKIN_TONE_MAN = "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468" - COUPLE_WITH_HEART_MAN_MAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fc" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f468\U0001f3fe\u200d\U0001f467\U0001f3fe\u200d\U0001f467\U0001f3fe" - KISS_WOMAN_MEDIUM_DARK_SKIN_TONE_MAN_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fb" - COUPLE_WITH_HEART_WOMAN_MEDIUM_SKIN_TONE_MAN = "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f468" - KISS_WOMAN_MEDIUM_SKIN_TONE_MAN_DARK_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3ff" - FLAG_FOR_BUDAPEST_HU_BU = "\U0001f3f4\U000e0068\U000e0075\U000e0062\U000e0075\U000e007f" - KISS_MAN_DARK_SKIN_TONE_MAN_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3ff" - KISS_MAN_WOMAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fc" - SIDEWAYS_BLACK_RIGHT_POINTING_INDEX = "\U0001f59b" - KISS_WOMAN_MEDIUM_DARK_SKIN_TONE_MAN_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3ff" - KISS_WOMAN_LIGHT_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fe" - KISS_WOMAN_DARK_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fc" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f466\U0001f3fc" - FLAG_FOR_SOUTH_ABACO_BS_SO = "\U0001f3f4\U000e0062\U000e0073\U000e0073\U000e006f\U000e007f" - KISS_WOMAN_LIGHT_SKIN_TONE_MAN_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fb" - COUPLE_WITH_HEART_MAN_DARK_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fc" - FLAG_FOR_RUM_CAY_BS_RC = "\U0001f3f4\U000e0062\U000e0073\U000e0072\U000e0063\U000e007f" - TAG_SOLIDUS = "\U000e002f" - COUPLE_WITH_HEART_MAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_DARK_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3ff" - FAMILY_MAN_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f466\U0001f3fd" - COUPLE_WITH_HEART_MAN_MEDIUM_SKIN_TONE_MAN_DARK_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3ff" - FAMILY_MAN_DARK_SKIN_TONE_WOMAN_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f469\U0001f3ff\u200d\U0001f476\U0001f3ff\u200d\U0001f467\U0001f3ff" - FAMILY_MAN_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f466\U0001f3ff\u200d\U0001f476\U0001f3ff" - COUPLE_WITH_HEART_MAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fe" - FAMILY_WOMAN_BOY_GIRL = "\U0001f469\u200d\U0001f466\u200d\U0001f467" - COUPLE_WITH_HEART_MAN_LIGHT_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fe" - FLAG_FOR_ANSE_ROYALE_SC_05 = "\U0001f3f4\U000e0073\U000e0063\U000e0030\U000e0035\U000e007f" - FLAG_FOR_DELHI_IN_DL = "\U0001f3f4\U000e0069\U000e006e\U000e0064\U000e006c\U000e007f" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f468\U0001f3fc\u200d\U0001f476\U0001f3fc\u200d\U0001f476\U0001f3fc" - FAMILY_MAN_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f467\U0001f3fb\u200d\U0001f476\U0001f3fb" - KISS_WOMAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fd" - FLAG_FOR_THIMPHU_BT_15 = "\U0001f3f4\U000e0062\U000e0074\U000e0031\U000e0035\U000e007f" - FLAG_FOR_CIEGO_DE_AVILA_CU_08 = "\U0001f3f4\U000e0063\U000e0075\U000e0030\U000e0038\U000e007f" - FLAG_FOR_PARO_BT_11 = "\U0001f3f4\U000e0062\U000e0074\U000e0031\U000e0031\U000e007f" - KISS_WOMAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fe" - FLAG_FOR_HAA_BT_13 = "\U0001f3f4\U000e0062\U000e0074\U000e0031\U000e0033\U000e007f" - FLAG_FOR_MADHYA_PRADESH_IN_MP = "\U0001f3f4\U000e0069\U000e006e\U000e006d\U000e0070\U000e007f" - KISS_MAN_LIGHT_SKIN_TONE_MAN_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fd" - FAMILY_WOMAN_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f466\U0001f3fb" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f469\U0001f3fe\u200d\U0001f476\U0001f3fe\u200d\U0001f466\U0001f3fe" - FLAG_FOR_WEST_GRAND_BAHAMA_BS_WG = "\U0001f3f4\U000e0062\U000e0073\U000e0077\U000e0067\U000e007f" - FLAG_FOR_PYONGYANG_KP_01 = "\U0001f3f4\U000e006b\U000e0070\U000e0030\U000e0031\U000e007f" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f469\U0001f3fd\u200d\U0001f466\U0001f3fd\u200d\U0001f467\U0001f3fd" - FLAG_FOR_DAGANA_BT_22 = "\U0001f3f4\U000e0062\U000e0074\U000e0032\U000e0032\U000e007f" - FLAG_FOR_ZHEMGANG_BT_34 = "\U0001f3f4\U000e0062\U000e0074\U000e0033\U000e0034\U000e007f" - FLAG_FOR_SARPANG_BT_31 = "\U0001f3f4\U000e0062\U000e0074\U000e0033\U000e0031\U000e007f" - KISS_WOMAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fd" - FLAG_FOR_KUNDUZ_AF_KDZ = "\U0001f3f4\U000e0061\U000e0066\U000e006b\U000e0064\U000e007a\U000e007f" - FLAG_FOR_TRASHIGANG_BT_41 = "\U0001f3f4\U000e0062\U000e0074\U000e0034\U000e0031\U000e007f" - COUPLE_WITH_HEART_WOMAN_DARK_SKIN_TONE_MAN_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fd" - FLAG_FOR_TRONGSA_BT_32 = "\U0001f3f4\U000e0062\U000e0074\U000e0033\U000e0032\U000e007f" - FLAG_FOR_BUMTHANG_BT_33 = "\U0001f3f4\U000e0062\U000e0074\U000e0033\U000e0033\U000e007f" - FLAG_FOR_MONGAR_BT_42 = "\U0001f3f4\U000e0062\U000e0074\U000e0034\U000e0032\U000e007f" - FAMILY_MAN_DARK_SKIN_TONE_MAN_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f468\U0001f3ff\u200d\U0001f466\U0001f3ff\u200d\U0001f466\U0001f3ff" - KISS_WOMAN_LIGHT_SKIN_TONE_MAN_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fd" - FLAG_FOR_PEMAGATSHEL_BT_43 = "\U0001f3f4\U000e0062\U000e0074\U000e0034\U000e0033\U000e007f" - FLAG_FOR_ACKLINS_BS_AK = "\U0001f3f4\U000e0062\U000e0073\U000e0061\U000e006b\U000e007f" - FLAG_FOR_CHOBE_BW_CH = "\U0001f3f4\U000e0062\U000e0077\U000e0063\U000e0068\U000e007f" - FLAG_FOR_SALTA_AR_A = "\U0001f3f4\U000e0061\U000e0072\U000e0061\U000e007f" - FLAG_FOR_LHUNTSE_BT_44 = "\U0001f3f4\U000e0062\U000e0074\U000e0034\U000e0034\U000e007f" - FLAG_FOR_TRASHIYANGTSE_BT_TY = "\U0001f3f4\U000e0062\U000e0074\U000e0074\U000e0079\U000e007f" - COUPLE_WITH_HEART_MAN_WOMAN_MEDIUM_DARK_SKIN_TONE = "\U0001f468\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fe" - FLAG_FOR_NEW_MEXICO_US_NM = "\U0001f3f4\U000e0075\U000e0073\U000e006e\U000e006d\U000e007f" - FAMILY_MAN_MAN_BABY = "\U0001f468\u200d\U0001f468\u200d\U0001f476" - LIGHTNING_MOOD_BUBBLE = "\U0001f5f1" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f469\U0001f3fc\u200d\U0001f476\U0001f3fc\u200d\U0001f467\U0001f3fc" - FLAG_FOR_JWANENG_BW_JW = "\U0001f3f4\U000e0062\U000e0077\U000e006a\U000e0077\U000e007f" - FAMILY_MAN_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f467\U0001f3fd" - KISS_MAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fb" - COUPLE_WITH_HEART_MAN_MEDIUM_SKIN_TONE_MAN = "\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f468" - FLAG_FOR_SOUTH_EAST_BW_SE = "\U0001f3f4\U000e0062\U000e0077\U000e0073\U000e0065\U000e007f" - LEFT_HANDED_INTERLACED_PENTAGRAM = "\u26e6" - FLAG_FOR_ATLANTIQUE_BJ_AQ = "\U0001f3f4\U000e0062\U000e006a\U000e0061\U000e0071\U000e007f" - FLAG_FOR_CENTRAL_BW_CE = "\U0001f3f4\U000e0062\U000e0077\U000e0063\U000e0065\U000e007f" - FLAG_FOR_WANGDUE_PHODRANG_BT_24 = "\U0001f3f4\U000e0062\U000e0074\U000e0032\U000e0034\U000e007f" - FLAG_FOR_KGATLENG_BW_KL = "\U0001f3f4\U000e0062\U000e0077\U000e006b\U000e006c\U000e007f" - FLAG_FOR_RONDONIA_BR_RO = "\U0001f3f4\U000e0062\U000e0072\U000e0072\U000e006f\U000e007f" - FLAG_FOR_CENTRAL_FINLAND_FI_08 = "\U0001f3f4\U000e0066\U000e0069\U000e0030\U000e0038\U000e007f" - KISS_MAN_MEDIUM_DARK_SKIN_TONE_WOMAN = "\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469" - FLAG_FOR_RUTANA_BI_RT = "\U0001f3f4\U000e0062\U000e0069\U000e0072\U000e0074\U000e007f" - FLAG_FOR_GHANZI_BW_GH = "\U0001f3f4\U000e0062\U000e0077\U000e0067\U000e0068\U000e007f" - FLAG_FOR_KGALAGADI_BW_KG = "\U0001f3f4\U000e0062\U000e0077\U000e006b\U000e0067\U000e007f" - FLAG_FOR_NORTH_EAST_BW_NE = "\U0001f3f4\U000e0062\U000e0077\U000e006e\U000e0065\U000e007f" - FLAG_FOR_TISINA_SI_010 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0031\U000e0030\U000e007f" - FLAG_FOR_KWENENG_BW_KW = "\U0001f3f4\U000e0062\U000e0077\U000e006b\U000e0077\U000e007f" - COUPLE_WITH_HEART_WOMAN_MAN_LIGHT_SKIN_TONE = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fb" - FLAG_FOR_SAMDRUP_JONGKHAR_BT_45 = "\U0001f3f4\U000e0062\U000e0074\U000e0034\U000e0035\U000e007f" - FLAG_FOR_SAMTSE_BT_14 = "\U0001f3f4\U000e0062\U000e0074\U000e0031\U000e0034\U000e007f" - FLAG_FOR_JIHOCESKY_KRAJ_CZ_31 = "\U0001f3f4\U000e0063\U000e007a\U000e0033\U000e0031\U000e007f" - FLAG_FOR_CHRIST_CHURCH_BB_01 = "\U0001f3f4\U000e0062\U000e0062\U000e0030\U000e0031\U000e007f" - FLAG_FOR_SELIBE_PHIKWE_BW_SP = "\U0001f3f4\U000e0062\U000e0077\U000e0073\U000e0070\U000e007f" - KISS_MAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fe" - COUPLE_WITH_HEART_MAN_LIGHT_SKIN_TONE_MAN_DARK_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3ff" - FAMILY_MAN_DARK_SKIN_TONE_MAN_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f468\U0001f3ff\u200d\U0001f476\U0001f3ff\u200d\U0001f466\U0001f3ff" - FAMILY_MAN_LIGHT_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f469\U0001f3fb\u200d\U0001f467\U0001f3fb\u200d\U0001f476\U0001f3fb" - FLAG_FOR_NORTH_WEST_BW_NW = "\U0001f3f4\U000e0062\U000e0077\U000e006e\U000e0077\U000e007f" - COUPLE_WITH_HEART_MAN_MEDIUM_LIGHT_SKIN_TONE_MAN = "\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f468" - FLAG_FOR_SOWA_TOWN_BW_ST = "\U0001f3f4\U000e0062\U000e0077\U000e0073\U000e0074\U000e007f" - FAMILY_WOMAN_MAN_BABY_BABY = "\U0001f469\u200d\U0001f468\u200d\U0001f476\u200d\U0001f476" - WHITE_CHESS_KING = "\u2654" - FLAG_FOR_ORURO_BO_O = "\U0001f3f4\U000e0062\U000e006f\U000e006f\U000e007f" - FLAG_FOR_PHOENIX_ISLANDS_KI_P = "\U0001f3f4\U000e006b\U000e0069\U000e0070\U000e007f" - FLAG_FOR_PIAUI_BR_PI = "\U0001f3f4\U000e0062\U000e0072\U000e0070\U000e0069\U000e007f" - FLAG_FOR_HRODNA_BY_HR = "\U0001f3f4\U000e0062\U000e0079\U000e0068\U000e0072\U000e007f" - FLAG_FOR_HOMEL_BY_HO = "\U0001f3f4\U000e0062\U000e0079\U000e0068\U000e006f\U000e007f" - FAMILY_WOMAN_DARK_SKIN_TONE_WOMAN_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f469\U0001f3ff\u200d\U0001f466\U0001f3ff\u200d\U0001f466\U0001f3ff" - FLAG_FOR_MINSK_BY_HM = "\U0001f3f4\U000e0062\U000e0079\U000e0068\U000e006d\U000e007f" - KISS_MAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fe" - FLAG_FOR_CHITTAGONG_DIVISION_BD_B = "\U0001f3f4\U000e0062\U000e0064\U000e0062\U000e007f" - MAP_SYMBOL_FOR_LIGHTHOUSE = "\u26ef" - FAMILY_WOMAN_DARK_SKIN_TONE_MAN_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f468\U0001f3ff\u200d\U0001f466\U0001f3ff\u200d\U0001f476\U0001f3ff" - FLAG_FOR_MOORE_S_ISLAND_BS_MI = "\U0001f3f4\U000e0062\U000e0073\U000e006d\U000e0069\U000e007f" - FLAG_FOR_MINSK_REGION_BY_MI = "\U0001f3f4\U000e0062\U000e0079\U000e006d\U000e0069\U000e007f" - COUPLE_WITH_HEART_MAN_LIGHT_SKIN_TONE_WOMAN = "\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f469" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f469\U0001f3fe\u200d\U0001f467\U0001f3fe\u200d\U0001f466\U0001f3fe" - FLAG_FOR_GUARDA_PT_09 = "\U0001f3f4\U000e0070\U000e0074\U000e0030\U000e0039\U000e007f" - KISS_WOMAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN = "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469" - FAMILY_MAN_LIGHT_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f469\U0001f3fb\u200d\U0001f466\U0001f3fb" - FAMILY_WOMAN_BABY_GIRL = "\U0001f469\u200d\U0001f476\u200d\U0001f467" - FAMILY_MAN_LIGHT_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE_BOY_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f469\U0001f3fb\u200d\U0001f466\U0001f3fb\u200d\U0001f466\U0001f3fb" - FLAG_FOR_VITEBSK_BY_VI = "\U0001f3f4\U000e0062\U000e0079\U000e0076\U000e0069\U000e007f" - FLAG_FOR_CASTILE_LA_MANCHA_ES_CM = "\U0001f3f4\U000e0065\U000e0073\U000e0063\U000e006d\U000e007f" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f476\U0001f3fe\u200d\U0001f466\U0001f3fe" - FLAG_FOR_ALMATY_KZ_ALA = "\U0001f3f4\U000e006b\U000e007a\U000e0061\U000e006c\U000e0061\U000e007f" - FLAG_FOR_METRO_MANILA_PH_00 = "\U0001f3f4\U000e0070\U000e0068\U000e0030\U000e0030\U000e007f" - FLAG_FOR_MAGILEU_BY_MA = "\U0001f3f4\U000e0062\U000e0079\U000e006d\U000e0061\U000e007f" - FAMILY_WOMAN_LIGHT_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f469\U0001f3fb\u200d\U0001f476\U0001f3fb\u200d\U0001f476\U0001f3fb" - COUPLE_WITH_HEART_MAN_DARK_SKIN_TONE_WOMAN = "\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f469" - FLAG_FOR_SILISTRA_BG_19 = "\U0001f3f4\U000e0062\U000e0067\U000e0031\U000e0039\U000e007f" - FAMILY_MAN_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f466\U0001f3ff" - FLAG_FOR_STANN_CREEK_BZ_SC = "\U0001f3f4\U000e0062\U000e007a\U000e0073\U000e0063\U000e007f" - FLAG_FOR_NORTH_ELEUTHERA_BS_NE = "\U0001f3f4\U000e0062\U000e0073\U000e006e\U000e0065\U000e007f" - FLAG_FOR_NEW_BRUNSWICK_CA_NB = "\U0001f3f4\U000e0063\U000e0061\U000e006e\U000e0062\U000e007f" - FAMILY_MAN_GIRL_BABY = "\U0001f468\u200d\U0001f467\u200d\U0001f476" - COUPLE_WITH_HEART_WOMAN_LIGHT_SKIN_TONE_MAN = "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f468" - COUPLE_WITH_HEART_WOMAN_WOMAN_MEDIUM_SKIN_TONE = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fd" - COUPLE_WITH_HEART_WOMAN_LIGHT_SKIN_TONE_WOMAN_DARK_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3ff" - FLAG_FOR_TOLEDO_BZ_TOL = "\U0001f3f4\U000e0062\U000e007a\U000e0074\U000e006f\U000e006c\U000e007f" - KISS_WOMAN_MEDIUM_SKIN_TONE_WOMAN = "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469" - KISS_MAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_DARK_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3ff" - FAMILY_MAN_MAN_BABY_BABY = "\U0001f468\u200d\U0001f468\u200d\U0001f476\u200d\U0001f476" - FLAG_FOR_TAMAULIPAS_MX_TAM = "\U0001f3f4\U000e006d\U000e0078\U000e0074\U000e0061\U000e006d\U000e007f" - FLAG_FOR_TRISTAN_DA_CUNHA_SH_TA = "\U0001f3f4\U000e0073\U000e0068\U000e0074\U000e0061\U000e007f" - FAMILY_MAN_WOMAN_BABY_BABY = "\U0001f468\u200d\U0001f469\u200d\U0001f476\u200d\U0001f476" - FLAG_FOR_SAN_LUIS_POTOSI_MX_SLP = "\U0001f3f4\U000e006d\U000e0078\U000e0073\U000e006c\U000e0070\U000e007f" - FLAG_FOR_VIENTIANE_PROVINCE_LA_VI = "\U0001f3f4\U000e006c\U000e0061\U000e0076\U000e0069\U000e007f" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f466\U0001f3fe\u200d\U0001f476\U0001f3fe" - FLAG_FOR_SOUTHERN_BW_SO = "\U0001f3f4\U000e0062\U000e0077\U000e0073\U000e006f\U000e007f" - FLAG_FOR_MAMBERE_KADEI_CF_HS = "\U0001f3f4\U000e0063\U000e0066\U000e0068\U000e0073\U000e007f" - FLAG_FOR_ITURI_CD_IT = "\U0001f3f4\U000e0063\U000e0064\U000e0069\U000e0074\U000e007f" - NOTCHED_LEFT_SEMICIRCLE_WITH_THREE_DOTS = "\U0001f543" - FLAG_FOR_MAI_NDOMBE_CD_MN = "\U0001f3f4\U000e0063\U000e0064\U000e006d\U000e006e\U000e007f" - FLAG_FOR_SOUTH_KIVU_CD_SK = "\U0001f3f4\U000e0063\U000e0064\U000e0073\U000e006b\U000e007f" - FLAG_FOR_KASAI_CENTRAL_CD_KC = "\U0001f3f4\U000e0063\U000e0064\U000e006b\U000e0063\U000e007f" - FLAG_FOR_HAUT_UELE_CD_HU = "\U0001f3f4\U000e0063\U000e0064\U000e0068\U000e0075\U000e007f" - FLAG_FOR_KWANGO_CD_KG = "\U0001f3f4\U000e0063\U000e0064\U000e006b\U000e0067\U000e007f" - FLAG_FOR_SWIETOKRZYSKIE_PL_SK = "\U0001f3f4\U000e0070\U000e006c\U000e0073\U000e006b\U000e007f" - FLAG_FOR_KASAI_CD_KS = "\U0001f3f4\U000e0063\U000e0064\U000e006b\U000e0073\U000e007f" - FLAG_FOR_RAGGED_ISLAND_BS_RI = "\U0001f3f4\U000e0062\U000e0073\U000e0072\U000e0069\U000e007f" - FAMILY_MAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f468\U0001f3fd\u200d\U0001f466\U0001f3fd" - FLAG_FOR_MANIEMA_CD_MA = "\U0001f3f4\U000e0063\U000e0064\U000e006d\U000e0061\U000e007f" - FLAG_FOR_SANKURU_CD_SA = "\U0001f3f4\U000e0063\U000e0064\U000e0073\U000e0061\U000e007f" - FLAG_FOR_MONTANA_BG_12 = "\U0001f3f4\U000e0062\U000e0067\U000e0031\U000e0032\U000e007f" - FLAG_FOR_OUHAM_CF_AC = "\U0001f3f4\U000e0063\U000e0066\U000e0061\U000e0063\U000e007f" - FLAG_FOR_HAUT_LOMAMI_CD_HL = "\U0001f3f4\U000e0063\U000e0064\U000e0068\U000e006c\U000e007f" - FLAG_FOR_BAMINGUI_BANGORAN_CF_BB = "\U0001f3f4\U000e0063\U000e0066\U000e0062\U000e0062\U000e007f" - FLAG_FOR_BUBANZA_BI_BB = "\U0001f3f4\U000e0062\U000e0069\U000e0062\U000e0062\U000e007f" - FLAG_FOR_KINSHASA_CD_KN = "\U0001f3f4\U000e0063\U000e0064\U000e006b\U000e006e\U000e007f" - FLAG_FOR_KASAI_ORIENTAL_CD_KE = "\U0001f3f4\U000e0063\U000e0064\U000e006b\U000e0065\U000e007f" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f469\U0001f3fc\u200d\U0001f476\U0001f3fc" - FLAG_FOR_MONGALA_CD_MO = "\U0001f3f4\U000e0063\U000e0064\U000e006d\U000e006f\U000e007f" - FLAG_FOR_TSHUAPA_CD_TU = "\U0001f3f4\U000e0063\U000e0064\U000e0074\U000e0075\U000e007f" - FLAG_FOR_TSHOPO_CD_TO = "\U0001f3f4\U000e0063\U000e0064\U000e0074\U000e006f\U000e007f" - FLAG_FOR_LUCERNE_CH_LU = "\U0001f3f4\U000e0063\U000e0068\U000e006c\U000e0075\U000e007f" - FLAG_FOR_RIO_GRANDE_DO_NORTE_BR_RN = "\U0001f3f4\U000e0062\U000e0072\U000e0072\U000e006e\U000e007f" - WOMAN_WITH_HEADSCARF_LIGHT_SKIN_TONE = "\U0001f9d5\U0001f3fb\u200d\u2640\ufe0f" - FAMILY_MAN_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f467\U0001f3ff\u200d\U0001f476\U0001f3ff" - FLAG_FOR_LOBAYE_CF_LB = "\U0001f3f4\U000e0063\U000e0066\U000e006c\U000e0062\U000e007f" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f468\U0001f3fe\u200d\U0001f466\U0001f3fe\u200d\U0001f466\U0001f3fe" - FLAG_FOR_LOMAMI_CD_LO = "\U0001f3f4\U000e0063\U000e0064\U000e006c\U000e006f\U000e007f" - FLAG_FOR_KWILU_CD_KL = "\U0001f3f4\U000e0063\U000e0064\U000e006b\U000e006c\U000e007f" - FLAG_FOR_LOBATSE_BW_LO = "\U0001f3f4\U000e0062\U000e0077\U000e006c\U000e006f\U000e007f" - FLAG_FOR_SANGHA_CG_13 = "\U0001f3f4\U000e0063\U000e0067\U000e0031\U000e0033\U000e007f" - FLAG_FOR_KOUILOU_CG_5 = "\U0001f3f4\U000e0063\U000e0067\U000e0035\U000e007f" - FLAG_FOR_LIKOUALA_CG_7 = "\U0001f3f4\U000e0063\U000e0067\U000e0037\U000e007f" - FLAG_FOR_BANGUI_CF_BGF = "\U0001f3f4\U000e0063\U000e0066\U000e0062\U000e0067\U000e0066\U000e007f" - FLAG_FOR_SANGHA_MBAERE_CF_SE = "\U0001f3f4\U000e0063\U000e0066\U000e0073\U000e0065\U000e007f" - FLAG_FOR_NIDWALDEN_CH_NW = "\U0001f3f4\U000e0063\U000e0068\U000e006e\U000e0077\U000e007f" - FLAG_FOR_ST_GALLEN_CH_SG = "\U0001f3f4\U000e0063\U000e0068\U000e0073\U000e0067\U000e007f" - FLAG_FOR_LEKOUMOU_CG_2 = "\U0001f3f4\U000e0063\U000e0067\U000e0032\U000e007f" - FLAG_FOR_MONO_BJ_MO = "\U0001f3f4\U000e0062\U000e006a\U000e006d\U000e006f\U000e007f" - FLAG_FOR_APPENZELL_INNERRHODEN_CH_AI = "\U0001f3f4\U000e0063\U000e0068\U000e0061\U000e0069\U000e007f" - FLAG_FOR_AARGAU_CH_AG = "\U0001f3f4\U000e0063\U000e0068\U000e0061\U000e0067\U000e007f" - FLAG_FOR_CUVETTE_OUEST_CG_15 = "\U0001f3f4\U000e0063\U000e0067\U000e0031\U000e0035\U000e007f" - FLAG_FOR_BRAZZAVILLE_CG_BZV = "\U0001f3f4\U000e0063\U000e0067\U000e0062\U000e007a\U000e0076\U000e007f" - FLAG_FOR_CUVETTE_CG_8 = "\U0001f3f4\U000e0063\U000e0067\U000e0038\U000e007f" - FLAG_FOR_LOS_RIOS_CL_LR = "\U0001f3f4\U000e0063\U000e006c\U000e006c\U000e0072\U000e007f" - FLAG_FOR_MONTAGNES_CI_MG = "\U0001f3f4\U000e0063\U000e0069\U000e006d\U000e0067\U000e007f" - FLAG_FOR_BAS_SASSANDRA_CI_BS = "\U0001f3f4\U000e0063\U000e0069\U000e0062\U000e0073\U000e007f" - FLAG_FOR_BASEL_STADT_CH_BS = "\U0001f3f4\U000e0063\U000e0068\U000e0062\U000e0073\U000e007f" - FAMILY_MAN_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f467\U0001f3ff" - FLAG_FOR_LITTORAL_CM_LT = "\U0001f3f4\U000e0063\U000e006d\U000e006c\U000e0074\U000e007f" - FLAG_FOR_NORTHWEST_CM_NW = "\U0001f3f4\U000e0063\U000e006d\U000e006e\U000e0077\U000e007f" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f466\U0001f3fe\u200d\U0001f467\U0001f3fe" - FLAG_FOR_FRIBOURG_CH_FR = "\U0001f3f4\U000e0063\U000e0068\U000e0066\U000e0072\U000e007f" - FLAG_FOR_ATACAMA_CL_AT = "\U0001f3f4\U000e0063\U000e006c\U000e0061\U000e0074\U000e007f" - FLAG_FOR_SAVANES_CI_SV = "\U0001f3f4\U000e0063\U000e0069\U000e0073\U000e0076\U000e007f" - FLAG_FOR_COQUIMBO_CL_CO = "\U0001f3f4\U000e0063\U000e006c\U000e0063\U000e006f\U000e007f" - FLAG_FOR_ADAMAWA_CM_AD = "\U0001f3f4\U000e0063\U000e006d\U000e0061\U000e0064\U000e007f" - FLAG_FOR_AL_MADINAH_SA_03 = "\U0001f3f4\U000e0073\U000e0061\U000e0030\U000e0033\U000e007f" - FLAG_FOR_SOUTHWEST_CM_SW = "\U0001f3f4\U000e0063\U000e006d\U000e0073\U000e0077\U000e007f" - FLAG_FOR_NORTH_CM_NO = "\U0001f3f4\U000e0063\U000e006d\U000e006e\U000e006f\U000e007f" - COUPLE_WITH_HEART_MAN_WOMAN = "\U0001f468\u200d\u2764\ufe0f\u200d\U0001f469" - FLAG_FOR_DENGUELE_CI_DN = "\U0001f3f4\U000e0063\U000e0069\U000e0064\U000e006e\U000e007f" - FLAG_FOR_MAGALLANES_REGION_CL_MA = "\U0001f3f4\U000e0063\U000e006c\U000e006d\U000e0061\U000e007f" - FLAG_FOR_HAUT_KATANGA_CD_HK = "\U0001f3f4\U000e0063\U000e0064\U000e0068\U000e006b\U000e007f" - FLAG_FOR_GOH_DJIBOUA_CI_GD = "\U0001f3f4\U000e0063\U000e0069\U000e0067\U000e0064\U000e007f" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f467\U0001f3fe" - FLAG_FOR_WOROBA_CI_WR = "\U0001f3f4\U000e0063\U000e0069\U000e0077\U000e0072\U000e007f" - FLAG_FOR_COMOE_CI_CM = "\U0001f3f4\U000e0063\U000e0069\U000e0063\U000e006d\U000e007f" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f476\U0001f3fc" - FLAG_FOR_YAMOUSSOUKRO_CI_YM = "\U0001f3f4\U000e0063\U000e0069\U000e0079\U000e006d\U000e007f" - FLAG_FOR_LACS_CI_LC = "\U0001f3f4\U000e0063\U000e0069\U000e006c\U000e0063\U000e007f" - FLAG_FOR_TARAPACA_CL_TA = "\U0001f3f4\U000e0063\U000e006c\U000e0074\U000e0061\U000e007f" - FLAG_FOR_PANEVEZYS_COUNTY_LT_PN = "\U0001f3f4\U000e006c\U000e0074\U000e0070\U000e006e\U000e007f" - FLAG_FOR_CENTRE_CM_CE = "\U0001f3f4\U000e0063\U000e006d\U000e0063\U000e0065\U000e007f" - FLAG_FOR_GUANGXI_CN_45 = "\U0001f3f4\U000e0063\U000e006e\U000e0034\U000e0035\U000e007f" - FLAG_FOR_HENAN_CN_41 = "\U0001f3f4\U000e0063\U000e006e\U000e0034\U000e0031\U000e007f" - FLAG_FOR_JIANGSU_CN_32 = "\U0001f3f4\U000e0063\U000e006e\U000e0033\U000e0032\U000e007f" - BLACK_CHESS_KNIGHT = "\u265e" - WHITE_HARD_SHELL_FLOPPY_DISK = "\U0001f5ab" - FLAG_FOR_BOYACA_CO_BOY = "\U0001f3f4\U000e0063\U000e006f\U000e0062\U000e006f\U000e0079\U000e007f" - FLAG_FOR_FUJIAN_CN_35 = "\U0001f3f4\U000e0063\U000e006e\U000e0033\U000e0035\U000e007f" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f466\U0001f3fc\u200d\U0001f466\U0001f3fc" - FLAG_FOR_CAQUETA_CO_CAQ = "\U0001f3f4\U000e0063\U000e006f\U000e0063\U000e0061\U000e0071\U000e007f" - FLAG_FOR_JIANGXI_CN_36 = "\U0001f3f4\U000e0063\U000e006e\U000e0033\U000e0036\U000e007f" - FLAG_FOR_OMBELLA_M_POKO_CF_MP = "\U0001f3f4\U000e0063\U000e0066\U000e006d\U000e0070\U000e007f" - FLAG_FOR_RUSE_BG_18 = "\U0001f3f4\U000e0062\U000e0067\U000e0031\U000e0038\U000e007f" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f468\U0001f3fe\u200d\U0001f476\U0001f3fe\u200d\U0001f467\U0001f3fe" - FLAG_FOR_LIBERTADOR_GENERAL_BERNARDO_O_HIGGINS_CL_LI = "\U0001f3f4\U000e0063\U000e006c\U000e006c\U000e0069\U000e007f" - FLAG_FOR_HAINAN_CN_46 = "\U0001f3f4\U000e0063\U000e006e\U000e0034\U000e0036\U000e007f" - FLAG_FOR_BATMAN_TR_72 = "\U0001f3f4\U000e0074\U000e0072\U000e0037\U000e0032\U000e007f" - FLAG_FOR_CALDAS_CO_CAL = "\U0001f3f4\U000e0063\U000e006f\U000e0063\U000e0061\U000e006c\U000e007f" - FLAG_FOR_QINGHAI_CN_63 = "\U0001f3f4\U000e0063\U000e006e\U000e0036\U000e0033\U000e007f" - FLAG_FOR_ARAUCA_CO_ARA = "\U0001f3f4\U000e0063\U000e006f\U000e0061\U000e0072\U000e0061\U000e007f" - COUPLE_WITH_HEART_WOMAN_DARK_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fe" - FLAG_FOR_GUAVIARE_CO_GUV = "\U0001f3f4\U000e0063\U000e006f\U000e0067\U000e0075\U000e0076\U000e007f" - FLAG_FOR_VAUPES_CO_VAU = "\U0001f3f4\U000e0063\U000e006f\U000e0076\U000e0061\U000e0075\U000e007f" - COUPLE_WITH_HEART_MAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fc" - FLAG_FOR_CARTAGO_CR_C = "\U0001f3f4\U000e0063\U000e0072\U000e0063\U000e007f" - FLAG_FOR_LA_GUAJIRA_CO_LAG = "\U0001f3f4\U000e0063\U000e006f\U000e006c\U000e0061\U000e0067\U000e007f" - FLAG_FOR_MATANZAS_CU_04 = "\U0001f3f4\U000e0063\U000e0075\U000e0030\U000e0034\U000e007f" - FLAG_FOR_ARIANA_TN_12 = "\U0001f3f4\U000e0074\U000e006e\U000e0031\U000e0032\U000e007f" - FLAG_FOR_LIMON_CR_L = "\U0001f3f4\U000e0063\U000e0072\U000e006c\U000e007f" - FLAG_FOR_FEDERALLY_ADMINISTERED_TRIBAL_AREAS_PK_TA = "\U0001f3f4\U000e0070\U000e006b\U000e0074\U000e0061\U000e007f" - FLAG_FOR_CIENFUEGOS_CU_06 = "\U0001f3f4\U000e0063\U000e0075\U000e0030\U000e0036\U000e007f" - FLAG_FOR_CORDOBA_CO_COR = "\U0001f3f4\U000e0063\U000e006f\U000e0063\U000e006f\U000e0072\U000e007f" - FLAG_FOR_NORTE_DE_SANTANDER_CO_NSA = "\U0001f3f4\U000e0063\U000e006f\U000e006e\U000e0073\U000e0061\U000e007f" - FLAG_FOR_HUBEI_CN_42 = "\U0001f3f4\U000e0063\U000e006e\U000e0034\U000e0032\U000e007f" - FLAG_FOR_PUNTARENAS_CR_P = "\U0001f3f4\U000e0063\U000e0072\U000e0070\U000e007f" - FLAG_FOR_CAMAGUEY_CU_09 = "\U0001f3f4\U000e0063\U000e0075\U000e0030\U000e0039\U000e007f" - KISS_WOMAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_DARK_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3ff" - FLAG_FOR_SAN_SALVADOR_BS_SS = "\U0001f3f4\U000e0062\U000e0073\U000e0073\U000e0073\U000e007f" - FLAG_FOR_LIMASSOL_CY_02 = "\U0001f3f4\U000e0063\U000e0079\U000e0030\U000e0032\U000e007f" - FLAG_FOR_TSIRANG_BT_21 = "\U0001f3f4\U000e0062\U000e0074\U000e0032\U000e0031\U000e007f" - FLAG_FOR_MORAVSKOSLEZSKY_KRAJ_CZ_80 = "\U0001f3f4\U000e0063\U000e007a\U000e0038\U000e0030\U000e007f" - FLAG_FOR_SLIVEN_BG_20 = "\U0001f3f4\U000e0062\U000e0067\U000e0032\U000e0030\U000e007f" - FLAG_FOR_BURGAS_BG_02 = "\U0001f3f4\U000e0062\U000e0067\U000e0030\U000e0032\U000e007f" - FLAG_FOR_SOTAVENTO_ISLANDS_CV_S = "\U0001f3f4\U000e0063\U000e0076\U000e0073\U000e007f" - FLAG_FOR_SALZBURG_AT_5 = "\U0001f3f4\U000e0061\U000e0074\U000e0035\U000e007f" - FLAG_FOR_KRAJ_VYSOCINA_CZ_63 = "\U0001f3f4\U000e0063\U000e007a\U000e0036\U000e0033\U000e007f" - FLAG_FOR_HOLGUIN_CU_11 = "\U0001f3f4\U000e0063\U000e0075\U000e0031\U000e0031\U000e007f" - KISS_WOMAN_MEDIUM_DARK_SKIN_TONE_WOMAN = "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469" - FLAG_FOR_PARANA_BR_PR = "\U0001f3f4\U000e0062\U000e0072\U000e0070\U000e0072\U000e007f" - FLAG_FOR_GUANTANAMO_CU_14 = "\U0001f3f4\U000e0063\U000e0075\U000e0031\U000e0034\U000e007f" - FLAG_FOR_XINJIANG_CN_65 = "\U0001f3f4\U000e0063\U000e006e\U000e0036\U000e0035\U000e007f" - FLAG_FOR_KRALOVEHRADECKY_KRAJ_CZ_52 = "\U0001f3f4\U000e0063\U000e007a\U000e0035\U000e0032\U000e007f" - FAMILY_WOMAN_LIGHT_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f469\U0001f3fb\u200d\U0001f467\U0001f3fb\u200d\U0001f476\U0001f3fb" - FLAG_FOR_KARLOVARSKY_KRAJ_CZ_41 = "\U0001f3f4\U000e0063\U000e007a\U000e0034\U000e0031\U000e007f" - FLAG_FOR_AMAPA_BR_AP = "\U0001f3f4\U000e0062\U000e0072\U000e0061\U000e0070\U000e007f" - FLAG_FOR_GASA_BT_GA = "\U0001f3f4\U000e0062\U000e0074\U000e0067\U000e0061\U000e007f" - FLAG_FOR_BARLAVENTO_ISLANDS_CV_B = "\U0001f3f4\U000e0063\U000e0076\U000e0062\U000e007f" - FLAG_FOR_MAYABEQUE_CU_16 = "\U0001f3f4\U000e0063\U000e0075\U000e0031\U000e0036\U000e007f" - KISS_MAN_DARK_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fc" - FLAG_FOR_LIBERECKY_KRAJ_CZ_51 = "\U0001f3f4\U000e0063\U000e007a\U000e0035\U000e0031\U000e007f" - FLAG_FOR_CIBAO_NOROESTE_DO_34 = "\U0001f3f4\U000e0064\U000e006f\U000e0033\U000e0034\U000e007f" - FLAG_FOR_RISARALDA_CO_RIS = "\U0001f3f4\U000e0063\U000e006f\U000e0072\U000e0069\U000e0073\U000e007f" - FLAG_FOR_HOPE_TOWN_BS_HT = "\U0001f3f4\U000e0062\U000e0073\U000e0068\U000e0074\U000e007f" - FLAG_FOR_BRITISH_COLUMBIA_CA_BC = "\U0001f3f4\U000e0063\U000e0061\U000e0062\U000e0063\U000e007f" - FLAG_FOR_NICOSIA_CY_01 = "\U0001f3f4\U000e0063\U000e0079\U000e0030\U000e0031\U000e007f" - FLAG_FOR_SHANXI_CN_14 = "\U0001f3f4\U000e0063\U000e006e\U000e0031\U000e0034\U000e007f" - FLAG_FOR_SAXONY_ANHALT_DE_ST = "\U0001f3f4\U000e0064\U000e0065\U000e0073\U000e0074\U000e007f" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f468\U0001f3fc\u200d\U0001f476\U0001f3fc\u200d\U0001f466\U0001f3fc" - FLAG_FOR_TADJOURAH_DJ_TA = "\U0001f3f4\U000e0064\U000e006a\U000e0074\U000e0061\U000e007f" - FLAG_FOR_CAPITAL_REGION_DK_84 = "\U0001f3f4\U000e0064\U000e006b\U000e0038\U000e0034\U000e007f" - FLAG_FOR_HIGUAMO_DO_39 = "\U0001f3f4\U000e0064\U000e006f\U000e0033\U000e0039\U000e007f" - FLAG_FOR_CIBAO_NORDESTE_DO_33 = "\U0001f3f4\U000e0064\U000e006f\U000e0033\U000e0033\U000e007f" - FLAG_FOR_CIBAO_SUR_DO_36 = "\U0001f3f4\U000e0064\U000e006f\U000e0033\U000e0036\U000e007f" - COUPLE_WITH_HEART_MAN_LIGHT_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fb" - FLAG_FOR_POHNPEI_FM_PNI = "\U0001f3f4\U000e0066\U000e006d\U000e0070\U000e006e\U000e0069\U000e007f" - FLAG_FOR_VALDESIA_DO_41 = "\U0001f3f4\U000e0064\U000e006f\U000e0034\U000e0031\U000e007f" - FLAG_FOR_YOZGAT_TR_66 = "\U0001f3f4\U000e0074\U000e0072\U000e0036\U000e0036\U000e007f" - FLAG_FOR_CAUCA_CO_CAU = "\U0001f3f4\U000e0063\U000e006f\U000e0063\U000e0061\U000e0075\U000e007f" - FLAG_FOR_OLOMOUCKY_KRAJ_CZ_71 = "\U0001f3f4\U000e0063\U000e007a\U000e0037\U000e0031\U000e007f" - FLAG_FOR_SAINT_MARK_DM_08 = "\U0001f3f4\U000e0064\U000e006d\U000e0030\U000e0038\U000e007f" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f469\U0001f3fe\u200d\U0001f466\U0001f3fe\u200d\U0001f466\U0001f3fe" - FAMILY_WOMAN_LIGHT_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f469\U0001f3fb\u200d\U0001f467\U0001f3fb\u200d\U0001f467\U0001f3fb" - FLAG_FOR_KHENCHELA_DZ_40 = "\U0001f3f4\U000e0064\U000e007a\U000e0034\U000e0030\U000e007f" - FAMILY_WOMAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f469\U0001f3fe\u200d\U0001f467\U0001f3fe" - FLAG_FOR_TIZI_OUZOU_DZ_15 = "\U0001f3f4\U000e0064\U000e007a\U000e0031\U000e0035\U000e007f" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f467\U0001f3fe\u200d\U0001f476\U0001f3fe" - FLAG_FOR_MOSTAGANEM_DZ_27 = "\U0001f3f4\U000e0064\U000e007a\U000e0032\U000e0037\U000e007f" - FLAG_FOR_OUARGLA_DZ_30 = "\U0001f3f4\U000e0064\U000e007a\U000e0033\U000e0030\U000e007f" - FLAG_FOR_BOUMERDES_DZ_35 = "\U0001f3f4\U000e0064\U000e007a\U000e0033\U000e0035\U000e007f" - FLAG_FOR_TIPASA_DZ_42 = "\U0001f3f4\U000e0064\U000e007a\U000e0034\U000e0032\U000e007f" - FLAG_FOR_BISKRA_DZ_07 = "\U0001f3f4\U000e0064\U000e007a\U000e0030\U000e0037\U000e007f" - FLAG_FOR_MEDEA_DZ_26 = "\U0001f3f4\U000e0064\U000e007a\U000e0032\U000e0036\U000e007f" - FLAG_FOR_SOUK_AHRAS_DZ_41 = "\U0001f3f4\U000e0064\U000e007a\U000e0034\U000e0031\U000e007f" - FLAG_FOR_ANNABA_DZ_23 = "\U0001f3f4\U000e0064\U000e007a\U000e0032\U000e0033\U000e007f" - FLAG_FOR_WESTERN_HIGHLANDS_PG_WHM = "\U0001f3f4\U000e0070\U000e0067\U000e0077\U000e0068\U000e006d\U000e007f" - FLAG_FOR_TINDOUF_DZ_37 = "\U0001f3f4\U000e0064\U000e007a\U000e0033\U000e0037\U000e007f" - FLAG_FOR_DJELFA_DZ_17 = "\U0001f3f4\U000e0064\U000e007a\U000e0031\U000e0037\U000e007f" - FLAG_FOR_MILA_DZ_43 = "\U0001f3f4\U000e0064\U000e007a\U000e0034\U000e0033\U000e007f" - FLAG_FOR_BATNA_DZ_05 = "\U0001f3f4\U000e0064\U000e007a\U000e0030\U000e0035\U000e007f" - FLAG_FOR_TISSEMSILT_DZ_38 = "\U0001f3f4\U000e0064\U000e007a\U000e0033\U000e0038\U000e007f" - FLAG_FOR_EL_BAYADH_DZ_32 = "\U0001f3f4\U000e0064\U000e007a\U000e0033\U000e0032\U000e007f" - FLAG_FOR_HUILA_CO_HUI = "\U0001f3f4\U000e0063\U000e006f\U000e0068\U000e0075\U000e0069\U000e007f" - FLAG_FOR_ENRIQUILLO_DO_38 = "\U0001f3f4\U000e0064\U000e006f\U000e0033\U000e0038\U000e007f" - FLAG_FOR_SUDUR_PASHCHIMANCHAL_NP_5 = "\U0001f3f4\U000e006e\U000e0070\U000e0035\U000e007f" - FLAG_FOR_BEJAIA_DZ_06 = "\U0001f3f4\U000e0064\U000e007a\U000e0030\U000e0036\U000e007f" - FLAG_FOR_BOUIRA_DZ_10 = "\U0001f3f4\U000e0064\U000e007a\U000e0031\U000e0030\U000e007f" - FLAG_FOR_ILLIZI_DZ_33 = "\U0001f3f4\U000e0064\U000e007a\U000e0033\U000e0033\U000e007f" - FLAG_FOR_PLATEAUX_CG_14 = "\U0001f3f4\U000e0063\U000e0067\U000e0031\U000e0034\U000e007f" - FLAG_FOR_SAINT_DAVID_DM_03 = "\U0001f3f4\U000e0064\U000e006d\U000e0030\U000e0033\U000e007f" - FLAG_FOR_TAMANGHASSET_DZ_11 = "\U0001f3f4\U000e0064\U000e007a\U000e0031\U000e0031\U000e007f" - FLAG_FOR_MORONA_SANTIAGO_EC_S = "\U0001f3f4\U000e0065\U000e0063\U000e0073\U000e007f" - FLAG_FOR_VALGA_EE_82 = "\U0001f3f4\U000e0065\U000e0065\U000e0038\U000e0032\U000e007f" - FLAG_FOR_RELIZANE_DZ_48 = "\U0001f3f4\U000e0064\U000e007a\U000e0034\U000e0038\U000e007f" - FAMILY_MAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f468\U0001f3fd\u200d\U0001f467\U0001f3fd\u200d\U0001f466\U0001f3fd" - FLAG_FOR_LOJA_EC_L = "\U0001f3f4\U000e0065\U000e0063\U000e006c\U000e007f" - FLAG_FOR_GHARDAIA_DZ_47 = "\U0001f3f4\U000e0064\U000e007a\U000e0034\U000e0037\U000e007f" - FLAG_FOR_PASTAZA_EC_Y = "\U0001f3f4\U000e0065\U000e0063\U000e0079\U000e007f" - FLAG_FOR_AIN_TEMOUCHENT_DZ_46 = "\U0001f3f4\U000e0064\U000e007a\U000e0034\U000e0036\U000e007f" - FLAG_FOR_LOS_RIOS_EC_R = "\U0001f3f4\U000e0065\U000e0063\U000e0072\U000e007f" - FLAG_FOR_LAANE_EE_57 = "\U0001f3f4\U000e0065\U000e0065\U000e0035\U000e0037\U000e007f" - FAMILY_MAN_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f476\U0001f3fd\u200d\U0001f466\U0001f3fd" - FLAG_FOR_SAARE_EE_74 = "\U0001f3f4\U000e0065\U000e0065\U000e0037\U000e0034\U000e007f" - FLAG_FOR_JOGEVA_EE_49 = "\U0001f3f4\U000e0065\U000e0065\U000e0034\U000e0039\U000e007f" - FLAG_FOR_POLVA_EE_65 = "\U0001f3f4\U000e0065\U000e0065\U000e0036\U000e0035\U000e007f" - FLAG_FOR_JARVA_EE_51 = "\U0001f3f4\U000e0065\U000e0065\U000e0035\U000e0031\U000e007f" - FLAG_FOR_PAPHOS_CY_05 = "\U0001f3f4\U000e0063\U000e0079\U000e0030\U000e0035\U000e007f" - FLAG_FOR_PARNU_EE_67 = "\U0001f3f4\U000e0065\U000e0065\U000e0036\U000e0037\U000e007f" - FLAG_FOR_CARCHI_EC_C = "\U0001f3f4\U000e0065\U000e0063\U000e0063\U000e007f" - FLAG_FOR_SUCUMBIOS_EC_U = "\U0001f3f4\U000e0065\U000e0063\U000e0075\U000e007f" - FLAG_FOR_OUM_EL_BOUAGHI_DZ_04 = "\U0001f3f4\U000e0064\U000e007a\U000e0030\U000e0034\U000e007f" - FLAG_FOR_GALAPAGOS_EC_W = "\U0001f3f4\U000e0065\U000e0063\U000e0077\U000e007f" - FLAG_FOR_GHARBIA_EG_GH = "\U0001f3f4\U000e0065\U000e0067\U000e0067\U000e0068\U000e007f" - FLAG_FOR_DAKAHLIA_EG_DK = "\U0001f3f4\U000e0065\U000e0067\U000e0064\U000e006b\U000e007f" - FLAG_FOR_AL_SHARQIA_EG_SHR = "\U0001f3f4\U000e0065\U000e0067\U000e0073\U000e0068\U000e0072\U000e007f" - FLAG_FOR_QALYUBIA_EG_KB = "\U0001f3f4\U000e0065\U000e0067\U000e006b\U000e0062\U000e007f" - FLAG_FOR_FEDERATION_OF_BOSNIA_AND_HERZEGOVINA_BA_BIH = "\U0001f3f4\U000e0062\U000e0061\U000e0062\U000e0069\U000e0068\U000e007f" - FLAG_FOR_CENTRAL_ABACO_BS_CO = "\U0001f3f4\U000e0062\U000e0073\U000e0063\U000e006f\U000e007f" - FLAG_FOR_MONUFIA_EG_MNF = "\U0001f3f4\U000e0065\U000e0067\U000e006d\U000e006e\U000e0066\U000e007f" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE_GIRL_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f476\U0001f3fe\u200d\U0001f467\U0001f3fe" - FLAG_FOR_BUJUMBURA_RURAL_BI_BL = "\U0001f3f4\U000e0062\U000e0069\U000e0062\U000e006c\U000e007f" - FLAG_FOR_MAEKEL_ER_MA = "\U0001f3f4\U000e0065\U000e0072\U000e006d\U000e0061\U000e007f" - FLAG_FOR_ISMAILIA_EG_IS = "\U0001f3f4\U000e0065\U000e0067\U000e0069\U000e0073\U000e007f" - FLAG_FOR_SAIDA_DZ_20 = "\U0001f3f4\U000e0064\U000e007a\U000e0032\U000e0030\U000e007f" - FLAG_FOR_QENA_EG_KN = "\U0001f3f4\U000e0065\U000e0067\U000e006b\U000e006e\U000e007f" - FLAG_FOR_TARTU_EE_78 = "\U0001f3f4\U000e0065\U000e0065\U000e0037\U000e0038\U000e007f" - FLAG_FOR_ANSEBA_ER_AN = "\U0001f3f4\U000e0065\U000e0072\U000e0061\U000e006e\U000e007f" - FLAG_FOR_ASWAN_EG_ASN = "\U0001f3f4\U000e0065\U000e0067\U000e0061\U000e0073\U000e006e\U000e007f" - FLAG_FOR_DEBUB_ER_DU = "\U0001f3f4\U000e0065\U000e0072\U000e0064\U000e0075\U000e007f" - FLAG_FOR_ASYUT_EG_AST = "\U0001f3f4\U000e0065\U000e0067\U000e0061\U000e0073\U000e0074\U000e007f" - FLAG_FOR_BEHEIRA_EG_BH = "\U0001f3f4\U000e0065\U000e0067\U000e0062\U000e0068\U000e007f" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_BABY_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f476\U0001f3fe" - FLAG_FOR_IMBABURA_EC_I = "\U0001f3f4\U000e0065\U000e0063\U000e0069\U000e007f" - FLAG_FOR_VILJANDI_EE_84 = "\U0001f3f4\U000e0065\U000e0065\U000e0038\U000e0034\U000e007f" - FLAG_FOR_VORU_EE_86 = "\U0001f3f4\U000e0065\U000e0065\U000e0038\U000e0036\U000e007f" - COUPLE_WITH_HEART_MAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fe" - FLAG_FOR_HAUTE_KOTTO_CF_HK = "\U0001f3f4\U000e0063\U000e0066\U000e0068\U000e006b\U000e007f" - FLAG_FOR_GUAINIA_CO_GUA = "\U0001f3f4\U000e0063\U000e006f\U000e0067\U000e0075\U000e0061\U000e007f" - FLAG_FOR_DAMIETTA_EG_DT = "\U0001f3f4\U000e0065\U000e0067\U000e0064\U000e0074\U000e007f" - FLAG_FOR_SAINT_PAUL_DM_10 = "\U0001f3f4\U000e0064\U000e006d\U000e0031\U000e0030\U000e007f" - FLAG_FOR_KAINUU_FI_05 = "\U0001f3f4\U000e0066\U000e0069\U000e0030\U000e0035\U000e007f" - FAMILY_MAN_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f476\U0001f3fd\u200d\U0001f467\U0001f3fd" - FLAG_FOR_KYMENLAAKSO_FI_09 = "\U0001f3f4\U000e0066\U000e0069\U000e0030\U000e0039\U000e007f" - COUPLE_WITH_HEART_WOMAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fd" - FLAG_FOR_BENISHANGUL_GUMUZ_ET_BE = "\U0001f3f4\U000e0065\U000e0074\U000e0062\U000e0065\U000e007f" - FLAG_FOR_SOUTH_KARELIA_FI_02 = "\U0001f3f4\U000e0066\U000e0069\U000e0030\U000e0032\U000e007f" - FLAG_FOR_PUNAKHA_BT_23 = "\U0001f3f4\U000e0062\U000e0074\U000e0032\U000e0033\U000e007f" - FLAG_FOR_YUMA_DO_42 = "\U0001f3f4\U000e0064\U000e006f\U000e0034\U000e0032\U000e007f" - FLAG_FOR_SANTA_ELENA_EC_SE = "\U0001f3f4\U000e0065\U000e0063\U000e0073\U000e0065\U000e007f" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f476\U0001f3fc\u200d\U0001f467\U0001f3fc" - KISS_MAN_MEDIUM_SKIN_TONE_WOMAN_DARK_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3ff" - FLAG_FOR_BISTRITA_NASAUD_RO_BN = "\U0001f3f4\U000e0072\U000e006f\U000e0062\U000e006e\U000e007f" - FLAG_FOR_KEMO_CF_KG = "\U0001f3f4\U000e0063\U000e0066\U000e006b\U000e0067\U000e007f" - FLAG_FOR_BLIDA_DZ_09 = "\U0001f3f4\U000e0064\U000e007a\U000e0030\U000e0039\U000e007f" - FLAG_FOR_HARARI_ET_HA = "\U0001f3f4\U000e0065\U000e0074\U000e0068\U000e0061\U000e007f" - FLAG_FOR_ARTEMISA_CU_15 = "\U0001f3f4\U000e0063\U000e0075\U000e0031\U000e0035\U000e007f" - FLAG_FOR_SOUTHERN_OSTROBOTHNIA_FI_03 = "\U0001f3f4\U000e0066\U000e0069\U000e0030\U000e0033\U000e007f" - FLAG_FOR_OBOCK_DJ_OB = "\U0001f3f4\U000e0064\U000e006a\U000e006f\U000e0062\U000e007f" - FLAG_FOR_MATROUH_EG_MT = "\U0001f3f4\U000e0065\U000e0067\U000e006d\U000e0074\U000e007f" - TAG_LATIN_CAPITAL_LETTER_W = "\U000e0057" - FLAG_FOR_VILLA_CLARA_CU_05 = "\U0001f3f4\U000e0063\U000e0075\U000e0030\U000e0035\U000e007f" - FLAG_FOR_CENTRAL_FJ_C = "\U0001f3f4\U000e0066\U000e006a\U000e0063\U000e007f" - FLAG_FOR_NORTH_KARELIA_FI_13 = "\U0001f3f4\U000e0066\U000e0069\U000e0031\U000e0033\U000e007f" - FLAG_FOR_CHUKHA_BT_12 = "\U0001f3f4\U000e0062\U000e0074\U000e0031\U000e0032\U000e007f" - FLAG_FOR_BENI_BO_B = "\U0001f3f4\U000e0062\U000e006f\U000e0062\U000e007f" - FLAG_FOR_ORAN_DZ_31 = "\U0001f3f4\U000e0064\U000e007a\U000e0033\U000e0031\U000e007f" - FLAG_FOR_NORTHERN_SAVONIA_FI_15 = "\U0001f3f4\U000e0066\U000e0069\U000e0031\U000e0035\U000e007f" - FLAG_FOR_APPENZELL_AUSSERRHODEN_CH_AR = "\U0001f3f4\U000e0063\U000e0068\U000e0061\U000e0072\U000e007f" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f468\U0001f3fc\u200d\U0001f467\U0001f3fc\u200d\U0001f466\U0001f3fc" - FLAG_FOR_PIRKANMAA_FI_11 = "\U0001f3f4\U000e0066\U000e0069\U000e0031\U000e0031\U000e007f" - FLAG_FOR_KOSRAE_FM_KSA = "\U0001f3f4\U000e0066\U000e006d\U000e006b\U000e0073\U000e0061\U000e007f" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f469\U0001f3fc\u200d\U0001f476\U0001f3fc\u200d\U0001f476\U0001f3fc" - FLAG_FOR_OUHAM_PENDE_CF_OP = "\U0001f3f4\U000e0063\U000e0066\U000e006f\U000e0070\U000e007f" - FLAG_FOR_BIO_BIO_CL_BI = "\U0001f3f4\U000e0063\U000e006c\U000e0062\U000e0069\U000e007f" - HORIZONTAL_MALE_WITH_STROKE_SIGN = "\u26a9" - FLAG_FOR_NOUVELLE_AQUITAINE_FR_NAQ = "\U0001f3f4\U000e0066\U000e0072\U000e006e\U000e0061\U000e0071\U000e007f" - FLAG_FOR_ANSE_AUX_PINS_SC_01 = "\U0001f3f4\U000e0073\U000e0063\U000e0030\U000e0031\U000e007f" - FLAG_FOR_SAINT_DAVID_GD_02 = "\U0001f3f4\U000e0067\U000e0064\U000e0030\U000e0032\U000e007f" - FLAG_FOR_OGOOUE_IVINDO_GA_6 = "\U0001f3f4\U000e0067\U000e0061\U000e0036\U000e007f" - FLAG_FOR_ZAMORA_CHINCHIPE_EC_Z = "\U0001f3f4\U000e0065\U000e0063\U000e007a\U000e007f" - COUPLE_WITH_HEART_WOMAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fe" - FLAG_FOR_MOYEN_OGOOUE_GA_3 = "\U0001f3f4\U000e0067\U000e0061\U000e0033\U000e007f" - FLAG_FOR_SATAKUNTA_FI_17 = "\U0001f3f4\U000e0066\U000e0069\U000e0031\U000e0037\U000e007f" - FLAG_FOR_BRONG_AHAFO_GH_BA = "\U0001f3f4\U000e0067\U000e0068\U000e0062\U000e0061\U000e007f" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f476\U0001f3fc\u200d\U0001f476\U0001f3fc" - FLAG_FOR_VAN_TR_65 = "\U0001f3f4\U000e0074\U000e0072\U000e0036\U000e0035\U000e007f" - FLAG_FOR_MTSKHETA_MTIANETI_GE_MM = "\U0001f3f4\U000e0067\U000e0065\U000e006d\U000e006d\U000e007f" - FLAG_FOR_TEBESSA_DZ_12 = "\U0001f3f4\U000e0064\U000e007a\U000e0031\U000e0032\U000e007f" - FLAG_FOR_WOLEU_NTEM_GA_9 = "\U0001f3f4\U000e0067\U000e0061\U000e0039\U000e007f" - FLAG_FOR_SAMEGRELO_ZEMO_SVANETI_GE_SZ = "\U0001f3f4\U000e0067\U000e0065\U000e0073\U000e007a\U000e007f" - FLAG_FOR_IMERETI_GE_IM = "\U0001f3f4\U000e0067\U000e0065\U000e0069\U000e006d\U000e007f" - FLAG_FOR_SAMTSKHE_JAVAKHETI_GE_SJ = "\U0001f3f4\U000e0067\U000e0065\U000e0073\U000e006a\U000e007f" - FLAG_FOR_ABIDJAN_CI_AB = "\U0001f3f4\U000e0063\U000e0069\U000e0061\U000e0062\U000e007f" - FLAG_FOR_SHIRAK_AM_SH = "\U0001f3f4\U000e0061\U000e006d\U000e0073\U000e0068\U000e007f" - FAMILY_MAN_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f476\U0001f3fd" - FLAG_FOR_GUELMA_DZ_24 = "\U0001f3f4\U000e0064\U000e007a\U000e0032\U000e0034\U000e007f" - FLAG_FOR_HAUT_OGOOUE_GA_2 = "\U0001f3f4\U000e0067\U000e0061\U000e0032\U000e007f" - FLAG_FOR_JIHOMORAVSKY_KRAJ_CZ_64 = "\U0001f3f4\U000e0063\U000e007a\U000e0036\U000e0034\U000e007f" - COUPLE_WITH_HEART_MAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fe" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f467\U0001f3fc\u200d\U0001f466\U0001f3fc" - FLAG_FOR_KUJALLEQ_GL_KU = "\U0001f3f4\U000e0067\U000e006c\U000e006b\U000e0075\U000e007f" - FLAG_FOR_THESSALY_GR_E = "\U0001f3f4\U000e0067\U000e0072\U000e0065\U000e007f" - FLAG_FOR_EPIRUS_GR_D = "\U0001f3f4\U000e0067\U000e0072\U000e0064\U000e007f" - FLAG_FOR_PICHINCHA_EC_P = "\U0001f3f4\U000e0065\U000e0063\U000e0070\U000e007f" - FLAG_FOR_ALI_SABIEH_DJ_AS = "\U0001f3f4\U000e0064\U000e006a\U000e0061\U000e0073\U000e007f" - FLAG_FOR_NORTHERN_FJ_N = "\U0001f3f4\U000e0066\U000e006a\U000e006e\U000e007f" - FLAG_FOR_NORTHERN_GH_NP = "\U0001f3f4\U000e0067\U000e0068\U000e006e\U000e0070\U000e007f" - FLAG_FOR_UPPER_WEST_GH_UW = "\U0001f3f4\U000e0067\U000e0068\U000e0075\U000e0077\U000e007f" - FLAG_FOR_UPPER_EAST_GH_UE = "\U0001f3f4\U000e0067\U000e0068\U000e0075\U000e0065\U000e007f" - FLAG_FOR_KINDIA_REGION_GN_D = "\U0001f3f4\U000e0067\U000e006e\U000e0064\U000e007f" - FLAG_FOR_CONAKRY_GN_C = "\U0001f3f4\U000e0067\U000e006e\U000e0063\U000e007f" - FLAG_FOR_IONIAN_ISLANDS_GR_F = "\U0001f3f4\U000e0067\U000e0072\U000e0066\U000e007f" - FAMILY_WOMAN_DARK_SKIN_TONE_MAN_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f468\U0001f3ff\u200d\U0001f476\U0001f3ff" - COUPLE_WITH_HEART_WOMAN_LIGHT_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fc" - FLAG_FOR_SINOE_LR_SI = "\U0001f3f4\U000e006c\U000e0072\U000e0073\U000e0069\U000e007f" - FLAG_FOR_FARANAH_REGION_GN_F = "\U0001f3f4\U000e0067\U000e006e\U000e0066\U000e007f" - COUPLE_WITH_HEART_WOMAN_LIGHT_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fc" - FLAG_FOR_HEILONGJIANG_CN_23 = "\U0001f3f4\U000e0063\U000e006e\U000e0032\U000e0033\U000e007f" - FLAG_FOR_RIO_MUNI_GQ_C = "\U0001f3f4\U000e0067\U000e0071\U000e0063\U000e007f" - WHITE_DOWN_POINTING_LEFT_HAND_INDEX = "\U0001f597" - FLAG_FOR_VOLTA_GH_TV = "\U0001f3f4\U000e0067\U000e0068\U000e0074\U000e0076\U000e007f" - FLAG_FOR_QAASUITSUP_GL_QA = "\U0001f3f4\U000e0067\U000e006c\U000e0071\U000e0061\U000e007f" - KISS_MAN_LIGHT_SKIN_TONE_MAN_DARK_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3ff" - FLAG_FOR_QUICHE_GT_QC = "\U0001f3f4\U000e0067\U000e0074\U000e0071\U000e0063\U000e007f" - FLAG_FOR_HUNAN_CN_43 = "\U0001f3f4\U000e0063\U000e006e\U000e0034\U000e0033\U000e007f" - COUPLE_WITH_HEART_WOMAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fe" - FLAG_FOR_QUETZALTENANGO_GT_QZ = "\U0001f3f4\U000e0067\U000e0074\U000e0071\U000e007a\U000e007f" - FLAG_FOR_LESTE_GW_L = "\U0001f3f4\U000e0067\U000e0077\U000e006c\U000e007f" - FLAG_FOR_CUYUNI_MAZARUNI_GY_CU = "\U0001f3f4\U000e0067\U000e0079\U000e0063\U000e0075\U000e007f" - FAMILY_MAN_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f476\U0001f3ff\u200d\U0001f476\U0001f3ff" - FLAG_FOR_PETEN_GT_PE = "\U0001f3f4\U000e0067\U000e0074\U000e0070\U000e0065\U000e007f" - FLAG_FOR_ZACAPA_GT_ZA = "\U0001f3f4\U000e0067\U000e0074\U000e007a\U000e0061\U000e007f" - FLAG_FOR_LIAONING_CN_21 = "\U0001f3f4\U000e0063\U000e006e\U000e0032\U000e0031\U000e007f" - FLAG_FOR_CHOLUTECA_HN_CH = "\U0001f3f4\U000e0068\U000e006e\U000e0063\U000e0068\U000e007f" - FLAG_FOR_CHIMALTENANGO_GT_CM = "\U0001f3f4\U000e0067\U000e0074\U000e0063\U000e006d\U000e007f" - FLAG_FOR_SOLOLA_GT_SO = "\U0001f3f4\U000e0067\U000e0074\U000e0073\U000e006f\U000e007f" - FLAG_FOR_HUEHUETENANGO_GT_HU = "\U0001f3f4\U000e0067\U000e0074\U000e0068\U000e0075\U000e007f" - KISS_MAN_MAN_LIGHT_SKIN_TONE = "\U0001f468\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fb" - FLAG_FOR_ESCUINTLA_GT_ES = "\U0001f3f4\U000e0067\U000e0074\U000e0065\U000e0073\U000e007f" - FLAG_FOR_CHIQUIMULA_GT_CQ = "\U0001f3f4\U000e0067\U000e0074\U000e0063\U000e0071\U000e007f" - FLAG_FOR_IZABAL_GT_IZ = "\U0001f3f4\U000e0067\U000e0074\U000e0069\U000e007a\U000e007f" - FLAG_FOR_ATLANTIDA_HN_AT = "\U0001f3f4\U000e0068\U000e006e\U000e0061\U000e0074\U000e007f" - FLAG_FOR_NEW_JERSEY_US_NJ = "\U0001f3f4\U000e0075\U000e0073\U000e006e\U000e006a\U000e007f" - FLAG_FOR_ESSEQUIBO_ISLANDS_WEST_DEMERARA_GY_ES = "\U0001f3f4\U000e0067\U000e0079\U000e0065\U000e0073\U000e007f" - FLAG_FOR_POTARO_SIPARUNI_GY_PT = "\U0001f3f4\U000e0067\U000e0079\U000e0070\U000e0074\U000e007f" - FLAG_FOR_BISSAU_GW_BS = "\U0001f3f4\U000e0067\U000e0077\U000e0062\U000e0073\U000e007f" - FLAG_FOR_DEMERARA_MAHAICA_GY_DE = "\U0001f3f4\U000e0067\U000e0079\U000e0064\U000e0065\U000e007f" - FLAG_FOR_IDA_VIRU_EE_44 = "\U0001f3f4\U000e0065\U000e0065\U000e0034\U000e0034\U000e007f" - FLAG_FOR_LIKA_SENJ_HR_09 = "\U0001f3f4\U000e0068\U000e0072\U000e0030\U000e0039\U000e007f" - FLAG_FOR_KRAPINA_ZAGORJE_HR_02 = "\U0001f3f4\U000e0068\U000e0072\U000e0030\U000e0032\U000e007f" - FLAG_FOR_ARTIBONITE_HT_AR = "\U0001f3f4\U000e0068\U000e0074\U000e0061\U000e0072\U000e007f" - FLAG_FOR_OCOTEPEQUE_HN_OC = "\U0001f3f4\U000e0068\U000e006e\U000e006f\U000e0063\U000e007f" - FLAG_FOR_CENTRE_HT_CE = "\U0001f3f4\U000e0068\U000e0074\U000e0063\U000e0065\U000e007f" - FLAG_FOR_SOLOTHURN_CH_SO = "\U0001f3f4\U000e0063\U000e0068\U000e0073\U000e006f\U000e007f" - FLAG_FOR_JALAPA_GT_JA = "\U0001f3f4\U000e0067\U000e0074\U000e006a\U000e0061\U000e007f" - FLAG_FOR_DUBROVNIK_NERETVA_HR_19 = "\U0001f3f4\U000e0068\U000e0072\U000e0031\U000e0039\U000e007f" - FLAG_FOR_VUKOVAR_SYRMIA_HR_16 = "\U0001f3f4\U000e0068\U000e0072\U000e0031\U000e0036\U000e007f" - FLAG_FOR_OSIJEK_BARANJA_HR_14 = "\U0001f3f4\U000e0068\U000e0072\U000e0031\U000e0034\U000e007f" - FLAG_FOR_KOPRIVNICA_KRIZEVCI_HR_06 = "\U0001f3f4\U000e0068\U000e0072\U000e0030\U000e0036\U000e007f" - FLAG_FOR_INTIBUCA_HN_IN = "\U0001f3f4\U000e0068\U000e006e\U000e0069\U000e006e\U000e007f" - FLAG_FOR_POZEGA_SLAVONIA_HR_11 = "\U0001f3f4\U000e0068\U000e0072\U000e0031\U000e0031\U000e007f" - FLAG_FOR_BERAT_COUNTY_AL_01 = "\U0001f3f4\U000e0061\U000e006c\U000e0030\U000e0031\U000e007f" - FLAG_FOR_SAINT_LUKE_DM_07 = "\U0001f3f4\U000e0064\U000e006d\U000e0030\U000e0037\U000e007f" - FLAG_FOR_COMAYAGUA_HN_CM = "\U0001f3f4\U000e0068\U000e006e\U000e0063\U000e006d\U000e007f" - FLAG_FOR_YORO_HN_YO = "\U0001f3f4\U000e0068\U000e006e\U000e0079\U000e006f\U000e007f" - FLAG_FOR_COPAN_HN_CP = "\U0001f3f4\U000e0068\U000e006e\U000e0063\U000e0070\U000e007f" - FLAG_FOR_VALLE_HN_VA = "\U0001f3f4\U000e0068\U000e006e\U000e0076\U000e0061\U000e007f" - FLAG_FOR_LEMPIRA_HN_LE = "\U0001f3f4\U000e0068\U000e006e\U000e006c\U000e0065\U000e007f" - FLAG_FOR_ME_IMURJE_HR_20 = "\U0001f3f4\U000e0068\U000e0072\U000e0032\U000e0030\U000e007f" - FLAG_FOR_SIBENIK_KNIN_HR_15 = "\U0001f3f4\U000e0068\U000e0072\U000e0031\U000e0035\U000e007f" - MERCURY = "\u263f\ufe0f" - FLAG_FOR_FRANCISCO_MORAZAN_HN_FM = "\U0001f3f4\U000e0068\U000e006e\U000e0066\U000e006d\U000e007f" - FLAG_FOR_OLANCHO_HN_OL = "\U0001f3f4\U000e0068\U000e006e\U000e006f\U000e006c\U000e007f" - FLAG_FOR_CORTES_HN_CR = "\U0001f3f4\U000e0068\U000e006e\U000e0063\U000e0072\U000e007f" - FLAG_FOR_ZAGREB_COUNTY_HR_01 = "\U0001f3f4\U000e0068\U000e0072\U000e0030\U000e0031\U000e007f" - FLAG_FOR_ISTRIA_HR_18 = "\U0001f3f4\U000e0068\U000e0072\U000e0031\U000e0038\U000e007f" - FLAG_FOR_ZADAR_HR_13 = "\U0001f3f4\U000e0068\U000e0072\U000e0031\U000e0033\U000e007f" - FLAG_FOR_PEST_HU_PE = "\U0001f3f4\U000e0068\U000e0075\U000e0070\U000e0065\U000e007f" - FAMILY_MAN_MEDIUM_SKIN_TONE_GIRL_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f467\U0001f3fd\u200d\U0001f466\U0001f3fd" - FLAG_FOR_HAJDU_BIHAR_HU_HB = "\U0001f3f4\U000e0068\U000e0075\U000e0068\U000e0062\U000e007f" - COUPLE_WITH_HEART_MAN_DARK_SKIN_TONE_MAN_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3ff" - FLAG_FOR_OUEST_HT_OU = "\U0001f3f4\U000e0068\U000e0074\U000e006f\U000e0075\U000e007f" - FLAG_FOR_SZOLNOK_HU_SK = "\U0001f3f4\U000e0068\U000e0075\U000e0073\U000e006b\U000e007f" - FLAG_FOR_NOGRAD_HU_NO = "\U0001f3f4\U000e0068\U000e0075\U000e006e\U000e006f\U000e007f" - FLAG_FOR_BORSOD_ABAUJ_ZEMPLEN_HU_BZ = "\U0001f3f4\U000e0068\U000e0075\U000e0062\U000e007a\U000e007f" - FLAG_FOR_SOPRON_HU_SN = "\U0001f3f4\U000e0068\U000e0075\U000e0073\U000e006e\U000e007f" - FLAG_FOR_JIJEL_DZ_18 = "\U0001f3f4\U000e0064\U000e007a\U000e0031\U000e0038\U000e007f" - FLAG_FOR_BEKESCSABA_HU_BC = "\U0001f3f4\U000e0068\U000e0075\U000e0062\U000e0063\U000e007f" - FLAG_FOR_NYIREGYHAZA_HU_NY = "\U0001f3f4\U000e0068\U000e0075\U000e006e\U000e0079\U000e007f" - FLAG_FOR_NORD_HT_ND = "\U0001f3f4\U000e0068\U000e0074\U000e006e\U000e0064\U000e007f" - FLAG_FOR_FEJER_HU_FE = "\U0001f3f4\U000e0068\U000e0075\U000e0066\U000e0065\U000e007f" - FLAG_FOR_NIPPES_HT_NI = "\U0001f3f4\U000e0068\U000e0074\U000e006e\U000e0069\U000e007f" - FLAG_FOR_ERD_HU_ER = "\U0001f3f4\U000e0068\U000e0075\U000e0065\U000e0072\U000e007f" - FLAG_FOR_SZOMBATHELY_HU_SH = "\U0001f3f4\U000e0068\U000e0075\U000e0073\U000e0068\U000e007f" - FLAG_FOR_EASTERN_GH_EP = "\U0001f3f4\U000e0067\U000e0068\U000e0065\U000e0070\U000e007f" - FLAG_FOR_DIRE_DAWA_ET_DD = "\U0001f3f4\U000e0065\U000e0074\U000e0064\U000e0064\U000e007f" - COUPLE_WITH_HEART_WOMAN_MEDIUM_DARK_SKIN_TONE_WOMAN = "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f469" - FLAG_FOR_SZEKESFEHERVAR_HU_SF = "\U0001f3f4\U000e0068\U000e0075\U000e0073\U000e0066\U000e007f" - COUPLE_WITH_HEART_MAN_MEDIUM_DARK_SKIN_TONE_WOMAN_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fb" - FLAG_FOR_KOMAROM_ESZTERGOM_HU_KE = "\U0001f3f4\U000e0068\U000e0075\U000e006b\U000e0065\U000e007f" - FLAG_FOR_MISKOLC_HU_MI = "\U0001f3f4\U000e0068\U000e0075\U000e006d\U000e0069\U000e007f" - FLAG_FOR_DEBRECEN_HU_DE = "\U0001f3f4\U000e0068\U000e0075\U000e0064\U000e0065\U000e007f" - FLAG_FOR_BACS_KISKUN_HU_BK = "\U0001f3f4\U000e0068\U000e0075\U000e0062\U000e006b\U000e007f" - FLAG_FOR_GYOR_HU_GY = "\U0001f3f4\U000e0068\U000e0075\U000e0067\U000e0079\U000e007f" - FLAG_FOR_DUNAUJVAROS_HU_DU = "\U0001f3f4\U000e0068\U000e0075\U000e0064\U000e0075\U000e007f" - FLAG_FOR_HEVES_HU_HE = "\U0001f3f4\U000e0068\U000e0075\U000e0068\U000e0065\U000e007f" - FLAG_FOR_BEKES_HU_BE = "\U0001f3f4\U000e0068\U000e0075\U000e0062\U000e0065\U000e007f" - FLAG_FOR_JASZ_NAGYKUN_SZOLNOK_HU_JN = "\U0001f3f4\U000e0068\U000e0075\U000e006a\U000e006e\U000e007f" - FLAG_FOR_RAPLA_EE_70 = "\U0001f3f4\U000e0065\U000e0065\U000e0037\U000e0030\U000e007f" - FLAG_FOR_ZALAEGERSZEG_HU_ZE = "\U0001f3f4\U000e0068\U000e0075\U000e007a\U000e0065\U000e007f" - FLAG_FOR_CONNACHT_IE_C = "\U0001f3f4\U000e0069\U000e0065\U000e0063\U000e007f" - COUPLE_WITH_HEART_WOMAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fd" - FLAG_FOR_CENTRAL_DISTRICT_IL_M = "\U0001f3f4\U000e0069\U000e006c\U000e006d\U000e007f" - FAMILY_MAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f468\U0001f3fd\u200d\U0001f476\U0001f3fd\u200d\U0001f466\U0001f3fd" - FLAG_FOR_GREATER_ACCRA_GH_AA = "\U0001f3f4\U000e0067\U000e0068\U000e0061\U000e0061\U000e007f" - FLAG_FOR_HAIFA_DISTRICT_IL_HA = "\U0001f3f4\U000e0069\U000e006c\U000e0068\U000e0061\U000e007f" - FLAG_FOR_BARANYA_HU_BA = "\U0001f3f4\U000e0068\U000e0075\U000e0062\U000e0061\U000e007f" - FLAG_FOR_VESZPREM_HU_VM = "\U0001f3f4\U000e0068\U000e0075\U000e0076\U000e006d\U000e007f" - FLAG_FOR_VESZPREM_COUNTY_HU_VE = "\U0001f3f4\U000e0068\U000e0075\U000e0076\U000e0065\U000e007f" - FLAG_FOR_MAGDALENA_CO_MAG = "\U0001f3f4\U000e0063\U000e006f\U000e006d\U000e0061\U000e0067\U000e007f" - FLAG_FOR_NAGYKANIZSA_HU_NK = "\U0001f3f4\U000e0068\U000e0075\U000e006e\U000e006b\U000e007f" - FLAG_FOR_SOMOGY_HU_SO = "\U0001f3f4\U000e0068\U000e0075\U000e0073\U000e006f\U000e007f" - FLAG_FOR_SOUTHERN_DISTRICT_IL_D = "\U0001f3f4\U000e0069\U000e006c\U000e0064\U000e007f" - COUPLE_WITH_HEART_WOMAN_MEDIUM_DARK_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fc" - FLAG_FOR_SAINT_PETER_DM_11 = "\U0001f3f4\U000e0064\U000e006d\U000e0031\U000e0031\U000e007f" - KISS_WOMAN_MEDIUM_SKIN_TONE_MAN_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fb" - FLAG_FOR_TOLNA_HU_TO = "\U0001f3f4\U000e0068\U000e0075\U000e0074\U000e006f\U000e007f" - FLAG_FOR_ODISHA_IN_OR = "\U0001f3f4\U000e0069\U000e006e\U000e006f\U000e0072\U000e007f" - FLAG_FOR_PUTUMAYO_CO_PUT = "\U0001f3f4\U000e0063\U000e006f\U000e0070\U000e0075\U000e0074\U000e007f" - FLAG_FOR_DOHUK_IQ_DA = "\U0001f3f4\U000e0069\U000e0071\U000e0064\U000e0061\U000e007f" - FLAG_FOR_AL_MUTHANNA_IQ_MU = "\U0001f3f4\U000e0069\U000e0071\U000e006d\U000e0075\U000e007f" - WHITE_DIAMOND_SUIT = "\u2662" - BLACK_CHESS_QUEEN = "\u265b" - FLAG_FOR_SALADIN_IQ_SD = "\U0001f3f4\U000e0069\U000e0071\U000e0073\U000e0064\U000e007f" - TAG_LATIN_SMALL_LETTER_V = "\U000e0076" - FAMILY_WOMAN_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f467\U0001f3ff\u200d\U0001f467\U0001f3ff" - FLAG_FOR_SHANDONG_CN_37 = "\U0001f3f4\U000e0063\U000e006e\U000e0033\U000e0037\U000e007f" - FLAG_FOR_AL_ANBAR_IQ_AN = "\U0001f3f4\U000e0069\U000e0071\U000e0061\U000e006e\U000e007f" - FLAG_FOR_NAJAF_IQ_NA = "\U0001f3f4\U000e0069\U000e0071\U000e006e\U000e0061\U000e007f" - FLAG_FOR_KARBALA_IQ_KA = "\U0001f3f4\U000e0069\U000e0071\U000e006b\U000e0061\U000e007f" - FLAG_FOR_SULAWESI_ID_SL = "\U0001f3f4\U000e0069\U000e0064\U000e0073\U000e006c\U000e007f" - FLAG_FOR_ATLANTICO_CO_ATL = "\U0001f3f4\U000e0063\U000e006f\U000e0061\U000e0074\U000e006c\U000e007f" - FAMILY_MAN_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f476\U0001f3fd\u200d\U0001f476\U0001f3fd" - FLAG_FOR_DIYALA_IQ_DI = "\U0001f3f4\U000e0069\U000e0071\U000e0064\U000e0069\U000e007f" - FLAG_FOR_AL_QADISIYYAH_IQ_QA = "\U0001f3f4\U000e0069\U000e0071\U000e0071\U000e0061\U000e007f" - FLAG_FOR_NAGALAND_IN_NL = "\U0001f3f4\U000e0069\U000e006e\U000e006e\U000e006c\U000e007f" - FLAG_FOR_SULAYMANIYAH_IQ_SU = "\U0001f3f4\U000e0069\U000e0071\U000e0073\U000e0075\U000e007f" - FLAG_FOR_SUEZ_EG_SUZ = "\U0001f3f4\U000e0065\U000e0067\U000e0073\U000e0075\U000e007a\U000e007f" - FLAG_FOR_MEGHALAYA_IN_ML = "\U0001f3f4\U000e0069\U000e006e\U000e006d\U000e006c\U000e007f" - FLAG_FOR_NINEVEH_IQ_NI = "\U0001f3f4\U000e0069\U000e0071\U000e006e\U000e0069\U000e007f" - FLAG_FOR_NEUCHATEL_CH_NE = "\U0001f3f4\U000e0063\U000e0068\U000e006e\U000e0065\U000e007f" - FLAG_FOR_MIZORAM_IN_MZ = "\U0001f3f4\U000e0069\U000e006e\U000e006d\U000e007a\U000e007f" - FLAG_FOR_OBWALDEN_CH_OW = "\U0001f3f4\U000e0063\U000e0068\U000e006f\U000e0077\U000e007f" - FLAG_FOR_LAKSHADWEEP_IN_LD = "\U0001f3f4\U000e0069\U000e006e\U000e006c\U000e0064\U000e007f" - FLAG_FOR_HORMOZGAN_IR_23 = "\U0001f3f4\U000e0069\U000e0072\U000e0032\U000e0033\U000e007f" - FLAG_FOR_QAZVIN_IR_28 = "\U0001f3f4\U000e0069\U000e0072\U000e0032\U000e0038\U000e007f" - FLAG_FOR_MAYSAN_IQ_MA = "\U0001f3f4\U000e0069\U000e0071\U000e006d\U000e0061\U000e007f" - FLAG_FOR_SEMNAN_IR_12 = "\U0001f3f4\U000e0069\U000e0072\U000e0031\U000e0032\U000e007f" - FLAG_FOR_ALAJUELA_CR_A = "\U0001f3f4\U000e0063\U000e0072\U000e0061\U000e007f" - FLAG_FOR_MARKAZI_IR_22 = "\U0001f3f4\U000e0069\U000e0072\U000e0032\U000e0032\U000e007f" - COUPLE_WITH_HEART_WOMAN_WOMAN_MEDIUM_DARK_SKIN_TONE = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fe" - FLAG_FOR_RHODE_ISLAND_US_RI = "\U0001f3f4\U000e0075\U000e0073\U000e0072\U000e0069\U000e007f" - FLAG_FOR_PIRAN_SI_090 = "\U0001f3f4\U000e0073\U000e0069\U000e0030\U000e0039\U000e0030\U000e007f" - FLAG_FOR_LEINSTER_IE_L = "\U0001f3f4\U000e0069\U000e0065\U000e006c\U000e007f" - FLAG_FOR_BANJUL_GM_B = "\U0001f3f4\U000e0067\U000e006d\U000e0062\U000e007f" - FLAG_FOR_NORTHWESTERN_IS_5 = "\U0001f3f4\U000e0069\U000e0073\U000e0035\U000e007f" - FAMILY_WOMAN_DARK_SKIN_TONE_WOMAN_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f469\U0001f3ff\u200d\U0001f467\U0001f3ff\u200d\U0001f466\U0001f3ff" - FLAG_FOR_MAHAICA_BERBICE_GY_MA = "\U0001f3f4\U000e0067\U000e0079\U000e006d\U000e0061\U000e007f" - FLAG_FOR_YAZD_IR_25 = "\U0001f3f4\U000e0069\U000e0072\U000e0032\U000e0035\U000e007f" - FLAG_FOR_WASIT_IQ_WA = "\U0001f3f4\U000e0069\U000e0071\U000e0077\U000e0061\U000e007f" - FLAG_FOR_SAINT_ANDREW_DM_02 = "\U0001f3f4\U000e0064\U000e006d\U000e0030\U000e0032\U000e007f" - FLAG_FOR_SUMATRA_ID_SM = "\U0001f3f4\U000e0069\U000e0064\U000e0073\U000e006d\U000e007f" - FLAG_FOR_ZALA_HU_ZA = "\U0001f3f4\U000e0068\U000e0075\U000e007a\U000e0061\U000e007f" - COUPLE_WITH_HEART_WOMAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fe" - FLAG_FOR_BOLIVAR_CO_BOL = "\U0001f3f4\U000e0063\U000e006f\U000e0062\U000e006f\U000e006c\U000e007f" - FLAG_FOR_SAINT_ELIZABETH_JM_11 = "\U0001f3f4\U000e006a\U000e006d\U000e0031\U000e0031\U000e007f" - FLAG_FOR_WESTERN_GH_WP = "\U0001f3f4\U000e0067\U000e0068\U000e0077\U000e0070\U000e007f" - FLAG_FOR_JERASH_JO_JA = "\U0001f3f4\U000e006a\U000e006f\U000e006a\U000e0061\U000e007f" - FLAG_FOR_TRELAWNY_JM_07 = "\U0001f3f4\U000e006a\U000e006d\U000e0030\U000e0037\U000e007f" - FLAG_FOR_MOLISE_IT_67 = "\U0001f3f4\U000e0069\U000e0074\U000e0036\U000e0037\U000e007f" - FLAG_FOR_NORTHEASTERN_IS_6 = "\U0001f3f4\U000e0069\U000e0073\U000e0036\U000e007f" - FLAG_FOR_AMMAN_JO_AM = "\U0001f3f4\U000e006a\U000e006f\U000e0061\U000e006d\U000e007f" - FLAG_FOR_UMM_AL_QUWAIN_AE_UQ = "\U0001f3f4\U000e0061\U000e0065\U000e0075\U000e0071\U000e007f" - FLAG_FOR_GOLESTAN_IR_27 = "\U0001f3f4\U000e0069\U000e0072\U000e0032\U000e0037\U000e007f" - FLAG_FOR_TAFILAH_JO_AT = "\U0001f3f4\U000e006a\U000e006f\U000e0061\U000e0074\U000e007f" - FLAG_FOR_KECSKEMET_HU_KM = "\U0001f3f4\U000e0068\U000e0075\U000e006b\U000e006d\U000e007f" - FLAG_FOR_UMBRIA_IT_55 = "\U0001f3f4\U000e0069\U000e0074\U000e0035\U000e0035\U000e007f" - FLAG_FOR_SAINT_ANN_JM_06 = "\U0001f3f4\U000e006a\U000e006d\U000e0030\U000e0036\U000e007f" - FLAG_FOR_SUD_HT_SD = "\U0001f3f4\U000e0068\U000e0074\U000e0073\U000e0064\U000e007f" - COUPLE_WITH_HEART_MAN_MEDIUM_DARK_SKIN_TONE_WOMAN = "\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f469" - FLAG_FOR_CHAHARMAHAL_AND_BAKHTIARI_IR_08 = "\U0001f3f4\U000e0069\U000e0072\U000e0030\U000e0038\U000e007f" - FLAG_FOR_WESTMORELAND_JM_10 = "\U0001f3f4\U000e006a\U000e006d\U000e0031\U000e0030\U000e007f" - FLAG_FOR_LIGURIA_IT_42 = "\U0001f3f4\U000e0069\U000e0074\U000e0034\U000e0032\U000e007f" - FLAG_FOR_GRACIAS_A_DIOS_HN_GD = "\U0001f3f4\U000e0068\U000e006e\U000e0067\U000e0064\U000e007f" - FLAG_FOR_CLARENDON_JM_13 = "\U0001f3f4\U000e006a\U000e006d\U000e0031\U000e0033\U000e007f" - FLAG_FOR_AQABA_JO_AQ = "\U0001f3f4\U000e006a\U000e006f\U000e0061\U000e0071\U000e007f" - FLAG_FOR_IRBID_JO_IR = "\U0001f3f4\U000e006a\U000e006f\U000e0069\U000e0072\U000e007f" - FLAG_FOR_BALQA_JO_BA = "\U0001f3f4\U000e006a\U000e006f\U000e0062\U000e0061\U000e007f" - FLAG_FOR_ZARQA_JO_AZ = "\U0001f3f4\U000e006a\U000e006f\U000e0061\U000e007a\U000e007f" - FLAG_FOR_OSTROBOTHNIA_FI_12 = "\U0001f3f4\U000e0066\U000e0069\U000e0031\U000e0032\U000e007f" - FAMILY_MAN_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f476\U0001f3ff\u200d\U0001f466\U0001f3ff" - FLAG_FOR_MAFRAQ_JO_MA = "\U0001f3f4\U000e006a\U000e006f\U000e006d\U000e0061\U000e007f" - FLAG_FOR_ARAUCANIA_CL_AR = "\U0001f3f4\U000e0063\U000e006c\U000e0061\U000e0072\U000e007f" - FLAG_FOR_EAST_CM_ES = "\U0001f3f4\U000e0063\U000e006d\U000e0065\U000e0073\U000e007f" - FLAG_FOR_YAMAGUCHI_JP_35 = "\U0001f3f4\U000e006a\U000e0070\U000e0033\U000e0035\U000e007f" - FLAG_FOR_NAGANO_JP_20 = "\U0001f3f4\U000e006a\U000e0070\U000e0032\U000e0030\U000e007f" - FLAG_FOR_TOCHIGI_JP_09 = "\U0001f3f4\U000e006a\U000e0070\U000e0030\U000e0039\U000e007f" - FLAG_FOR_SHIMANE_JP_32 = "\U0001f3f4\U000e006a\U000e0070\U000e0033\U000e0032\U000e007f" - FLAG_FOR_ILAM_IR_05 = "\U0001f3f4\U000e0069\U000e0072\U000e0030\U000e0035\U000e007f" - FLAG_FOR_NARA_JP_29 = "\U0001f3f4\U000e006a\U000e0070\U000e0032\U000e0039\U000e007f" - FLAG_FOR_SPLIT_DALMATIA_HR_17 = "\U0001f3f4\U000e0068\U000e0072\U000e0031\U000e0037\U000e007f" - FLAG_FOR_EHIME_JP_38 = "\U0001f3f4\U000e006a\U000e0070\U000e0033\U000e0038\U000e007f" - FLAG_FOR_ORELLANA_EC_D = "\U0001f3f4\U000e0065\U000e0063\U000e0064\U000e007f" - FLAG_FOR_LA_RIOJA_ES_RI = "\U0001f3f4\U000e0065\U000e0073\U000e0072\U000e0069\U000e007f" - FLAG_FOR_IBARAKI_JP_08 = "\U0001f3f4\U000e006a\U000e0070\U000e0030\U000e0038\U000e007f" - FLAG_FOR_GRANMA_CU_12 = "\U0001f3f4\U000e0063\U000e0075\U000e0031\U000e0032\U000e007f" - FLAG_FOR_SHIZUOKA_JP_22 = "\U0001f3f4\U000e006a\U000e0070\U000e0032\U000e0032\U000e007f" - FLAG_FOR_IWATE_JP_03 = "\U0001f3f4\U000e006a\U000e0070\U000e0030\U000e0033\U000e007f" - FLAG_FOR_TOYAMA_JP_16 = "\U0001f3f4\U000e006a\U000e0070\U000e0031\U000e0036\U000e007f" - FLAG_FOR_MADABA_JO_MD = "\U0001f3f4\U000e006a\U000e006f\U000e006d\U000e0064\U000e007f" - FLAG_FOR_YAMANASHI_JP_19 = "\U0001f3f4\U000e006a\U000e0070\U000e0031\U000e0039\U000e007f" - FLAG_FOR_TOKUSHIMA_JP_36 = "\U0001f3f4\U000e006a\U000e0070\U000e0033\U000e0036\U000e007f" - FLAG_FOR_ISHIKAWA_JP_17 = "\U0001f3f4\U000e006a\U000e0070\U000e0031\U000e0037\U000e007f" - FLAG_FOR_KANAGAWA_JP_14 = "\U0001f3f4\U000e006a\U000e0070\U000e0031\U000e0034\U000e007f" - FLAG_FOR_MIE_JP_24 = "\U0001f3f4\U000e006a\U000e0070\U000e0032\U000e0034\U000e007f" - FLAG_FOR_NIIGATA_JP_15 = "\U0001f3f4\U000e006a\U000e0070\U000e0031\U000e0035\U000e007f" - FLAG_FOR_HYOGO_JP_28 = "\U0001f3f4\U000e006a\U000e0070\U000e0032\U000e0038\U000e007f" - FLAG_FOR_DHI_QAR_IQ_DQ = "\U0001f3f4\U000e0069\U000e0071\U000e0064\U000e0071\U000e007f" - FLAG_FOR_SHIGA_JP_25 = "\U0001f3f4\U000e006a\U000e0070\U000e0032\U000e0035\U000e007f" - FLAG_FOR_TOTTORI_JP_31 = "\U0001f3f4\U000e006a\U000e0070\U000e0033\U000e0031\U000e007f" - FLAG_FOR_OKAYAMA_JP_33 = "\U0001f3f4\U000e006a\U000e0070\U000e0033\U000e0033\U000e007f" - FLAG_FOR_CHIBA_JP_12 = "\U0001f3f4\U000e006a\U000e0070\U000e0031\U000e0032\U000e007f" - FLAG_FOR_MURANG_A_KE_29 = "\U0001f3f4\U000e006b\U000e0065\U000e0032\U000e0039\U000e007f" - FLAG_FOR_SOUTH_SINAI_EG_JS = "\U0001f3f4\U000e0065\U000e0067\U000e006a\U000e0073\U000e007f" - FLAG_FOR_KAJIADO_KE_10 = "\U0001f3f4\U000e006b\U000e0065\U000e0031\U000e0030\U000e007f" - FLAG_FOR_KOCHI_JP_39 = "\U0001f3f4\U000e006a\U000e0070\U000e0033\U000e0039\U000e007f" - FLAG_FOR_BASRA_IQ_BA = "\U0001f3f4\U000e0069\U000e0071\U000e0062\U000e0061\U000e007f" - FLAG_FOR_NAROK_KE_33 = "\U0001f3f4\U000e006b\U000e0065\U000e0033\U000e0033\U000e007f" - FLAG_FOR_FUKUOKA_JP_40 = "\U0001f3f4\U000e006a\U000e0070\U000e0034\U000e0030\U000e007f" - FLAG_FOR_BUNGOMA_KE_03 = "\U0001f3f4\U000e006b\U000e0065\U000e0030\U000e0033\U000e007f" - FLAG_FOR_GUNMA_JP_10 = "\U0001f3f4\U000e006a\U000e0070\U000e0031\U000e0030\U000e007f" - FLAG_FOR_MERU_KE_26 = "\U0001f3f4\U000e006b\U000e0065\U000e0032\U000e0036\U000e007f" - FLAG_FOR_ELGEYO_MARAKWET_KE_05 = "\U0001f3f4\U000e006b\U000e0065\U000e0030\U000e0035\U000e007f" - FLAG_FOR_LAIKIPIA_KE_20 = "\U0001f3f4\U000e006b\U000e0065\U000e0032\U000e0030\U000e007f" - FLAG_FOR_ISIOLO_KE_09 = "\U0001f3f4\U000e006b\U000e0065\U000e0030\U000e0039\U000e007f" - FLAG_FOR_KISII_KE_16 = "\U0001f3f4\U000e006b\U000e0065\U000e0031\U000e0036\U000e007f" - FLAG_FOR_SOUTHERN_PENINSULA_IS_2 = "\U0001f3f4\U000e0069\U000e0073\U000e0032\U000e007f" - FLAG_FOR_KAKAMEGA_KE_11 = "\U0001f3f4\U000e006b\U000e0065\U000e0031\U000e0031\U000e007f" - FLAG_FOR_MAKUENI_KE_23 = "\U0001f3f4\U000e006b\U000e0065\U000e0032\U000e0033\U000e007f" - FLAG_FOR_BITOLA_MK_04 = "\U0001f3f4\U000e006d\U000e006b\U000e0030\U000e0034\U000e007f" - FLAG_FOR_MIGORI_KE_27 = "\U0001f3f4\U000e006b\U000e0065\U000e0032\U000e0037\U000e007f" - FLAG_FOR_KILIFI_KE_14 = "\U0001f3f4\U000e006b\U000e0065\U000e0031\U000e0034\U000e007f" - FLAG_FOR_LAMU_KE_21 = "\U0001f3f4\U000e006b\U000e0065\U000e0032\U000e0031\U000e007f" - FLAG_FOR_MOMBASA_KE_28 = "\U0001f3f4\U000e006b\U000e0065\U000e0032\U000e0038\U000e007f" - FLAG_FOR_TATABANYA_HU_TB = "\U0001f3f4\U000e0068\U000e0075\U000e0074\U000e0062\U000e007f" - FLAG_FOR_BOMET_KE_02 = "\U0001f3f4\U000e006b\U000e0065\U000e0030\U000e0032\U000e007f" - FLAG_FOR_KUMAMOTO_JP_43 = "\U0001f3f4\U000e006a\U000e0070\U000e0034\U000e0033\U000e007f" - FLAG_FOR_KIAMBU_KE_13 = "\U0001f3f4\U000e006b\U000e0065\U000e0031\U000e0033\U000e007f" - FLAG_FOR_NANDI_KE_32 = "\U0001f3f4\U000e006b\U000e0065\U000e0033\U000e0032\U000e007f" - FLAG_FOR_YAMAGATA_JP_06 = "\U0001f3f4\U000e006a\U000e0070\U000e0030\U000e0036\U000e007f" - FLAG_FOR_BUSIA_KE_04 = "\U0001f3f4\U000e006b\U000e0065\U000e0030\U000e0034\U000e007f" - FLAG_FOR_MANDERA_KE_24 = "\U0001f3f4\U000e006b\U000e0065\U000e0032\U000e0034\U000e007f" - FLAG_FOR_TURKANA_KE_43 = "\U0001f3f4\U000e006b\U000e0065\U000e0034\U000e0033\U000e007f" - FLAG_FOR_NARYN_KG_N = "\U0001f3f4\U000e006b\U000e0067\U000e006e\U000e007f" - FLAG_FOR_SIAYA_KE_38 = "\U0001f3f4\U000e006b\U000e0065\U000e0033\U000e0038\U000e007f" - FLAG_FOR_UASIN_GISHU_KE_44 = "\U0001f3f4\U000e006b\U000e0065\U000e0034\U000e0034\U000e007f" - FLAG_FOR_JALAL_ABAD_KG_J = "\U0001f3f4\U000e006b\U000e0067\U000e006a\U000e007f" - FLAG_FOR_MONDULKIRI_KH_11 = "\U0001f3f4\U000e006b\U000e0068\U000e0031\U000e0031\U000e007f" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f466\U0001f3fe" - FLAG_FOR_VIHIGA_KE_45 = "\U0001f3f4\U000e006b\U000e0065\U000e0034\U000e0035\U000e007f" - FLAG_FOR_WEST_POKOT_KE_47 = "\U0001f3f4\U000e006b\U000e0065\U000e0034\U000e0037\U000e007f" - FLAG_FOR_PREY_VENG_KH_14 = "\U0001f3f4\U000e006b\U000e0068\U000e0031\U000e0034\U000e007f" - FLAG_FOR_TALAS_KG_T = "\U0001f3f4\U000e006b\U000e0067\U000e0074\U000e007f" - FLAG_FOR_HOMA_BAY_KE_08 = "\U0001f3f4\U000e006b\U000e0065\U000e0030\U000e0038\U000e007f" - FLAG_FOR_KRATIE_KH_10 = "\U0001f3f4\U000e006b\U000e0068\U000e0031\U000e0030\U000e007f" - FLAG_FOR_TBONG_KHMUM_KH_25 = "\U0001f3f4\U000e006b\U000e0068\U000e0032\U000e0035\U000e007f" - FLAG_FOR_BISHKEK_KG_GB = "\U0001f3f4\U000e006b\U000e0067\U000e0067\U000e0062\U000e007f" - FLAG_FOR_ODDAR_MEANCHEY_KH_22 = "\U0001f3f4\U000e006b\U000e0068\U000e0032\U000e0032\U000e007f" - FLAG_FOR_PHNOM_PENH_KH_12 = "\U0001f3f4\U000e006b\U000e0068\U000e0031\U000e0032\U000e007f" - FLAG_FOR_NYERI_KE_36 = "\U0001f3f4\U000e006b\U000e0065\U000e0033\U000e0036\U000e007f" - FLAG_FOR_STUNG_TRENG_KH_19 = "\U0001f3f4\U000e006b\U000e0068\U000e0031\U000e0039\U000e007f" - FLAG_FOR_PURSAT_KH_15 = "\U0001f3f4\U000e006b\U000e0068\U000e0031\U000e0035\U000e007f" - FLAG_FOR_NYANDARUA_KE_35 = "\U0001f3f4\U000e006b\U000e0065\U000e0033\U000e0035\U000e007f" - FLAG_FOR_SAMBURU_KE_37 = "\U0001f3f4\U000e006b\U000e0065\U000e0033\U000e0037\U000e007f" - FLAG_FOR_MACHAKOS_KE_22 = "\U0001f3f4\U000e006b\U000e0065\U000e0032\U000e0032\U000e007f" - FLAG_FOR_BATTAMBANG_KH_2 = "\U0001f3f4\U000e006b\U000e0068\U000e0032\U000e007f" - FLAG_FOR_TAITA_TAVETA_KE_39 = "\U0001f3f4\U000e006b\U000e0065\U000e0033\U000e0039\U000e007f" - FLAG_FOR_TAKEO_KH_21 = "\U0001f3f4\U000e006b\U000e0068\U000e0032\U000e0031\U000e007f" - FLAG_FOR_SIHANOUKVILLE_KH_18 = "\U0001f3f4\U000e006b\U000e0068\U000e0031\U000e0038\U000e007f" - FLAG_FOR_NYAMIRA_KE_34 = "\U0001f3f4\U000e006b\U000e0065\U000e0033\U000e0034\U000e007f" - FLAG_FOR_KEP_KH_23 = "\U0001f3f4\U000e006b\U000e0068\U000e0032\U000e0033\U000e007f" - FLAG_FOR_THARAKA_NITHI_KE_41 = "\U0001f3f4\U000e006b\U000e0065\U000e0034\U000e0031\U000e007f" - FAMILY_MAN_MEDIUM_DARK_SKIN_TONE_WOMAN_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE_BOY_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f469\U0001f3fe\u200d\U0001f466\U0001f3fe\u200d\U0001f466\U0001f3fe" - FLAG_FOR_SUCHITEPEQUEZ_GT_SU = "\U0001f3f4\U000e0067\U000e0074\U000e0073\U000e0075\U000e007f" - FLAG_FOR_AGUASCALIENTES_MX_AGU = "\U0001f3f4\U000e006d\U000e0078\U000e0061\U000e0067\U000e0075\U000e007f" - FLAG_FOR_COLON_HN_CL = "\U0001f3f4\U000e0068\U000e006e\U000e0063\U000e006c\U000e007f" - FLAG_FOR_KAMPOT_KH_7 = "\U0001f3f4\U000e006b\U000e0068\U000e0037\U000e007f" - FLAG_FOR_SIRNAK_TR_73 = "\U0001f3f4\U000e0074\U000e0072\U000e0037\U000e0033\U000e007f" - FLAG_FOR_ZAGREB_HR_21 = "\U0001f3f4\U000e0068\U000e0072\U000e0032\U000e0031\U000e007f" - FLAG_FOR_GRAND_ANSE_HT_GA = "\U0001f3f4\U000e0068\U000e0074\U000e0067\U000e0061\U000e007f" - FLAG_FOR_LISBON_PT_11 = "\U0001f3f4\U000e0070\U000e0074\U000e0031\U000e0031\U000e007f" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f468\U0001f3fc\u200d\U0001f476\U0001f3fc\u200d\U0001f467\U0001f3fc" - FLAG_FOR_CHAGANG_KP_04 = "\U0001f3f4\U000e006b\U000e0070\U000e0030\U000e0034\U000e007f" - FLAG_FOR_SOUTH_HAMGYONG_KP_08 = "\U0001f3f4\U000e006b\U000e0070\U000e0030\U000e0038\U000e007f" - FLAG_FOR_SOUTH_CHUNGCHEONG_KR_44 = "\U0001f3f4\U000e006b\U000e0072\U000e0034\U000e0034\U000e007f" - FLAG_FOR_MOUNT_ATHOS_GR_69 = "\U0001f3f4\U000e0067\U000e0072\U000e0036\U000e0039\U000e007f" - WOMAN_IN_BUSINESS_SUIT_LEVITATING_MEDIUM_SKIN_TONE = "\U0001f574\U0001f3fd\u200d\u2640\ufe0f" - FLAG_FOR_RASON_KP_13 = "\U0001f3f4\U000e006b\U000e0070\U000e0031\U000e0033\U000e007f" - FLAG_FOR_INCHEON_KR_28 = "\U0001f3f4\U000e006b\U000e0072\U000e0032\U000e0038\U000e007f" - FLAG_FOR_KAMPONG_THOM_KH_6 = "\U0001f3f4\U000e006b\U000e0068\U000e0036\U000e007f" - FLAG_FOR_EAST_MACEDONIA_AND_THRACE_GR_A = "\U0001f3f4\U000e0067\U000e0072\U000e0061\U000e007f" - FLAG_FOR_AIN_DEFLA_DZ_44 = "\U0001f3f4\U000e0064\U000e007a\U000e0034\U000e0034\U000e007f" - FLAG_FOR_NORTH_PYONGAN_KP_03 = "\U0001f3f4\U000e006b\U000e0070\U000e0030\U000e0033\U000e007f" - FLAG_FOR_GANGWON_KR_42 = "\U0001f3f4\U000e006b\U000e0072\U000e0034\U000e0032\U000e007f" - FLAG_FOR_KAMPONG_SPEU_KH_5 = "\U0001f3f4\U000e006b\U000e0068\U000e0035\U000e007f" - FLAG_FOR_CENTRAL_OSTROBOTHNIA_FI_07 = "\U0001f3f4\U000e0066\U000e0069\U000e0030\U000e0037\U000e007f" - FLAG_FOR_SOUTH_HWANGHAE_KP_05 = "\U0001f3f4\U000e006b\U000e0070\U000e0030\U000e0035\U000e007f" - FLAG_FOR_ULSAN_KR_31 = "\U0001f3f4\U000e006b\U000e0072\U000e0033\U000e0031\U000e007f" - FLAG_FOR_NORTH_BANK_DIVISION_GM_N = "\U0001f3f4\U000e0067\U000e006d\U000e006e\U000e007f" - COUPLE_WITH_HEART_WOMAN_DARK_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fc" - FLAG_FOR_AL_FARWANIYAH_KW_FA = "\U0001f3f4\U000e006b\U000e0077\U000e0066\U000e0061\U000e007f" - FLAG_FOR_KARAGANDY_KZ_KAR = "\U0001f3f4\U000e006b\U000e007a\U000e006b\U000e0061\U000e0072\U000e007f" - FLAG_FOR_SERMERSOOQ_GL_SM = "\U0001f3f4\U000e0067\U000e006c\U000e0073\U000e006d\U000e007f" - FLAG_FOR_PHONGSALY_LA_PH = "\U0001f3f4\U000e006c\U000e0061\U000e0070\U000e0068\U000e007f" - FLAG_FOR_OUDOMXAY_LA_OU = "\U0001f3f4\U000e006c\U000e0061\U000e006f\U000e0075\U000e007f" - FLAG_FOR_ATYRAU_KZ_ATY = "\U0001f3f4\U000e006b\U000e007a\U000e0061\U000e0074\U000e0079\U000e007f" - FLAG_FOR_LUANG_NAMTHA_LA_LM = "\U0001f3f4\U000e006c\U000e0061\U000e006c\U000e006d\U000e007f" - FLAG_FOR_LUANG_PRABANG_LA_LP = "\U0001f3f4\U000e006c\U000e0061\U000e006c\U000e0070\U000e007f" - FAMILY_MAN_DARK_SKIN_TONE_WOMAN_DARK_SKIN_TONE_BABY_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f469\U0001f3ff\u200d\U0001f476\U0001f3ff\u200d\U0001f466\U0001f3ff" - FLAG_FOR_HONG_KONG_SAR_CHINA_CN_91 = "\U0001f3f4\U000e0063\U000e006e\U000e0039\U000e0031\U000e007f" - FLAG_FOR_VIENTIANE_LA_VT = "\U0001f3f4\U000e006c\U000e0061\U000e0076\U000e0074\U000e007f" - FLAG_FOR_NORTH_CHUNGCHEONG_KR_43 = "\U0001f3f4\U000e006b\U000e0072\U000e0034\U000e0033\U000e007f" - FLAG_FOR_JAMBYL_KZ_ZHA = "\U0001f3f4\U000e006b\U000e007a\U000e007a\U000e0068\U000e0061\U000e007f" - FLAG_FOR_QOM_IR_26 = "\U0001f3f4\U000e0069\U000e0072\U000e0032\U000e0036\U000e007f" - FLAG_FOR_MUBARAK_AL_KABEER_KW_MU = "\U0001f3f4\U000e006b\U000e0077\U000e006d\U000e0075\U000e007f" - FLAG_FOR_KYZYLORDA_KZ_KZY = "\U0001f3f4\U000e006b\U000e007a\U000e006b\U000e007a\U000e0079\U000e007f" - FLAG_FOR_AL_JAHRA_KW_JA = "\U0001f3f4\U000e006b\U000e0077\U000e006a\U000e0061\U000e007f" - FLAG_FOR_AL_ASIMAH_KW_KU = "\U0001f3f4\U000e006b\U000e0077\U000e006b\U000e0075\U000e007f" - FLAG_FOR_GYEONGGI_KR_41 = "\U0001f3f4\U000e006b\U000e0072\U000e0034\U000e0031\U000e007f" - FLAG_FOR_PAVLODAR_KZ_PAV = "\U0001f3f4\U000e006b\U000e007a\U000e0070\U000e0061\U000e0076\U000e007f" - FLAG_FOR_ANJOUAN_KM_A = "\U0001f3f4\U000e006b\U000e006d\U000e0061\U000e007f" - FLAG_FOR_CHAMPASAK_LA_CH = "\U0001f3f4\U000e006c\U000e0061\U000e0063\U000e0068\U000e007f" - FLAG_FOR_SEJONG_KR_50 = "\U0001f3f4\U000e006b\U000e0072\U000e0035\U000e0030\U000e007f" - FLAG_FOR_NORTH_KAZAKHSTAN_KZ_SEV = "\U0001f3f4\U000e006b\U000e007a\U000e0073\U000e0065\U000e0076\U000e007f" - FLAG_FOR_HANOVER_JM_09 = "\U0001f3f4\U000e006a\U000e006d\U000e0030\U000e0039\U000e007f" - FLAG_FOR_UPPER_RIVER_DIVISION_GM_U = "\U0001f3f4\U000e0067\U000e006d\U000e0075\U000e007f" - FLAG_FOR_MAUREN_LI_04 = "\U0001f3f4\U000e006c\U000e0069\U000e0030\U000e0034\U000e007f" - FLAG_FOR_BAALBEK_HERMEL_LB_BH = "\U0001f3f4\U000e006c\U000e0062\U000e0062\U000e0068\U000e007f" - FLAG_FOR_NORTH_CENTRAL_LK_7 = "\U0001f3f4\U000e006c\U000e006b\U000e0037\U000e007f" - FLAG_FOR_SOUFRIERE_LC_10 = "\U0001f3f4\U000e006c\U000e0063\U000e0031\U000e0030\U000e007f" - FLAG_FOR_NORTHERN_LK_4 = "\U0001f3f4\U000e006c\U000e006b\U000e0034\U000e007f" - FLAG_FOR_TRIESEN_LI_09 = "\U0001f3f4\U000e006c\U000e0069\U000e0030\U000e0039\U000e007f" - FLAG_FOR_BJELOVAR_BILOGORA_HR_07 = "\U0001f3f4\U000e0068\U000e0072\U000e0030\U000e0037\U000e007f" - FLAG_FOR_SAINYABULI_LA_XA = "\U0001f3f4\U000e006c\U000e0061\U000e0078\U000e0061\U000e007f" - FLAG_FOR_SOUTHERN_SAVONIA_FI_04 = "\U0001f3f4\U000e0066\U000e0069\U000e0030\U000e0034\U000e007f" - FLAG_FOR_UVA_LK_8 = "\U0001f3f4\U000e006c\U000e006b\U000e0038\U000e007f" - FLAG_FOR_GAMPRIN_LI_03 = "\U0001f3f4\U000e006c\U000e0069\U000e0030\U000e0033\U000e007f" - FLAG_FOR_ESCHEN_LI_02 = "\U0001f3f4\U000e006c\U000e0069\U000e0030\U000e0032\U000e007f" - FLAG_FOR_SOUTH_LB_JA = "\U0001f3f4\U000e006c\U000e0062\U000e006a\U000e0061\U000e007f" - FLAG_FOR_NORTH_LB_AS = "\U0001f3f4\U000e006c\U000e0062\U000e0061\U000e0073\U000e007f" - FLAG_FOR_RUGGELL_LI_06 = "\U0001f3f4\U000e006c\U000e0069\U000e0030\U000e0036\U000e007f" - FLAG_FOR_NORTH_WESTERN_LK_6 = "\U0001f3f4\U000e006c\U000e006b\U000e0036\U000e007f" - FLAG_FOR_GROS_ISLET_LC_06 = "\U0001f3f4\U000e006c\U000e0063\U000e0030\U000e0036\U000e007f" - FLAG_FOR_NABATIEH_LB_NA = "\U0001f3f4\U000e006c\U000e0062\U000e006e\U000e0061\U000e007f" - FLAG_FOR_GRANDE_COMORE_KM_G = "\U0001f3f4\U000e006b\U000e006d\U000e0067\U000e007f" - FLAG_FOR_CASTRIES_LC_02 = "\U0001f3f4\U000e006c\U000e0063\U000e0030\U000e0032\U000e007f" - FLAG_FOR_VADUZ_LI_11 = "\U0001f3f4\U000e006c\U000e0069\U000e0031\U000e0031\U000e007f" - FLAG_FOR_KOSTANAY_KZ_KUS = "\U0001f3f4\U000e006b\U000e007a\U000e006b\U000e0075\U000e0073\U000e007f" - FLAG_FOR_CHOISEUL_LC_03 = "\U0001f3f4\U000e006c\U000e0063\U000e0030\U000e0033\U000e007f" - FLAG_FOR_CENTRAL_LK_2 = "\U0001f3f4\U000e006c\U000e006b\U000e0032\U000e007f" - FLAG_FOR_DENNERY_LC_05 = "\U0001f3f4\U000e006c\U000e0063\U000e0030\U000e0035\U000e007f" - FLAG_FOR_GARISSA_KE_07 = "\U0001f3f4\U000e006b\U000e0065\U000e0030\U000e0037\U000e007f" - FLAG_FOR_ANSE_LA_RAYE_LC_01 = "\U0001f3f4\U000e006c\U000e0063\U000e0030\U000e0031\U000e007f" - FLAG_FOR_RYANGGANG_KP_10 = "\U0001f3f4\U000e006b\U000e0070\U000e0031\U000e0030\U000e007f" - FLAG_FOR_JONAVA_LT_10 = "\U0001f3f4\U000e006c\U000e0074\U000e0031\U000e0030\U000e007f" - FLAG_FOR_MOHALE_S_HOEK_LS_F = "\U0001f3f4\U000e006c\U000e0073\U000e0066\U000e007f" - FLAG_FOR_KALVARIJA_LT_14 = "\U0001f3f4\U000e006c\U000e0074\U000e0031\U000e0034\U000e007f" - FLAG_FOR_GBARPOLU_LR_GP = "\U0001f3f4\U000e006c\U000e0072\U000e0067\U000e0070\U000e007f" - TAG_LATIN_CAPITAL_LETTER_F = "\U000e0046" - FLAG_FOR_NORTH_KIVU_CD_NK = "\U0001f3f4\U000e0063\U000e0064\U000e006e\U000e006b\U000e007f" - FLAG_FOR_IGNALINA_LT_09 = "\U0001f3f4\U000e006c\U000e0074\U000e0030\U000e0039\U000e007f" - FLAG_FOR_MAFETENG_LS_E = "\U0001f3f4\U000e006c\U000e0073\U000e0065\U000e007f" - UNIVERSAL_RECYCLING_SYMBOL = "\u2672" - FLAG_FOR_BIHAR_IN_BR = "\U0001f3f4\U000e0069\U000e006e\U000e0062\U000e0072\U000e007f" - FLAG_FOR_THABA_TSEKA_LS_K = "\U0001f3f4\U000e006c\U000e0073\U000e006b\U000e007f" - FLAG_FOR_ALYTUS_LT_03 = "\U0001f3f4\U000e006c\U000e0074\U000e0030\U000e0033\U000e007f" - FLAG_FOR_DRUSKININKAI_LT_07 = "\U0001f3f4\U000e006c\U000e0074\U000e0030\U000e0037\U000e007f" - FLAG_FOR_MOKHOTLONG_LS_J = "\U0001f3f4\U000e006c\U000e0073\U000e006a\U000e007f" - FLAG_FOR_KAUNO_MUNICIPALITY_LT_15 = "\U0001f3f4\U000e006c\U000e0074\U000e0031\U000e0035\U000e007f" - FLAG_FOR_JONISKIS_LT_11 = "\U0001f3f4\U000e006c\U000e0074\U000e0031\U000e0031\U000e007f" - FLAG_FOR_QACHA_S_NEK_LS_H = "\U0001f3f4\U000e006c\U000e0073\U000e0068\U000e007f" - FLAG_FOR_BEREA_LS_D = "\U0001f3f4\U000e006c\U000e0073\U000e0064\U000e007f" - FLAG_FOR_NIMBA_LR_NI = "\U0001f3f4\U000e006c\U000e0072\U000e006e\U000e0069\U000e007f" - FLAG_FOR_ELEKTRENAI_LT_08 = "\U0001f3f4\U000e006c\U000e0074\U000e0030\U000e0038\U000e007f" - FLAG_FOR_ERBIL_IQ_AR = "\U0001f3f4\U000e0069\U000e0071\U000e0061\U000e0072\U000e007f" - FLAG_FOR_KANGWON_KP_07 = "\U0001f3f4\U000e006b\U000e0070\U000e0030\U000e0037\U000e007f" - FLAG_FOR_KAISIADORYS_LT_13 = "\U0001f3f4\U000e006c\U000e0074\U000e0031\U000e0033\U000e007f" - FLAG_FOR_BUTHA_BUTHE_LS_B = "\U0001f3f4\U000e006c\U000e0073\U000e0062\U000e007f" - FLAG_FOR_BIRSTONAS_LT_05 = "\U0001f3f4\U000e006c\U000e0074\U000e0030\U000e0035\U000e007f" - FLAG_FOR_KIRINYAGA_KE_15 = "\U0001f3f4\U000e006b\U000e0065\U000e0031\U000e0035\U000e007f" - FLAG_FOR_ANYKSCIAI_LT_04 = "\U0001f3f4\U000e006c\U000e0074\U000e0030\U000e0034\U000e007f" - FLAG_FOR_JURBARKAS_LT_12 = "\U0001f3f4\U000e006c\U000e0074\U000e0031\U000e0032\U000e007f" - FLAG_FOR_UTENA_LT_54 = "\U0001f3f4\U000e006c\U000e0074\U000e0035\U000e0034\U000e007f" - FLAG_FOR_PASVALYS_LT_34 = "\U0001f3f4\U000e006c\U000e0074\U000e0033\U000e0034\U000e007f" - FLAG_FOR_PRIENAI_LT_36 = "\U0001f3f4\U000e006c\U000e0074\U000e0033\U000e0036\U000e007f" - FLAG_FOR_KUPISKIS_LT_23 = "\U0001f3f4\U000e006c\U000e0074\U000e0032\U000e0033\U000e007f" - FLAG_FOR_ROKISKIS_LT_40 = "\U0001f3f4\U000e006c\U000e0074\U000e0034\U000e0030\U000e007f" - FLAG_FOR_SIAULIU_MUNICIPALITY_LT_43 = "\U0001f3f4\U000e006c\U000e0074\U000e0034\U000e0033\U000e007f" - FLAG_FOR_KAZLU_RUDA_LT_17 = "\U0001f3f4\U000e006c\U000e0074\U000e0031\U000e0037\U000e007f" - FLAG_FOR_TELSIAI_LT_51 = "\U0001f3f4\U000e006c\U000e0074\U000e0035\U000e0031\U000e007f" - FLAG_FOR_KLAIPEDA_LT_21 = "\U0001f3f4\U000e006c\U000e0074\U000e0032\U000e0031\U000e007f" - FLAG_FOR_KEDAINIAI_LT_18 = "\U0001f3f4\U000e006c\U000e0074\U000e0031\U000e0038\U000e007f" - FLAG_FOR_SIAULIAI_LT_44 = "\U0001f3f4\U000e006c\U000e0074\U000e0034\U000e0034\U000e007f" - FLAG_FOR_PALANGA_LT_31 = "\U0001f3f4\U000e006c\U000e0074\U000e0033\U000e0031\U000e007f" - FLAG_FOR_PANEVEZYS_LT_33 = "\U0001f3f4\U000e006c\U000e0074\U000e0033\U000e0033\U000e007f" - FLAG_FOR_SILALE_LT_45 = "\U0001f3f4\U000e006c\U000e0074\U000e0034\U000e0035\U000e007f" - FLAG_FOR_SAKIAI_LT_41 = "\U0001f3f4\U000e006c\U000e0074\U000e0034\U000e0031\U000e007f" - FLAG_FOR_TAURAGE_LT_50 = "\U0001f3f4\U000e006c\U000e0074\U000e0035\U000e0030\U000e007f" - FLAG_FOR_MOLETAI_LT_27 = "\U0001f3f4\U000e006c\U000e0074\U000e0032\U000e0037\U000e007f" - FLAG_FOR_SKUODAS_LT_48 = "\U0001f3f4\U000e006c\U000e0074\U000e0034\U000e0038\U000e007f" - FLAG_FOR_UKMERGE_LT_53 = "\U0001f3f4\U000e006c\U000e0074\U000e0035\U000e0033\U000e007f" - FLAG_FOR_VILKAVISKIS_LT_56 = "\U0001f3f4\U000e006c\U000e0074\U000e0035\U000e0036\U000e007f" - FLAG_FOR_PAGEGIAI_LT_29 = "\U0001f3f4\U000e006c\U000e0074\U000e0032\U000e0039\U000e007f" - FLAG_FOR_RIETAVAS_LT_39 = "\U0001f3f4\U000e006c\U000e0074\U000e0033\U000e0039\U000e007f" - FLAG_FOR_SILUTE_LT_46 = "\U0001f3f4\U000e006c\U000e0074\U000e0034\U000e0036\U000e007f" - FLAG_FOR_SVENCIONYS_LT_49 = "\U0001f3f4\U000e006c\U000e0074\U000e0034\U000e0039\U000e007f" - FLAG_FOR_GRAND_BASSA_LR_GB = "\U0001f3f4\U000e006c\U000e0072\U000e0067\U000e0062\U000e007f" - FLAG_FOR_TRAKAI_LT_52 = "\U0001f3f4\U000e006c\U000e0074\U000e0035\U000e0032\U000e007f" - FLAG_FOR_LAZDIJAI_LT_24 = "\U0001f3f4\U000e006c\U000e0074\U000e0032\U000e0034\U000e007f" - FLAG_FOR_MARIJAMPOLE_LT_25 = "\U0001f3f4\U000e006c\U000e0074\U000e0032\U000e0035\U000e007f" - FLAG_FOR_VARENA_LT_55 = "\U0001f3f4\U000e006c\U000e0074\U000e0035\U000e0035\U000e007f" - FLAG_FOR_MAZEIKIAI_LT_26 = "\U0001f3f4\U000e006c\U000e0074\U000e0032\U000e0036\U000e007f" - FLAG_FOR_PLUNGE_LT_35 = "\U0001f3f4\U000e006c\U000e0074\U000e0033\U000e0035\U000e007f" - FLAG_FOR_RADVILISKIS_LT_37 = "\U0001f3f4\U000e006c\U000e0074\U000e0033\U000e0037\U000e007f" - FLAG_FOR_KRETINGA_LT_22 = "\U0001f3f4\U000e006c\U000e0074\U000e0032\U000e0032\U000e007f" - FLAG_FOR_KELME_LT_19 = "\U0001f3f4\U000e006c\U000e0074\U000e0031\U000e0039\U000e007f" - FLAG_FOR_SIRVINTOS_LT_47 = "\U0001f3f4\U000e006c\U000e0074\U000e0034\U000e0037\U000e007f" - FLAG_FOR_NERINGA_LT_28 = "\U0001f3f4\U000e006c\U000e0074\U000e0032\U000e0038\U000e007f" - FLAG_FOR_AIZPUTE_LV_003 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0030\U000e0033\U000e007f" - FLAG_FOR_GREVENMACHER_LU_GR = "\U0001f3f4\U000e006c\U000e0075\U000e0067\U000e0072\U000e007f" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_MAN_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f468\U0001f3fd\u200d\U0001f476\U0001f3fd" - FLAG_FOR_BALDONE_LV_013 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0031\U000e0033\U000e007f" - FLAG_FOR_AKNISTE_LV_004 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0030\U000e0034\U000e007f" - FLAG_FOR_UTENA_COUNTY_LT_UT = "\U0001f3f4\U000e006c\U000e0074\U000e0075\U000e0074\U000e007f" - FLAG_FOR_ALYTUS_COUNTY_LT_AL = "\U0001f3f4\U000e006c\U000e0074\U000e0061\U000e006c\U000e007f" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f467\U0001f3fc\u200d\U0001f467\U0001f3fc" - FLAG_FOR_VISAGINAS_LT_59 = "\U0001f3f4\U000e006c\U000e0074\U000e0035\U000e0039\U000e007f" - KISS_WOMAN_MEDIUM_LIGHT_SKIN_TONE_MAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fc" - FLAG_FOR_KLAIPEDA_COUNTY_LT_KL = "\U0001f3f4\U000e006c\U000e0074\U000e006b\U000e006c\U000e007f" - FLAG_FOR_REMICH_LU_RM = "\U0001f3f4\U000e006c\U000e0075\U000e0072\U000e006d\U000e007f" - FLAG_FOR_RATANAKIRI_KH_16 = "\U0001f3f4\U000e006b\U000e0068\U000e0031\U000e0036\U000e007f" - FLAG_FOR_AMATA_LV_008 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0030\U000e0038\U000e007f" - FLAG_FOR_RASEINIAI_LT_38 = "\U0001f3f4\U000e006c\U000e0074\U000e0033\U000e0038\U000e007f" - FLAG_FOR_MERSCH_LU_ME = "\U0001f3f4\U000e006c\U000e0075\U000e006d\U000e0065\U000e007f" - FLAG_FOR_VIANDEN_LU_VD = "\U0001f3f4\U000e006c\U000e0075\U000e0076\U000e0064\U000e007f" - FLAG_FOR_CLERVAUX_LU_CL = "\U0001f3f4\U000e006c\U000e0075\U000e0063\U000e006c\U000e007f" - FLAG_FOR_VILNIUS_COUNTY_LT_VL = "\U0001f3f4\U000e006c\U000e0074\U000e0076\U000e006c\U000e007f" - FLAG_FOR_ECHTERNACH_LU_EC = "\U0001f3f4\U000e006c\U000e0075\U000e0065\U000e0063\U000e007f" - FLAG_FOR_DIEKIRCH_LU_DI = "\U0001f3f4\U000e006c\U000e0075\U000e0064\U000e0069\U000e007f" - FLAG_FOR_SIAULIAI_COUNTY_LT_SA = "\U0001f3f4\U000e006c\U000e0074\U000e0073\U000e0061\U000e007f" - FLAG_FOR_TAURAGE_COUNTY_LT_TA = "\U0001f3f4\U000e006c\U000e0074\U000e0074\U000e0061\U000e007f" - FLAG_FOR_MARIJAMPOLE_COUNTY_LT_MR = "\U0001f3f4\U000e006c\U000e0074\U000e006d\U000e0072\U000e007f" - FLAG_FOR_ADAZI_LV_011 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0031\U000e0031\U000e007f" - FLAG_FOR_ALUKSNE_LV_007 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0030\U000e0037\U000e007f" - FLAG_FOR_KULDIGA_LV_050 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0035\U000e0030\U000e007f" - FLAG_FOR_BALVI_LV_015 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0031\U000e0035\U000e007f" - FLAG_FOR_LABORIE_LC_07 = "\U0001f3f4\U000e006c\U000e0063\U000e0030\U000e0037\U000e007f" - FLAG_FOR_KARSAVA_LV_044 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0034\U000e0034\U000e007f" - FLAG_FOR_KEGUMS_LV_051 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0035\U000e0031\U000e007f" - FLAG_FOR_BALTINAVA_LV_014 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0031\U000e0034\U000e007f" - FLAG_FOR_KRUSTPILS_LV_049 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0034\U000e0039\U000e007f" - FLAG_FOR_GROBINA_LV_032 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0033\U000e0032\U000e007f" - FLAG_FOR_BAUSKA_LV_016 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0031\U000e0036\U000e007f" - FLAG_FOR_DOBELE_LV_026 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0032\U000e0036\U000e007f" - FLAG_FOR_BROCENI_LV_018 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0031\U000e0038\U000e007f" - FLAG_FOR_IECAVA_LV_034 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0033\U000e0034\U000e007f" - FLAG_FOR_CESVAINE_LV_021 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0032\U000e0031\U000e007f" - FLAG_FOR_AJLOUN_JO_AJ = "\U0001f3f4\U000e006a\U000e006f\U000e0061\U000e006a\U000e007f" - FLAG_FOR_JAUNJELGAVA_LV_038 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0033\U000e0038\U000e007f" - FLAG_FOR_KOCENI_LV_045 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0034\U000e0035\U000e007f" - FLAG_FOR_GARKALNE_LV_031 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0033\U000e0031\U000e007f" - FLAG_FOR_AIZKRAUKLE_LV_002 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0030\U000e0032\U000e007f" - FLAG_FOR_CIBLA_LV_023 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0032\U000e0033\U000e007f" - FLAG_FOR_JAUNPIEBALGA_LV_039 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0033\U000e0039\U000e007f" - FLAG_FOR_DUNDAGA_LV_027 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0032\U000e0037\U000e007f" - FLAG_FOR_CESIS_LV_022 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0032\U000e0032\U000e007f" - FLAG_FOR_KRIMULDA_LV_048 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0034\U000e0038\U000e007f" - FLAG_FOR_CARNIKAVA_LV_020 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0032\U000e0030\U000e007f" - FLAG_FOR_IKSKILE_LV_035 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0033\U000e0035\U000e007f" - FLAG_FOR_GULBENE_LV_033 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0033\U000e0033\U000e007f" - FLAG_FOR_KRASLAVA_LV_047 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0034\U000e0037\U000e007f" - FLAG_FOR_JAUNPILS_LV_040 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0034\U000e0030\U000e007f" - FLAG_FOR_BURTNIEKI_LV_019 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0031\U000e0039\U000e007f" - FLAG_FOR_BEVERINA_LV_017 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0031\U000e0037\U000e007f" - FLAG_FOR_KANDAVA_LV_043 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0034\U000e0033\U000e007f" - FLAG_FOR_LIGATNE_LV_055 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0035\U000e0035\U000e007f" - FLAG_FOR_SAULKRASTI_LV_089 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0038\U000e0039\U000e007f" - FLAG_FOR_SALASPILS_LV_087 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0038\U000e0037\U000e007f" - FLAG_FOR_PRIEKULI_LV_075 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0037\U000e0035\U000e007f" - FLAG_FOR_RUCAVA_LV_081 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0038\U000e0031\U000e007f" - FLAG_FOR_NAUKSENI_LV_064 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0036\U000e0034\U000e007f" - FLAG_FOR_PRIEKULE_LV_074 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0037\U000e0034\U000e007f" - FLAG_FOR_SEJA_LV_090 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0039\U000e0030\U000e007f" - FLAG_FOR_NERETA_LV_065 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0036\U000e0035\U000e007f" - FLAG_FOR_PARGAUJA_LV_070 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0037\U000e0030\U000e007f" - FLAG_FOR_MARUPE_LV_062 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0036\U000e0032\U000e007f" - FLAG_FOR_PAVILOSTA_LV_071 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0037\U000e0031\U000e007f" - FLAG_FOR_OLAINE_LV_068 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0036\U000e0038\U000e007f" - FLAG_FOR_LIMBAZI_LV_054 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0035\U000e0034\U000e007f" - FLAG_FOR_RUJIENA_LV_084 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0038\U000e0034\U000e007f" - FLAG_FOR_PLAVINAS_LV_072 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0037\U000e0032\U000e007f" - FLAG_FOR_RUNDALE_LV_083 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0038\U000e0033\U000e007f" - FLAG_FOR_PREILI_LV_073 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0037\U000e0033\U000e007f" - FLAG_FOR_RUGAJI_LV_082 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0038\U000e0032\U000e007f" - FLAG_FOR_MERSRAGS_LV_063 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0036\U000e0033\U000e007f" - FLAG_FOR_SALDUS_LV_088 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0038\U000e0038\U000e007f" - FLAG_FOR_LUDZA_LV_058 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0035\U000e0038\U000e007f" - FLAG_FOR_LIVANI_LV_056 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0035\U000e0036\U000e007f" - FLAG_FOR_ROPAZI_LV_080 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0038\U000e0030\U000e007f" - FLAG_FOR_KOH_KONG_KH_9 = "\U0001f3f4\U000e006b\U000e0068\U000e0039\U000e007f" - FLAG_FOR_RIEBINI_LV_078 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0037\U000e0038\U000e007f" - FLAG_FOR_LIELVARDE_LV_053 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0035\U000e0033\U000e007f" - FLAG_FOR_KEKAVA_LV_052 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0035\U000e0032\U000e007f" - FLAG_FOR_NICA_LV_066 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0036\U000e0036\U000e007f" - FLAG_FOR_LUBANA_LV_057 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0035\U000e0037\U000e007f" - FLAG_FOR_GRAND_EST_FR_GES = "\U0001f3f4\U000e0066\U000e0072\U000e0067\U000e0065\U000e0073\U000e007f" - FLAG_FOR_RAUNA_LV_076 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0037\U000e0036\U000e007f" - FLAG_FOR_ROJA_LV_079 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0037\U000e0039\U000e007f" - FLAG_FOR_MALPILS_LV_061 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0036\U000e0031\U000e007f" - FLAG_FOR_SMILTENE_LV_094 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0039\U000e0034\U000e007f" - FLAG_FOR_VARAKLANI_LV_102 = "\U0001f3f4\U000e006c\U000e0076\U000e0031\U000e0030\U000e0032\U000e007f" - FLAG_FOR_JABAL_AL_GHARBI_LY_JG = "\U0001f3f4\U000e006c\U000e0079\U000e006a\U000e0067\U000e007f" - FLAG_FOR_VARKAVA_LV_103 = "\U0001f3f4\U000e006c\U000e0076\U000e0031\U000e0030\U000e0033\U000e007f" - FLAG_FOR_VIESITE_LV_107 = "\U0001f3f4\U000e006c\U000e0076\U000e0031\U000e0030\U000e0037\U000e007f" - FLAG_FOR_DAUGAVPILS_LV_DGV = "\U0001f3f4\U000e006c\U000e0076\U000e0064\U000e0067\U000e0076\U000e007f" - FLAG_FOR_KUFRA_LY_KF = "\U0001f3f4\U000e006c\U000e0079\U000e006b\U000e0066\U000e007f" - FLAG_FOR_VILANI_LV_109 = "\U0001f3f4\U000e006c\U000e0076\U000e0031\U000e0030\U000e0039\U000e007f" - FLAG_FOR_JURMALA_LV_JUR = "\U0001f3f4\U000e006c\U000e0076\U000e006a\U000e0075\U000e0072\U000e007f" - FLAG_FOR_VILAKA_LV_108 = "\U0001f3f4\U000e006c\U000e0076\U000e0031\U000e0030\U000e0038\U000e007f" - FLAG_FOR_VENTSPILS_LV_VEN = "\U0001f3f4\U000e006c\U000e0076\U000e0076\U000e0065\U000e006e\U000e007f" - FLAG_FOR_MURQUB_LY_MB = "\U0001f3f4\U000e006c\U000e0079\U000e006d\U000e0062\U000e007f" - FLAG_FOR_VECPIEBALGA_LV_104 = "\U0001f3f4\U000e006c\U000e0076\U000e0031\U000e0030\U000e0034\U000e007f" - FLAG_FOR_TUKUMS_LV_099 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0039\U000e0039\U000e007f" - FLAG_FOR_BENGHAZI_LY_BA = "\U0001f3f4\U000e006c\U000e0079\U000e0062\U000e0061\U000e007f" - FLAG_FOR_LIEPAJA_LV_LPX = "\U0001f3f4\U000e006c\U000e0076\U000e006c\U000e0070\U000e0078\U000e007f" - FLAG_FOR_JABAL_AL_AKHDAR_LY_JA = "\U0001f3f4\U000e006c\U000e0079\U000e006a\U000e0061\U000e007f" - FLAG_FOR_ZARASAI_LT_60 = "\U0001f3f4\U000e006c\U000e0074\U000e0036\U000e0030\U000e007f" - FLAG_FOR_VECUMNIEKI_LV_105 = "\U0001f3f4\U000e006c\U000e0076\U000e0031\U000e0030\U000e0035\U000e007f" - FLAG_FOR_GHAT_LY_GT = "\U0001f3f4\U000e006c\U000e0079\U000e0067\U000e0074\U000e007f" - FLAG_FOR_TALSI_LV_097 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0039\U000e0037\U000e007f" - FLAG_FOR_JELGAVA_LV_JEL = "\U0001f3f4\U000e006c\U000e0076\U000e006a\U000e0065\U000e006c\U000e007f" - FLAG_FOR_STRENCI_LV_096 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0039\U000e0036\U000e007f" - FLAG_FOR_NORTH_HAMGYONG_KP_09 = "\U0001f3f4\U000e006b\U000e0070\U000e0030\U000e0039\U000e007f" - FLAG_FOR_VALMIERA_LV_VMR = "\U0001f3f4\U000e006c\U000e0076\U000e0076\U000e006d\U000e0072\U000e007f" - FLAG_FOR_TERVETE_LV_098 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0039\U000e0038\U000e007f" - FLAG_FOR_REZEKNE_LV_REZ = "\U0001f3f4\U000e006c\U000e0076\U000e0072\U000e0065\U000e007a\U000e007f" - FLAG_FOR_ZILUPE_LV_110 = "\U0001f3f4\U000e006c\U000e0076\U000e0031\U000e0031\U000e0030\U000e007f" - FLAG_FOR_JEKABPILS_LV_JKB = "\U0001f3f4\U000e006c\U000e0076\U000e006a\U000e006b\U000e0062\U000e007f" - FLAG_FOR_SIGULDA_LV_091 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0039\U000e0031\U000e007f" - FLAG_FOR_STOPINI_LV_095 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0039\U000e0035\U000e007f" - FLAG_FOR_ENGURE_LV_029 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0032\U000e0039\U000e007f" - FLAG_FOR_WADI_AL_HAYAA_LY_WD = "\U0001f3f4\U000e006c\U000e0079\U000e0077\U000e0064\U000e007f" - FLAG_FOR_AL_WAHAT_LY_WA = "\U0001f3f4\U000e006c\U000e0079\U000e0077\U000e0061\U000e007f" - FLAG_FOR_LABE_REGION_GN_L = "\U0001f3f4\U000e0067\U000e006e\U000e006c\U000e007f" - FLAG_FOR_NALUT_LY_NL = "\U0001f3f4\U000e006c\U000e0079\U000e006e\U000e006c\U000e007f" - FLAG_FOR_MONEGHETTI_MC_MG = "\U0001f3f4\U000e006d\U000e0063\U000e006d\U000e0067\U000e007f" - FLAG_FOR_MARRAKESH_TENSIFT_EL_HAOUZ_MA_11 = "\U0001f3f4\U000e006d\U000e0061\U000e0031\U000e0031\U000e007f" - FLAG_FOR_LA_COLLE_MC_CL = "\U0001f3f4\U000e006d\U000e0063\U000e0063\U000e006c\U000e007f" - FLAG_FOR_ZAWIYA_LY_ZA = "\U0001f3f4\U000e006c\U000e0079\U000e007a\U000e0061\U000e007f" - FLAG_FOR_EL_TARF_DZ_36 = "\U0001f3f4\U000e0064\U000e007a\U000e0033\U000e0036\U000e007f" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f469\U0001f3fc\u200d\U0001f466\U0001f3fc\u200d\U0001f476\U0001f3fc" - FLAG_FOR_MURZUQ_LY_MQ = "\U0001f3f4\U000e006c\U000e0079\U000e006d\U000e0071\U000e007f" - FLAG_FOR_CHAOUIA_OUARDIGHA_MA_09 = "\U0001f3f4\U000e006d\U000e0061\U000e0030\U000e0039\U000e007f" - FLAG_FOR_SIRTE_LY_SR = "\U0001f3f4\U000e006c\U000e0079\U000e0073\U000e0072\U000e007f" - FLAG_FOR_NUQAT_AL_KHAMS_LY_NQ = "\U0001f3f4\U000e006c\U000e0079\U000e006e\U000e0071\U000e007f" - FLAG_FOR_LA_CONDAMINE_MC_CO = "\U0001f3f4\U000e006d\U000e0063\U000e0063\U000e006f\U000e007f" - FLAG_FOR_MEKNES_TAFILALET_MA_06 = "\U0001f3f4\U000e006d\U000e0061\U000e0030\U000e0036\U000e007f" - FLAG_FOR_LA_GARE_MC_GA = "\U0001f3f4\U000e006d\U000e0063\U000e0067\U000e0061\U000e007f" - FLAG_FOR_GUELMIM_ES_SEMARA_MA_14 = "\U0001f3f4\U000e006d\U000e0061\U000e0031\U000e0034\U000e007f" - COUPLE_WITH_HEART_WOMAN_LIGHT_SKIN_TONE_MAN_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fb" - FLAG_FOR_GHARB_CHRARDA_BENI_HSSEN_MA_02 = "\U0001f3f4\U000e006d\U000e0061\U000e0030\U000e0032\U000e007f" - FLAG_FOR_FONTVIEILLE_MC_FO = "\U0001f3f4\U000e006d\U000e0063\U000e0066\U000e006f\U000e007f" - FLAG_FOR_TADLA_AZILAL_MA_12 = "\U0001f3f4\U000e006d\U000e0061\U000e0031\U000e0032\U000e007f" - FAMILY_MAN_DARK_SKIN_TONE_BOY_DARK_SKIN_TONE_GIRL_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f466\U0001f3ff\u200d\U0001f467\U0001f3ff" - FLAG_FOR_RABAT_SALE_ZEMMOUR_ZAER_MA_07 = "\U0001f3f4\U000e006d\U000e0061\U000e0030\U000e0037\U000e007f" - FLAG_FOR_MALBOUSQUET_MC_MA = "\U0001f3f4\U000e006d\U000e0063\U000e006d\U000e0061\U000e007f" - FLAG_FOR_OUED_ED_DAHAB_LAGOUIRA_MA_16 = "\U0001f3f4\U000e006d\U000e0061\U000e0031\U000e0036\U000e007f" - FLAG_FOR_CALARASI_MD_CL = "\U0001f3f4\U000e006d\U000e0064\U000e0063\U000e006c\U000e007f" - FLAG_FOR_SOLDANESTI_MD_SD = "\U0001f3f4\U000e006d\U000e0064\U000e0073\U000e0064\U000e007f" - FLAG_FOR_IALOVENI_MD_IA = "\U0001f3f4\U000e006d\U000e0064\U000e0069\U000e0061\U000e007f" - FLAG_FOR_LEOVA_MD_LE = "\U0001f3f4\U000e006d\U000e0064\U000e006c\U000e0065\U000e007f" - FLAG_FOR_BALTI_MD_BA = "\U0001f3f4\U000e006d\U000e0064\U000e0062\U000e0061\U000e007f" - FLAG_FOR_CRIULENI_MD_CR = "\U0001f3f4\U000e006d\U000e0064\U000e0063\U000e0072\U000e007f" - FLAG_FOR_CENTRE_VAL_DE_LOIRE_FR_CVL = "\U0001f3f4\U000e0066\U000e0072\U000e0063\U000e0076\U000e006c\U000e007f" - FLAG_FOR_CANTEMIR_MD_CT = "\U0001f3f4\U000e006d\U000e0064\U000e0063\U000e0074\U000e007f" - FLAG_FOR_CHISINAU_MD_CU = "\U0001f3f4\U000e006d\U000e0064\U000e0063\U000e0075\U000e007f" - FLAG_FOR_FALESTI_MD_FA = "\U0001f3f4\U000e006d\U000e0064\U000e0066\U000e0061\U000e007f" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE_GIRL_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f469\U0001f3fc\u200d\U0001f467\U0001f3fc\u200d\U0001f476\U0001f3fc" - FLAG_FOR_DONDUSENI_MD_DO = "\U0001f3f4\U000e006d\U000e0064\U000e0064\U000e006f\U000e007f" - FLAG_FOR_MELILLA_ES_ML = "\U0001f3f4\U000e0065\U000e0073\U000e006d\U000e006c\U000e007f" - FLAG_FOR_OCNITA_MD_OC = "\U0001f3f4\U000e006d\U000e0064\U000e006f\U000e0063\U000e007f" - FLAG_FOR_SOROCA_MD_SO = "\U0001f3f4\U000e006d\U000e0064\U000e0073\U000e006f\U000e007f" - FLAG_FOR_DUBASARI_MD_DU = "\U0001f3f4\U000e006d\U000e0064\U000e0064\U000e0075\U000e007f" - FLAG_FOR_SINGEREI_MD_SI = "\U0001f3f4\U000e006d\U000e0064\U000e0073\U000e0069\U000e007f" - FLAG_FOR_EDINET_MD_ED = "\U0001f3f4\U000e006d\U000e0064\U000e0065\U000e0064\U000e007f" - FLAG_FOR_SABHA_LY_SB = "\U0001f3f4\U000e006c\U000e0079\U000e0073\U000e0062\U000e007f" - FLAG_FOR_NISPORENI_MD_NI = "\U0001f3f4\U000e006d\U000e0064\U000e006e\U000e0069\U000e007f" - FLAG_FOR_BRICENI_MD_BR = "\U0001f3f4\U000e006d\U000e0064\U000e0062\U000e0072\U000e007f" - FAMILY_WOMAN_MEDIUM_SKIN_TONE_BABY_MEDIUM_SKIN_TONE_BOY_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f476\U0001f3fd\u200d\U0001f466\U0001f3fd" - FLAG_FOR_ORHEI_MD_OR = "\U0001f3f4\U000e006d\U000e0064\U000e006f\U000e0072\U000e007f" - FLAG_FOR_CIMISLIA_MD_CM = "\U0001f3f4\U000e006d\U000e0064\U000e0063\U000e006d\U000e007f" - FLAG_FOR_GLODENI_MD_GL = "\U0001f3f4\U000e006d\U000e0064\U000e0067\U000e006c\U000e007f" - FLAG_FOR_RISCANI_MD_RI = "\U0001f3f4\U000e006d\U000e0064\U000e0072\U000e0069\U000e007f" - FLAG_FOR_BENDER_MD_BD = "\U0001f3f4\U000e006d\U000e0064\U000e0062\U000e0064\U000e007f" - FLAG_FOR_BASARABEASCA_MD_BS = "\U0001f3f4\U000e006d\U000e0064\U000e0062\U000e0073\U000e007f" - FLAG_FOR_JARDIN_EXOTIQUE_DE_MONACO_MC_JE = "\U0001f3f4\U000e006d\U000e0063\U000e006a\U000e0065\U000e007f" - FLAG_FOR_ERGLI_LV_030 = "\U0001f3f4\U000e006c\U000e0076\U000e0030\U000e0033\U000e0030\U000e007f" - FLAG_FOR_CAHUL_MD_CA = "\U0001f3f4\U000e006d\U000e0064\U000e0063\U000e0061\U000e007f" - FLAG_FOR_FLORESTI_MD_FL = "\U0001f3f4\U000e006d\U000e0064\U000e0066\U000e006c\U000e007f" - FLAG_FOR_HINCESTI_MD_HI = "\U0001f3f4\U000e006d\U000e0064\U000e0068\U000e0069\U000e007f" - FLAG_FOR_TOAMASINA_MG_A = "\U0001f3f4\U000e006d\U000e0067\U000e0061\U000e007f" - TAG_APOSTROPHE = "\U000e0027" - FLAG_FOR_TRIPOLI_LY_TB = "\U0001f3f4\U000e006c\U000e0079\U000e0074\U000e0062\U000e007f" - FLAG_FOR_BOGOVINJE_MK_06 = "\U0001f3f4\U000e006d\U000e006b\U000e0030\U000e0036\U000e007f" - FLAG_FOR_MOJKOVAC_ME_11 = "\U0001f3f4\U000e006d\U000e0065\U000e0031\U000e0031\U000e007f" - FLAG_FOR_PLUZINE_ME_15 = "\U0001f3f4\U000e006d\U000e0065\U000e0031\U000e0035\U000e007f" - FLAG_FOR_BOGDANCI_MK_05 = "\U0001f3f4\U000e006d\U000e006b\U000e0030\U000e0035\U000e007f" - FLAG_FOR_ISFAHAN_IR_04 = "\U0001f3f4\U000e0069\U000e0072\U000e0030\U000e0034\U000e007f" - FLAG_FOR_BUDVA_ME_05 = "\U0001f3f4\U000e006d\U000e0065\U000e0030\U000e0035\U000e007f" - FLAG_FOR_FIANARANTSOA_MG_F = "\U0001f3f4\U000e006d\U000e0067\U000e0066\U000e007f" - FLAG_FOR_PLJEVLJA_ME_14 = "\U0001f3f4\U000e006d\U000e0065\U000e0031\U000e0034\U000e007f" - FAMILY_MAN_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE_BABY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f466\U0001f3fc\u200d\U0001f476\U0001f3fc" - FLAG_FOR_BIJELO_POLJE_ME_04 = "\U0001f3f4\U000e006d\U000e0065\U000e0030\U000e0034\U000e007f" - FLAG_FOR_ANTSIRANANA_MG_D = "\U0001f3f4\U000e006d\U000e0067\U000e0064\U000e007f" - FLAG_FOR_SAVNIK_ME_18 = "\U0001f3f4\U000e006d\U000e0065\U000e0031\U000e0038\U000e007f" - FLAG_FOR_BEROVO_MK_03 = "\U0001f3f4\U000e006d\U000e006b\U000e0030\U000e0033\U000e007f" - FLAG_FOR_TOLIARA_MG_U = "\U0001f3f4\U000e006d\U000e0067\U000e0075\U000e007f" - FLAG_FOR_KOLASIN_ME_09 = "\U0001f3f4\U000e006d\U000e0065\U000e0030\U000e0039\U000e007f" - FLAG_FOR_ANDRIJEVICA_ME_01 = "\U0001f3f4\U000e006d\U000e0065\U000e0030\U000e0031\U000e007f" - FLAG_FOR_DERNA_LY_DR = "\U0001f3f4\U000e006c\U000e0079\U000e0064\U000e0072\U000e007f" - FLAG_FOR_ANTANANARIVO_MG_T = "\U0001f3f4\U000e006d\U000e0067\U000e0074\U000e007f" - FLAG_FOR_QEQQATA_GL_QE = "\U0001f3f4\U000e0067\U000e006c\U000e0071\U000e0065\U000e007f" - FLAG_FOR_MAHAJANGA_MG_M = "\U0001f3f4\U000e006d\U000e0067\U000e006d\U000e007f" - FLAG_FOR_NIKSIC_ME_12 = "\U0001f3f4\U000e006d\U000e0065\U000e0031\U000e0032\U000e007f" - FLAG_FOR_PETNJICA_ME_23 = "\U0001f3f4\U000e006d\U000e0065\U000e0032\U000e0033\U000e007f" - FLAG_FOR_BOSILOVO_MK_07 = "\U0001f3f4\U000e006d\U000e006b\U000e0030\U000e0037\U000e007f" - FLAG_FOR_FES_BOULEMANE_MA_05 = "\U0001f3f4\U000e006d\U000e0061\U000e0030\U000e0035\U000e007f" - FLAG_FOR_HOUAPHANH_LA_HO = "\U0001f3f4\U000e006c\U000e0061\U000e0068\U000e006f\U000e007f" - FLAG_FOR_STEFAN_VODA_MD_SV = "\U0001f3f4\U000e006d\U000e0064\U000e0073\U000e0076\U000e007f" - FAMILY_MAN_LIGHT_SKIN_TONE_MAN_LIGHT_SKIN_TONE_BABY_LIGHT_SKIN_TONE_GIRL_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f468\U0001f3fb\u200d\U0001f476\U0001f3fb\u200d\U0001f467\U0001f3fb" - FLAG_FOR_WADI_AL_SHATII_LY_WS = "\U0001f3f4\U000e006c\U000e0079\U000e0077\U000e0073\U000e007f" - FLAG_FOR_PODGORICA_ME_16 = "\U0001f3f4\U000e006d\U000e0065\U000e0031\U000e0036\U000e007f" - FLAG_FOR_KOTOR_ME_10 = "\U0001f3f4\U000e006d\U000e0065\U000e0031\U000e0030\U000e007f" - FLAG_FOR_MARANHAO_BR_MA = "\U0001f3f4\U000e0062\U000e0072\U000e006d\U000e0061\U000e007f" - FLAG_FOR_KRIVOGASTANI_MK_45 = "\U0001f3f4\U000e006d\U000e006b\U000e0034\U000e0035\U000e007f" - FLAG_FOR_KARBINCI_MK_37 = "\U0001f3f4\U000e006d\U000e006b\U000e0033\U000e0037\U000e007f" - FLAG_FOR_KAVADARCI_MK_36 = "\U0001f3f4\U000e006d\U000e006b\U000e0033\U000e0036\U000e007f" - FLAG_FOR_MAVROVO_AND_ROSTUSA_MK_50 = "\U0001f3f4\U000e006d\U000e006b\U000e0035\U000e0030\U000e007f" - FLAG_FOR_ZELINO_MK_30 = "\U0001f3f4\U000e006d\U000e006b\U000e0033\U000e0030\U000e007f" - FLAG_FOR_DOJRAN_MK_26 = "\U0001f3f4\U000e006d\U000e006b\U000e0032\U000e0036\U000e007f" - FLAG_FOR_KOCANI_MK_42 = "\U0001f3f4\U000e006d\U000e006b\U000e0034\U000e0032\U000e007f" - FLAG_FOR_NEGOTINO_MK_54 = "\U0001f3f4\U000e006d\U000e006b\U000e0035\U000e0034\U000e007f" - FLAG_FOR_ILINDEN_MK_34 = "\U0001f3f4\U000e006d\U000e006b\U000e0033\U000e0034\U000e007f" - FLAG_FOR_DEBAR_MK_21 = "\U0001f3f4\U000e006d\U000e006b\U000e0032\U000e0031\U000e007f" - FLAG_FOR_LOZOVO_MK_49 = "\U0001f3f4\U000e006d\U000e006b\U000e0034\U000e0039\U000e007f" - FLAG_FOR_GEVGELIJA_MK_18 = "\U0001f3f4\U000e006d\U000e006b\U000e0031\U000e0038\U000e007f" - FLAG_FOR_NOVACI_MK_55 = "\U0001f3f4\U000e006d\U000e006b\U000e0035\U000e0035\U000e007f" - FLAG_FOR_VINICA_MK_14 = "\U0001f3f4\U000e006d\U000e006b\U000e0031\U000e0034\U000e007f" - FLAG_FOR_VASILEVO_MK_11 = "\U0001f3f4\U000e006d\U000e006b\U000e0031\U000e0031\U000e007f" - FLAG_FOR_NOVO_SELO_MK_56 = "\U0001f3f4\U000e006d\U000e006b\U000e0035\U000e0036\U000e007f" - FLAG_FOR_KUMANOVO_MK_47 = "\U0001f3f4\U000e006d\U000e006b\U000e0034\U000e0037\U000e007f" - FLAG_FOR_GRADSKO_MK_20 = "\U0001f3f4\U000e006d\U000e006b\U000e0032\U000e0030\U000e007f" - FLAG_FOR_KONCE_MK_41 = "\U0001f3f4\U000e006d\U000e006b\U000e0034\U000e0031\U000e007f" - FLAG_FOR_ZELENIKOVO_MK_32 = "\U0001f3f4\U000e006d\U000e006b\U000e0033\U000e0032\U000e007f" - FLAG_FOR_JEGUNOVCE_MK_35 = "\U0001f3f4\U000e006d\U000e006b\U000e0033\U000e0035\U000e007f" - FLAG_FOR_MAKEDONSKA_KAMENICA_MK_51 = "\U0001f3f4\U000e006d\U000e006b\U000e0035\U000e0031\U000e007f" - FLAG_FOR_VEVCANI_MK_12 = "\U0001f3f4\U000e006d\U000e006b\U000e0031\U000e0032\U000e007f" - FLAG_FOR_GOSTIVAR_MK_19 = "\U0001f3f4\U000e006d\U000e006b\U000e0031\U000e0039\U000e007f" - FLAG_FOR_KRUSEVO_MK_46 = "\U0001f3f4\U000e006d\U000e006b\U000e0034\U000e0036\U000e007f" - FLAG_FOR_BRVENICA_MK_08 = "\U0001f3f4\U000e006d\U000e006b\U000e0030\U000e0038\U000e007f" - FLAG_FOR_LIPKOVO_MK_48 = "\U0001f3f4\U000e006d\U000e006b\U000e0034\U000e0038\U000e007f" - FLAG_FOR_VALANDOVO_MK_10 = "\U0001f3f4\U000e006d\U000e006b\U000e0031\U000e0030\U000e007f" - FLAG_FOR_KRATOVO_MK_43 = "\U0001f3f4\U000e006d\U000e006b\U000e0034\U000e0033\U000e007f" - FLAG_FOR_KRIVA_PALANKA_MK_44 = "\U0001f3f4\U000e006d\U000e006b\U000e0034\U000e0034\U000e007f" - FLAG_FOR_ZRNOVCI_MK_33 = "\U0001f3f4\U000e006d\U000e006b\U000e0033\U000e0033\U000e007f" - FLAG_FOR_TEL_AVIV_DISTRICT_IL_TA = "\U0001f3f4\U000e0069\U000e006c\U000e0074\U000e0061\U000e007f" - FLAG_FOR_DANILOVGRAD_ME_07 = "\U0001f3f4\U000e006d\U000e0065\U000e0030\U000e0037\U000e007f" - FLAG_FOR_DEBARCA_MK_22 = "\U0001f3f4\U000e006d\U000e006b\U000e0032\U000e0032\U000e007f" - FLAG_FOR_VRAPCISTE_MK_16 = "\U0001f3f4\U000e006d\U000e006b\U000e0031\U000e0036\U000e007f" - FLAG_FOR_DELCEVO_MK_23 = "\U0001f3f4\U000e006d\U000e006b\U000e0032\U000e0033\U000e007f" - FLAG_FOR_VELES_MK_13 = "\U0001f3f4\U000e006d\U000e006b\U000e0031\U000e0033\U000e007f" - FLAG_FOR_AYEYARWADY_MM_07 = "\U0001f3f4\U000e006d\U000e006d\U000e0030\U000e0037\U000e007f" - FLAG_FOR_KAYAH_MM_12 = "\U0001f3f4\U000e006d\U000e006d\U000e0031\U000e0032\U000e007f" - FLAG_FOR_STARO_NAGORICANE_MK_71 = "\U0001f3f4\U000e006d\U000e006b\U000e0037\U000e0031\U000e007f" - FLAG_FOR_STIP_MK_83 = "\U0001f3f4\U000e006d\U000e006b\U000e0038\U000e0033\U000e007f" - FLAG_FOR_DAEJEON_KR_30 = "\U0001f3f4\U000e006b\U000e0072\U000e0033\U000e0030\U000e007f" - FLAG_FOR_TETOVO_MK_76 = "\U0001f3f4\U000e006d\U000e006b\U000e0037\U000e0036\U000e007f" - FLAG_FOR_SZEGED_HU_SD = "\U0001f3f4\U000e0068\U000e0075\U000e0073\U000e0064\U000e007f" - FLAG_FOR_MOPTI_ML_5 = "\U0001f3f4\U000e006d\U000e006c\U000e0035\U000e007f" - FLAG_FOR_SIKASSO_ML_3 = "\U0001f3f4\U000e006d\U000e006c\U000e0033\U000e007f" - FLAG_FOR_U_S_OUTLYING_ISLANDS_US_UM = "\U0001f3f4\U000e0075\U000e0073\U000e0075\U000e006d\U000e007f" - FLAG_FOR_CASKA_MK_80 = "\U0001f3f4\U000e006d\U000e006b\U000e0038\U000e0030\U000e007f" - FLAG_FOR_STRUGA_MK_72 = "\U0001f3f4\U000e006d\U000e006b\U000e0037\U000e0032\U000e007f" - FLAG_FOR_KOULIKORO_ML_2 = "\U0001f3f4\U000e006d\U000e006c\U000e0032\U000e007f" - FLAG_FOR_PETROVEC_MK_59 = "\U0001f3f4\U000e006d\U000e006b\U000e0035\U000e0039\U000e007f" - KISS_MAN_MAN_MEDIUM_DARK_SKIN_TONE = "\U0001f468\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fe" - FLAG_FOR_CUCER_SANDEVO_MK_82 = "\U0001f3f4\U000e006d\U000e006b\U000e0038\U000e0032\U000e007f" - FLAG_FOR_SOPISTE_MK_70 = "\U0001f3f4\U000e006d\U000e006b\U000e0037\U000e0030\U000e007f" - FLAG_FOR_MANDALAY_MM_04 = "\U0001f3f4\U000e006d\U000e006d\U000e0030\U000e0034\U000e007f" - FLAG_FOR_GENEVA_CH_GE = "\U0001f3f4\U000e0063\U000e0068\U000e0067\U000e0065\U000e007f" - FLAG_FOR_KACHIN_MM_11 = "\U0001f3f4\U000e006d\U000e006d\U000e0031\U000e0031\U000e007f" - FLAG_FOR_PROBISTIP_MK_63 = "\U0001f3f4\U000e006d\U000e006b\U000e0036\U000e0033\U000e007f" - FLAG_FOR_RESEN_MK_66 = "\U0001f3f4\U000e006d\U000e006b\U000e0036\U000e0036\U000e007f" - FLAG_FOR_TANINTHARYI_MM_05 = "\U0001f3f4\U000e006d\U000e006d\U000e0030\U000e0035\U000e007f" - FLAG_FOR_ROSOMAN_MK_67 = "\U0001f3f4\U000e006d\U000e006b\U000e0036\U000e0037\U000e007f" - FLAG_FOR_CESINOVO_OBLESEVO_MK_81 = "\U0001f3f4\U000e006d\U000e006b\U000e0038\U000e0031\U000e007f" - FLAG_FOR_PEHCEVO_MK_60 = "\U0001f3f4\U000e006d\U000e006b\U000e0036\U000e0030\U000e007f" - FLAG_FOR_SAGAING_MM_01 = "\U0001f3f4\U000e006d\U000e006d\U000e0030\U000e0031\U000e007f" - FLAG_FOR_ZAVKHAN_MN_057 = "\U0001f3f4\U000e006d\U000e006e\U000e0030\U000e0035\U000e0037\U000e007f" - FLAG_FOR_TOV_MN_047 = "\U0001f3f4\U000e006d\U000e006e\U000e0030\U000e0034\U000e0037\U000e007f" - FLAG_FOR_INCHIRI_MR_12 = "\U0001f3f4\U000e006d\U000e0072\U000e0031\U000e0032\U000e007f" - FLAG_FOR_TAGANT_MR_09 = "\U0001f3f4\U000e006d\U000e0072\U000e0030\U000e0039\U000e007f" - FLAG_FOR_DAKHLET_NOUADHIBOU_MR_08 = "\U0001f3f4\U000e006d\U000e0072\U000e0030\U000e0038\U000e007f" - FLAG_FOR_OVORKHANGAI_MN_055 = "\U0001f3f4\U000e006d\U000e006e\U000e0030\U000e0035\U000e0035\U000e007f" - TAG_DOLLAR_SIGN = "\U000e0024" - FLAG_FOR_HODH_EL_GHARBI_MR_02 = "\U0001f3f4\U000e006d\U000e0072\U000e0030\U000e0032\U000e007f" - FLAG_FOR_BAYAN_OLGII_MN_071 = "\U0001f3f4\U000e006d\U000e006e\U000e0030\U000e0037\U000e0031\U000e007f" - FLAG_FOR_DUNDGOVI_MN_059 = "\U0001f3f4\U000e006d\U000e006e\U000e0030\U000e0035\U000e0039\U000e007f" - FLAG_FOR_SCHWYZ_CH_SZ = "\U0001f3f4\U000e0063\U000e0068\U000e0073\U000e007a\U000e007f" - FLAG_FOR_BULGAN_MN_067 = "\U0001f3f4\U000e006d\U000e006e\U000e0030\U000e0036\U000e0037\U000e007f" - TAG_TILDE = "\U000e007e" - FLAG_FOR_KAYIN_MM_13 = "\U0001f3f4\U000e006d\U000e006d\U000e0031\U000e0033\U000e007f" - FLAG_FOR_GAO_ML_7 = "\U0001f3f4\U000e006d\U000e006c\U000e0037\U000e007f" - FLAG_FOR_DORNOD_MN_061 = "\U0001f3f4\U000e006d\U000e006e\U000e0030\U000e0036\U000e0031\U000e007f" - FLAG_FOR_TRARZA_MR_06 = "\U0001f3f4\U000e006d\U000e0072\U000e0030\U000e0036\U000e007f" - FLAG_FOR_DARKHAN_UUL_MN_037 = "\U0001f3f4\U000e006d\U000e006e\U000e0030\U000e0033\U000e0037\U000e007f" - FLAG_FOR_UVS_MN_046 = "\U0001f3f4\U000e006d\U000e006e\U000e0030\U000e0034\U000e0036\U000e007f" - FLAG_FOR_KHOVSGOL_MN_041 = "\U0001f3f4\U000e006d\U000e006e\U000e0030\U000e0034\U000e0031\U000e007f" - FLAG_FOR_BAYANKHONGOR_MN_069 = "\U0001f3f4\U000e006d\U000e006e\U000e0030\U000e0036\U000e0039\U000e007f" - FLAG_FOR_HODH_ECH_CHARGUI_MR_01 = "\U0001f3f4\U000e006d\U000e0072\U000e0030\U000e0031\U000e007f" - FLAG_FOR_NAYPYIDAW_MM_18 = "\U0001f3f4\U000e006d\U000e006d\U000e0031\U000e0038\U000e007f" - FLAG_FOR_DORNOGOVI_MN_063 = "\U0001f3f4\U000e006d\U000e006e\U000e0030\U000e0036\U000e0033\U000e007f" - FAMILY_WOMAN_MEDIUM_LIGHT_SKIN_TONE_WOMAN_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE_BOY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f469\U0001f3fc\u200d\U0001f466\U0001f3fc\u200d\U0001f466\U0001f3fc" - FLAG_FOR_SAN_MIGUEL_SV_SM = "\U0001f3f4\U000e0073\U000e0076\U000e0073\U000e006d\U000e007f" - FLAG_FOR_MANCHESTER_JM_12 = "\U0001f3f4\U000e006a\U000e006d\U000e0031\U000e0032\U000e007f" - WHITE_RIGHT_POINTING_INDEX_EMOJI_MODIFIER_FITZPATRICK_TYPE_1_2 = "\u261e\U0001f3fb" - REVERSED_THUMBS_DOWN_SIGN_EMOJI_MODIFIER_FITZPATRICK_TYPE_1_2 = "\U0001f593\U0001f3fb" - BLACK_RIGHT_POINTING_INDEX_EMOJI_MODIFIER_FITZPATRICK_TYPE_1_2 = "\u261b\U0001f3fb" - REVERSED_RAISED_HAND_WITH_FINGERS_SPLAYED_EMOJI_MODIFIER_FITZPATRICK_TYPE_1_2 = "\U0001f591\U0001f3fb" - WHITE_DOWN_POINTING_INDEX_EMOJI_MODIFIER_FITZPATRICK_TYPE_1_2 = "\u261f\U0001f3fb" - BLACK_LEFT_POINTING_INDEX_EMOJI_MODIFIER_FITZPATRICK_TYPE_1_2 = "\u261a\U0001f3fb" - REVERSED_VICTORY_HAND_EMOJI_MODIFIER_FITZPATRICK_TYPE_1_2 = "\U0001f594\U0001f3fb" - WHITE_LEFT_POINTING_INDEX_EMOJI_MODIFIER_FITZPATRICK_TYPE_1_2 = "\u261c\U0001f3fb" - REVERSED_THUMBS_UP_SIGN_EMOJI_MODIFIER_FITZPATRICK_TYPE_1_2 = "\U0001f592\U0001f3fb" - LEFT_WRITING_HAND_EMOJI_MODIFIER_FITZPATRICK_TYPE_1_2 = "\U0001f58e\U0001f3fb" - WHITE_LEFT_POINTING_INDEX_EMOJI_MODIFIER_FITZPATRICK_TYPE_3 = "\u261c\U0001f3fc" - BLACK_RIGHT_POINTING_INDEX_EMOJI_MODIFIER_FITZPATRICK_TYPE_3 = "\u261b\U0001f3fc" - REVERSED_THUMBS_UP_SIGN_EMOJI_MODIFIER_FITZPATRICK_TYPE_3 = "\U0001f592\U0001f3fc" - WHITE_RIGHT_POINTING_INDEX_EMOJI_MODIFIER_FITZPATRICK_TYPE_3 = "\u261e\U0001f3fc" - LEFT_WRITING_HAND_EMOJI_MODIFIER_FITZPATRICK_TYPE_3 = "\U0001f58e\U0001f3fc" - REVERSED_VICTORY_HAND_EMOJI_MODIFIER_FITZPATRICK_TYPE_3 = "\U0001f594\U0001f3fc" - WHITE_DOWN_POINTING_INDEX_EMOJI_MODIFIER_FITZPATRICK_TYPE_3 = "\u261f\U0001f3fc" - BLACK_LEFT_POINTING_INDEX_EMOJI_MODIFIER_FITZPATRICK_TYPE_3 = "\u261a\U0001f3fc" - REVERSED_THUMBS_DOWN_SIGN_EMOJI_MODIFIER_FITZPATRICK_TYPE_3 = "\U0001f593\U0001f3fc" - REVERSED_RAISED_HAND_WITH_FINGERS_SPLAYED_EMOJI_MODIFIER_FITZPATRICK_TYPE_3 = "\U0001f591\U0001f3fc" - REVERSED_RAISED_HAND_WITH_FINGERS_SPLAYED_EMOJI_MODIFIER_FITZPATRICK_TYPE_4 = "\U0001f591\U0001f3fd" - BLACK_LEFT_POINTING_INDEX_EMOJI_MODIFIER_FITZPATRICK_TYPE_4 = "\u261a\U0001f3fd" - REVERSED_THUMBS_DOWN_SIGN_EMOJI_MODIFIER_FITZPATRICK_TYPE_4 = "\U0001f593\U0001f3fd" - WHITE_DOWN_POINTING_INDEX_EMOJI_MODIFIER_FITZPATRICK_TYPE_4 = "\u261f\U0001f3fd" - REVERSED_THUMBS_UP_SIGN_EMOJI_MODIFIER_FITZPATRICK_TYPE_4 = "\U0001f592\U0001f3fd" - BLACK_RIGHT_POINTING_INDEX_EMOJI_MODIFIER_FITZPATRICK_TYPE_4 = "\u261b\U0001f3fd" - REVERSED_VICTORY_HAND_EMOJI_MODIFIER_FITZPATRICK_TYPE_4 = "\U0001f594\U0001f3fd" - WHITE_LEFT_POINTING_INDEX_EMOJI_MODIFIER_FITZPATRICK_TYPE_4 = "\u261c\U0001f3fd" - LEFT_WRITING_HAND_EMOJI_MODIFIER_FITZPATRICK_TYPE_4 = "\U0001f58e\U0001f3fd" - WHITE_RIGHT_POINTING_INDEX_EMOJI_MODIFIER_FITZPATRICK_TYPE_4 = "\u261e\U0001f3fd" - LEFT_WRITING_HAND_EMOJI_MODIFIER_FITZPATRICK_TYPE_5 = "\U0001f58e\U0001f3fe" - WHITE_RIGHT_POINTING_INDEX_EMOJI_MODIFIER_FITZPATRICK_TYPE_5 = "\u261e\U0001f3fe" - WHITE_LEFT_POINTING_INDEX_EMOJI_MODIFIER_FITZPATRICK_TYPE_5 = "\u261c\U0001f3fe" - BLACK_LEFT_POINTING_INDEX_EMOJI_MODIFIER_FITZPATRICK_TYPE_5 = "\u261a\U0001f3fe" - WHITE_DOWN_POINTING_INDEX_EMOJI_MODIFIER_FITZPATRICK_TYPE_5 = "\u261f\U0001f3fe" - REVERSED_THUMBS_DOWN_SIGN_EMOJI_MODIFIER_FITZPATRICK_TYPE_5 = "\U0001f593\U0001f3fe" - REVERSED_RAISED_HAND_WITH_FINGERS_SPLAYED_EMOJI_MODIFIER_FITZPATRICK_TYPE_5 = "\U0001f591\U0001f3fe" - REVERSED_VICTORY_HAND_EMOJI_MODIFIER_FITZPATRICK_TYPE_5 = "\U0001f594\U0001f3fe" - BLACK_RIGHT_POINTING_INDEX_EMOJI_MODIFIER_FITZPATRICK_TYPE_5 = "\u261b\U0001f3fe" - REVERSED_THUMBS_UP_SIGN_EMOJI_MODIFIER_FITZPATRICK_TYPE_5 = "\U0001f592\U0001f3fe" - REVERSED_THUMBS_DOWN_SIGN_EMOJI_MODIFIER_FITZPATRICK_TYPE_6 = "\U0001f593\U0001f3ff" - WHITE_LEFT_POINTING_INDEX_EMOJI_MODIFIER_FITZPATRICK_TYPE_6 = "\u261c\U0001f3ff" - WHITE_RIGHT_POINTING_INDEX_EMOJI_MODIFIER_FITZPATRICK_TYPE_6 = "\u261e\U0001f3ff" - BLACK_LEFT_POINTING_INDEX_EMOJI_MODIFIER_FITZPATRICK_TYPE_6 = "\u261a\U0001f3ff" - BLACK_RIGHT_POINTING_INDEX_EMOJI_MODIFIER_FITZPATRICK_TYPE_6 = "\u261b\U0001f3ff" - REVERSED_RAISED_HAND_WITH_FINGERS_SPLAYED_EMOJI_MODIFIER_FITZPATRICK_TYPE_6 = "\U0001f591\U0001f3ff" - WHITE_DOWN_POINTING_INDEX_EMOJI_MODIFIER_FITZPATRICK_TYPE_6 = "\u261f\U0001f3ff" - REVERSED_THUMBS_UP_SIGN_EMOJI_MODIFIER_FITZPATRICK_TYPE_6 = "\U0001f592\U0001f3ff" - LEFT_WRITING_HAND_EMOJI_MODIFIER_FITZPATRICK_TYPE_6 = "\U0001f58e\U0001f3ff" - REVERSED_VICTORY_HAND_EMOJI_MODIFIER_FITZPATRICK_TYPE_6 = "\U0001f594\U0001f3ff" diff --git a/pyrogram/client/ext/syncer.py b/pyrogram/client/ext/syncer.py deleted file mode 100644 index 71dc3f353a5..00000000000 --- a/pyrogram/client/ext/syncer.py +++ /dev/null @@ -1,128 +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 . - -import base64 -import json -import logging -import os -import shutil -import time -from threading import Thread, Event, Lock - -from . import utils - -log = logging.getLogger(__name__) - - -class Syncer: - INTERVAL = 20 - - clients = {} - thread = None - event = Event() - lock = Lock() - - @classmethod - def add(cls, client): - with cls.lock: - cls.sync(client) - - cls.clients[id(client)] = client - - if len(cls.clients) == 1: - cls.start() - - @classmethod - def remove(cls, client): - with cls.lock: - cls.sync(client) - - del cls.clients[id(client)] - - if len(cls.clients) == 0: - cls.stop() - - @classmethod - def start(cls): - cls.event.clear() - cls.thread = Thread(target=cls.worker, name=cls.__name__) - cls.thread.start() - - @classmethod - def stop(cls): - cls.event.set() - - @classmethod - def worker(cls): - while True: - cls.event.wait(cls.INTERVAL) - - if cls.event.is_set(): - break - - with cls.lock: - for client in cls.clients.values(): - cls.sync(client) - - @classmethod - def sync(cls, client): - temporary = os.path.join(client.workdir, "{}.sync".format(client.session_name)) - persistent = os.path.join(client.workdir, "{}.session".format(client.session_name)) - - try: - auth_key = base64.b64encode(client.auth_key).decode() - auth_key = [auth_key[i: i + 43] for i in range(0, len(auth_key), 43)] - - data = dict( - dc_id=client.dc_id, - test_mode=client.test_mode, - auth_key=auth_key, - user_id=client.user_id, - date=int(time.time()), - is_bot=client.is_bot, - peers_by_id={ - k: getattr(v, "access_hash", None) - for k, v in client.peers_by_id.copy().items() - }, - peers_by_username={ - k: utils.get_peer_id(v) - for k, v in client.peers_by_username.copy().items() - }, - peers_by_phone={ - k: utils.get_peer_id(v) - for k, v in client.peers_by_phone.copy().items() - } - ) - - os.makedirs(client.workdir, exist_ok=True) - - with open(temporary, "w", encoding="utf-8") as f: - json.dump(data, f, indent=4) - - f.flush() - os.fsync(f.fileno()) - except Exception as e: - log.critical(e, exc_info=True) - else: - shutil.move(temporary, persistent) - log.info("Synced {}".format(client.session_name)) - finally: - try: - os.remove(temporary) - except OSError: - pass diff --git a/pyrogram/client/ext/utils.py b/pyrogram/client/ext/utils.py deleted file mode 100644 index 981752fa551..00000000000 --- a/pyrogram/client/ext/utils.py +++ /dev/null @@ -1,84 +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 base64 import b64decode, b64encode - -from ...api import types - - -def decode(s: str) -> bytes: - s = b64decode(s + "=" * (-len(s) % 4), "-_") - r = b"" - - assert s[-1] == 2 - - i = 0 - while i < len(s) - 1: - if s[i] != 0: - r += bytes([s[i]]) - else: - r += b"\x00" * s[i + 1] - i += 1 - - i += 1 - - return r - - -def encode(s: bytes) -> str: - r = b"" - n = 0 - - for i in s + bytes([2]): - if i == 0: - n += 1 - else: - if n: - r += b"\x00" + bytes([n]) - n = 0 - - r += bytes([i]) - - return b64encode(r, b"-_").decode().rstrip("=") - - -def get_peer_id(input_peer) -> int: - return ( - input_peer.user_id if isinstance(input_peer, types.InputPeerUser) - else -input_peer.chat_id if isinstance(input_peer, types.InputPeerChat) - else int("-100" + str(input_peer.channel_id)) - ) - - -def get_input_peer(peer_id: int, access_hash: int): - return ( - types.InputPeerUser(user_id=peer_id, access_hash=access_hash) if peer_id > 0 - else types.InputPeerChannel(channel_id=int(str(peer_id)[4:]), access_hash=access_hash) - if (str(peer_id).startswith("-100") and access_hash) - else types.InputPeerChat(chat_id=-peer_id) - ) - - -def get_offset_date(dialogs): - for m in reversed(dialogs.messages): - if isinstance(m, types.MessageEmpty): - continue - else: - return m.date - else: - return 0 diff --git a/pyrogram/client/filters/__init__.py b/pyrogram/client/filters/__init__.py deleted file mode 100644 index 318d864542a..00000000000 --- a/pyrogram/client/filters/__init__.py +++ /dev/null @@ -1,19 +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 .filters import Filters diff --git a/pyrogram/client/filters/filter.py b/pyrogram/client/filters/filter.py deleted file mode 100644 index f8bf5e3e1c1..00000000000 --- a/pyrogram/client/filters/filter.py +++ /dev/null @@ -1,57 +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 . - - -class Filter: - def __call__(self, message): - raise NotImplementedError - - def __invert__(self): - return InvertFilter(self) - - def __and__(self, other): - return AndFilter(self, other) - - def __or__(self, other): - return OrFilter(self, other) - - -class InvertFilter(Filter): - def __init__(self, base): - self.base = base - - def __call__(self, message): - return not self.base(message) - - -class AndFilter(Filter): - def __init__(self, base, other): - self.base = base - self.other = other - - def __call__(self, message): - return self.base(message) and self.other(message) - - -class OrFilter(Filter): - def __init__(self, base, other): - self.base = base - self.other = other - - def __call__(self, message): - return self.base(message) or self.other(message) diff --git a/pyrogram/client/filters/filters.py b/pyrogram/client/filters/filters.py deleted file mode 100644 index 5070bd5238d..00000000000 --- a/pyrogram/client/filters/filters.py +++ /dev/null @@ -1,358 +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 . - -import re - -from .filter import Filter -from ..types.keyboards import InlineKeyboardMarkup, ReplyKeyboardMarkup - - -def create(name: str, func: callable, **kwargs) -> type: - """Use this method to create a Filter. - - Custom filters give you extra control over which updates are allowed or not to be processed by your handlers. - - Parameters: - name (``str``): - Your filter's name. Can be anything you like. - - func (``callable``): - 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 - 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`). - """ - # TODO: unpack kwargs using **kwargs into the dict itself. For Python 3.5+ only - d = {"__call__": func} - d.update(kwargs) - - return type(name, (Filter,), d)() - - -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. - 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. - """ - - create = create - - me = create("Me", lambda _, m: bool(m.from_user and m.from_user.is_self)) - """Filter messages generated by you yourself.""" - - bot = create("Bot", lambda _, m: bool(m.from_user and m.from_user.is_bot)) - """Filter messages coming from bots.""" - - incoming = create("Incoming", lambda _, m: not m.outgoing) - """Filter incoming messages. Messages sent to your own chat (Saved Messages) are also recognised as incoming.""" - - outgoing = create("Outgoing", lambda _, m: m.outgoing) - """Filter outgoing messages. Messages sent to your own chat (Saved Messages) are not recognized as outgoing.""" - - text = create("Text", lambda _, m: bool(m.text)) - """Filter text messages.""" - - reply = create("Reply", lambda _, m: bool(m.reply_to_message)) - """Filter messages that are replies to other messages.""" - - forwarded = create("Forwarded", lambda _, m: bool(m.forward_date)) - """Filter messages that are forwarded.""" - - caption = create("Caption", lambda _, m: bool(m.caption)) - """Filter media messages that contain captions.""" - - edited = create("Edited", lambda _, m: bool(m.edit_date)) - """Filter edited messages.""" - - audio = create("Audio", lambda _, m: bool(m.audio)) - """Filter messages that contain :obj:`Audio` objects.""" - - document = create("Document", lambda _, m: bool(m.document)) - """Filter messages that contain :obj:`Document` objects.""" - - photo = create("Photo", lambda _, m: bool(m.photo)) - """Filter messages that contain :obj:`Photo` objects.""" - - sticker = create("Sticker", lambda _, m: bool(m.sticker)) - """Filter messages that contain :obj:`Sticker` objects.""" - - animation = create("Animation", lambda _, m: bool(m.animation)) - """Filter messages that contain :obj:`Animation` objects.""" - - game = create("Game", lambda _, m: bool(m.game)) - """Filter messages that contain :obj:`Game` objects.""" - - video = create("Video", lambda _, m: bool(m.video)) - """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.""" - - video_note = create("VideoNote", lambda _, m: bool(m.video_note)) - """Filter messages that contain :obj:`VideoNote` objects.""" - - contact = create("Contact", lambda _, m: bool(m.contact)) - """Filter messages that contain :obj:`Contact` objects.""" - - location = create("Location", lambda _, m: bool(m.location)) - """Filter messages that contain :obj:`Location` objects.""" - - venue = create("Venue", lambda _, m: bool(m.venue)) - """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.""" - - private = create("Private", lambda _, m: bool(m.chat and m.chat.type == "private")) - """Filter messages sent in private chats.""" - - group = create("Group", lambda _, m: bool(m.chat and m.chat.type in {"group", "supergroup"})) - """Filter messages sent in group or supergroup chats.""" - - channel = create("Channel", lambda _, m: bool(m.chat and m.chat.type == "channel")) - """Filter messages sent in channels.""" - - new_chat_members = create("NewChatMembers", lambda _, m: bool(m.new_chat_members)) - """Filter service messages for new chat members.""" - - left_chat_member = create("LeftChatMember", lambda _, m: bool(m.left_chat_member)) - """Filter service messages for members that left the chat.""" - - new_chat_title = create("NewChatTitle", lambda _, m: bool(m.new_chat_title)) - """Filter service messages for new chat titles.""" - - new_chat_photo = create("NewChatPhoto", lambda _, m: bool(m.new_chat_photo)) - """Filter service messages for new chat photos.""" - - delete_chat_photo = create("DeleteChatPhoto", lambda _, m: bool(m.delete_chat_photo)) - """Filter service messages for deleted photos.""" - - group_chat_created = create("GroupChatCreated", lambda _, m: bool(m.group_chat_created)) - """Filter service messages for group chat creations.""" - - supergroup_chat_created = create("SupergroupChatCreated", lambda _, m: bool(m.supergroup_chat_created)) - """Filter service messages for supergroup chat creations.""" - - channel_chat_created = create("ChannelChatCreated", lambda _, m: bool(m.channel_chat_created)) - """Filter service messages for channel chat creations.""" - - migrate_to_chat_id = create("MigrateToChatId", lambda _, m: bool(m.migrate_to_chat_id)) - """Filter service messages that contain migrate_to_chat_id.""" - - migrate_from_chat_id = create("MigrateFromChatId", lambda _, m: bool(m.migrate_from_chat_id)) - """Filter service messages that contain migrate_from_chat_id.""" - - pinned_message = create("PinnedMessage", lambda _, m: bool(m.pinned_message)) - """Filter service messages for pinned messages.""" - - game_high_score = create("GameHighScore", lambda _, m: bool(m.game_high_score)) - """Filter service messages for game high scores.""" - - reply_keyboard = create("ReplyKeyboard", lambda _, m: isinstance(m.reply_markup, ReplyKeyboardMarkup)) - """Filter messages containing reply keyboard markups""" - - inline_keyboard = create("InlineKeyboard", lambda _, m: isinstance(m.reply_markup, InlineKeyboardMarkup)) - """Filter messages containing inline keyboard markups""" - - mentioned = create("Mentioned", lambda _, m: bool(m.mentioned)) - """Filter messages containing mentions""" - - via_bot = create("ViaBot", lambda _, m: bool(m.via_bot)) - """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 - - - 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 - - - audio - - document - - photo - - sticker - - video - - animation - - voice - - video_note - - contact - - location - - venue - - poll""" - - @staticmethod - def command( - commands: str or list, - prefix: str or list = "/", - separator: str = " ", - case_sensitive: bool = False - ): - """Filter commands, i.e.: text messages starting with "/" or any other custom prefix. - - 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`. - - prefix (``str`` | ``list``, *optional*): - A prefix or a list of prefixes as string the filter should look for. - Defaults to "/" (slash). Examples: ".", "!", ["/", "!", "."]. - Can be None or "" (empty string) to allow commands with no prefix at all. - - separator (``str``, *optional*): - The command arguments separator. Defaults to " " (white space). - Examples: /start first second, /start-first-second, /start.first.second. - - case_sensitive (``bool``, *optional*): - Pass True if you want your command(s) to be case sensitive. Defaults to False. - Examples: when True, command="Start" would trigger /Start but not /start. - """ - - def func(flt, message): - text = message.text or message.caption - - if text: - for p in flt.p: - if text.startswith(p): - s = text.split(flt.s) - c, a = s[0][len(p):], s[1:] - c = c if flt.cs else c.lower() - message.command = ([c] + a) if c in flt.c else None - break - - return bool(message.command) - - commands = commands if type(commands) is list else [commands] - commands = {c if case_sensitive else c.lower() for c in commands} - prefixes = set(prefix) if prefix else {""} - - return create("Command", func=func, c=commands, p=prefixes, s=separator, cs=case_sensitive) - - @staticmethod - def regex(pattern, flags: int = 0): - """Filter messages that match a given RegEx pattern. - - 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. - - flags (``int``, *optional*): - RegEx flags. - """ - - def f(_, m): - m.matches = [i for i in _.p.finditer(m.text or m.caption or "")] - return bool(m.matches) - - return create("Regex", f, p=re.compile(pattern, flags)) - - # noinspection PyPep8Naming - class user(Filter, set): - """Filter messages coming from one or more users. - - You can use `set bound methods `_ to manipulate the - users container. - - 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. - Defaults to None (no users). - """ - - def __init__(self, users: int or str or list = None): - users = [] if users is None else users if type(users) is list else [users] - - super().__init__( - "me" if u in ["me", "self"] - else u.lower().strip("@") if type(u) is str - else u for u in users - ) - - def __call__(self, message): - return (message.from_user - and (message.from_user.id in self - or (message.from_user.username - and message.from_user.username.lower() in self) - or ("me" in self - and message.from_user.is_self))) - - # noinspection PyPep8Naming - class chat(Filter, set): - """Filter messages coming from one or more chats. - - You can use `set bound methods `_ to manipulate the - chats container. - - 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". - Defaults to None (no chats). - """ - - def __init__(self, chats: int or str or list = None): - chats = [] if chats is None else chats if type(chats) is list else [chats] - - super().__init__( - "me" if c in ["me", "self"] - else c.lower().strip("@") if type(c) is str - else c for c in chats - ) - - def __call__(self, message): - return (message.chat - and (message.chat.id in self - or (message.chat.username - and message.chat.username.lower() in self) - or ("me" in self - and message.from_user - and message.from_user.is_self - and not message.outgoing))) - - 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 deleted file mode 100644 index c88c12fe140..00000000000 --- a/pyrogram/client/handlers/__init__.py +++ /dev/null @@ -1,31 +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 .callback_query_handler import CallbackQueryHandler -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", "PollHandler" -] diff --git a/pyrogram/client/handlers/callback_query_handler.py b/pyrogram/client/handlers/callback_query_handler.py deleted file mode 100644 index feb46cb072d..00000000000 --- a/pyrogram/client/handlers/callback_query_handler.py +++ /dev/null @@ -1,47 +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 .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() ` - - For a nicer way to register this handler, have a look at the - :meth:`on_callback_query() ` decorator. - - Parameters: - callback (``callable``): - Pass a function that will be called when a new CallbackQuery arrives. It takes *(client, callback_query)* - as positional arguments (look at the section below for a detailed description). - - filters (:obj:`Filters`): - Pass one or more filters to allow only a subset of callback queries to be passed - in your callback function. - - Other parameters: - client (:obj:`Client`): - The Client itself, useful when you want to call other API methods inside the message handler. - - callback_query (:obj:`CallbackQuery `): - The received callback query. - """ - - def __init__(self, callback: callable, filters=None): - super().__init__(callback, filters) diff --git a/pyrogram/client/handlers/deleted_messages_handler.py b/pyrogram/client/handlers/deleted_messages_handler.py deleted file mode 100644 index f37caaedb75..00000000000 --- a/pyrogram/client/handlers/deleted_messages_handler.py +++ /dev/null @@ -1,51 +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 .handler import Handler - - -class DeletedMessagesHandler(Handler): - """The deleted Messages handler class. Used to handle deleted messages coming from any chat - (private, group, channel). It is intended to be used with - :meth:`add_handler() ` - - For a nicer way to register this handler, have a look at the - :meth:`on_deleted_messages() ` decorator. - - Parameters: - callback (``callable``): - Pass a function that will be called when one or more Messages have been deleted. - It takes *(client, messages)* as positional arguments (look at the section below for a detailed description). - - filters (:obj:`Filters`): - Pass one or more filters to allow only a subset of messages to be passed - in your callback function. - - Other parameters: - client (:obj:`Client`): - The Client itself, useful when you want to call other API methods inside the message handler. - - messages (:obj:`Messages`): - The deleted messages. - """ - - def __init__(self, callback: callable, filters=None): - super().__init__(callback, filters) - - def check(self, messages): - return super().check(messages.messages[0]) diff --git a/pyrogram/client/handlers/disconnect_handler.py b/pyrogram/client/handlers/disconnect_handler.py deleted file mode 100644 index b9e6350af17..00000000000 --- a/pyrogram/client/handlers/disconnect_handler.py +++ /dev/null @@ -1,41 +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 .handler import Handler - - -class DisconnectHandler(Handler): - """The Disconnect handler class. Used to handle disconnections. It is intended to be used with - :meth:`add_handler() ` - - For a nicer way to register this handler, have a look at the - :meth:`on_disconnect() ` decorator. - - Parameters: - callback (``callable``): - Pass a function that will be called when a disconnection occurs. It takes *(client)* - as positional argument (look at the section below for a detailed description). - - Other parameters: - client (:obj:`Client`): - The Client itself. Useful, for example, when you want to change the proxy before a new connection - is established. - """ - - def __init__(self, callback: callable): - super().__init__(callback) diff --git a/pyrogram/client/handlers/handler.py b/pyrogram/client/handlers/handler.py deleted file mode 100644 index 36963280de3..00000000000 --- a/pyrogram/client/handlers/handler.py +++ /dev/null @@ -1,30 +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 . - - -class Handler: - def __init__(self, callback: callable, filters=None): - self.callback = callback - self.filters = filters - - def check(self, update): - return ( - self.filters(update) - if callable(self.filters) - else True - ) diff --git a/pyrogram/client/handlers/inline_query_handler.py b/pyrogram/client/handlers/inline_query_handler.py deleted file mode 100644 index 98a256529e5..00000000000 --- a/pyrogram/client/handlers/inline_query_handler.py +++ /dev/null @@ -1,47 +0,0 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 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 .handler import Handler - - -class InlineQueryHandler(Handler): - """The InlineQuery handler class. Used to handle inline queries. - It is intended to be used with :meth:`add_handler() ` - - For a nicer way to register this handler, have a look at the - :meth:`on_inline_query() ` decorator. - - Parameters: - callback (``callable``): - Pass a function that will be called when a new InlineQuery arrives. It takes *(client, inline_query)* - as positional arguments (look at the section below for a detailed description). - - filters (:obj:`Filters`): - Pass one or more filters to allow only a subset of inline queries to be passed - in your callback function. - - Other parameters: - client (:obj:`Client`): - The Client itself, useful when you want to call other API methods inside the inline query handler. - - inline_query (:obj:`InlineQuery`): - The received inline query. - """ - - def __init__(self, callback: callable, filters=None): - super().__init__(callback, filters) diff --git a/pyrogram/client/handlers/message_handler.py b/pyrogram/client/handlers/message_handler.py deleted file mode 100644 index 10fff47953f..00000000000 --- a/pyrogram/client/handlers/message_handler.py +++ /dev/null @@ -1,48 +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 .handler import Handler - - -class MessageHandler(Handler): - """The Message handler class. Used to handle text, media and service messages coming from - any chat (private, group, channel). It is intended to be used with - :meth:`add_handler() ` - - For a nicer way to register this handler, have a look at the - :meth:`on_message() ` decorator. - - Parameters: - callback (``callable``): - Pass a function that will be called when a new Message arrives. It takes *(client, message)* - as positional arguments (look at the section below for a detailed description). - - filters (:obj:`Filters`): - Pass one or more filters to allow only a subset of messages to be passed - in your callback function. - - Other parameters: - client (:obj:`Client`): - The Client itself, useful when you want to call other API methods inside the message handler. - - message (:obj:`Message`): - The received message. - """ - - def __init__(self, callback: callable, filters=None): - super().__init__(callback, filters) diff --git a/pyrogram/client/handlers/poll_handler.py b/pyrogram/client/handlers/poll_handler.py deleted file mode 100644 index 9e97f2ac03f..00000000000 --- a/pyrogram/client/handlers/poll_handler.py +++ /dev/null @@ -1,48 +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 .handler import Handler - - -class PollHandler(Handler): - """The Poll handler class. Used to handle polls updates. - - It is intended to be used with :meth:`add_handler() ` - - For a nicer way to register this handler, have a look at the - :meth:`on_poll() ` decorator. - - Parameters: - callback (``callable``): - Pass a function that will be called when a new poll update arrives. It takes *(client, poll)* - as positional arguments (look at the section below for a detailed description). - - filters (:obj:`Filters `): - Pass one or more filters to allow only a subset of polls to be passed - in your callback function. - - Other parameters: - client (:obj:`Client `): - The Client itself, useful when you want to call other API methods inside the poll handler. - - poll (:obj:`Poll `): - The received poll. - """ - - def __init__(self, callback: callable, filters=None): - super().__init__(callback, filters) diff --git a/pyrogram/client/handlers/raw_update_handler.py b/pyrogram/client/handlers/raw_update_handler.py deleted file mode 100644 index f54d59b6d5a..00000000000 --- a/pyrogram/client/handlers/raw_update_handler.py +++ /dev/null @@ -1,65 +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 .handler import Handler - - -class RawUpdateHandler(Handler): - """The Raw Update handler class. Used to handle raw updates. It is intended to be used with - :meth:`add_handler() ` - - For a nicer way to register this handler, have a look at the - :meth:`on_raw_update() ` decorator. - - Parameters: - callback (``callable``): - A function that will be called when a new update is received from the server. It takes - *(client, update, users, chats)* as positional arguments (look at the section below for - a detailed description). - - Other Parameters: - client (:class:`Client`): - The Client itself, useful when you want to call other API methods inside the update handler. - - update (``Update``): - The received update, which can be one of the many single Updates listed in the *updates* - field you see in the :obj:`Update ` type. - - users (``dict``): - Dictionary of all :obj:`User ` mentioned in the update. - You can access extra info about the user (such as *first_name*, *last_name*, etc...) by using - the IDs you find in the *update* argument (e.g.: *users[1768841572]*). - - chats (``dict``): - Dictionary of all :obj:`Chat ` and - :obj:`Channel ` mentioned in the update. - You can access extra info about the chat (such as *title*, *participants_count*, etc...) - by using the IDs you find in the *update* argument (e.g.: *chats[1701277281]*). - - Note: - The following Empty or Forbidden types may exist inside the *users* and *chats* dictionaries. - They mean you have been blocked by the user or banned from the group/channel. - - - :obj:`UserEmpty ` - - :obj:`ChatEmpty ` - - :obj:`ChatForbidden ` - - :obj:`ChannelForbidden ` - """ - - def __init__(self, callback: callable): - super().__init__(callback) diff --git a/pyrogram/client/handlers/user_status_handler.py b/pyrogram/client/handlers/user_status_handler.py deleted file mode 100644 index 1250cb19dd8..00000000000 --- a/pyrogram/client/handlers/user_status_handler.py +++ /dev/null @@ -1,47 +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 .handler import Handler - - -class UserStatusHandler(Handler): - """The UserStatus handler class. Used to handle user status updates (user going online or offline). - It is intended to be used with :meth:`add_handler() ` - - For a nicer way to register this handler, have a look at the - :meth:`on_user_status() ` decorator. - - Parameters: - callback (``callable``): - Pass a function that will be called when a new UserStatus update arrives. It takes *(client, user_status)* - as positional arguments (look at the section below for a detailed description). - - filters (:obj:`Filters`): - Pass one or more filters to allow only a subset of messages to be passed - in your callback function. - - Other parameters: - client (:obj:`Client`): - The Client itself, useful when you want to call other API methods inside the user status handler. - - user_status (:obj:`UserStatus`): - The received UserStatus update. - """ - - def __init__(self, callback: callable, filters=None): - super().__init__(callback, filters) diff --git a/pyrogram/client/methods/__init__.py b/pyrogram/client/methods/__init__.py deleted file mode 100644 index 625ec09a4c7..00000000000 --- a/pyrogram/client/methods/__init__.py +++ /dev/null @@ -1,37 +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 .bots import Bots -from .chats import Chats -from .contacts import Contacts -from .decorators import Decorators -from .messages import Messages -from .password import Password -from .users import Users - - -class Methods( - Bots, - Contacts, - Password, - Chats, - Users, - Messages, - Decorators -): - pass diff --git a/pyrogram/client/methods/bots/__init__.py b/pyrogram/client/methods/bots/__init__.py deleted file mode 100644 index 916a62dc351..00000000000 --- a/pyrogram/client/methods/bots/__init__.py +++ /dev/null @@ -1,39 +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 .answer_callback_query import AnswerCallbackQuery -from .answer_inline_query import AnswerInlineQuery -from .get_game_high_scores import GetGameHighScores -from .get_inline_bot_results import GetInlineBotResults -from .request_callback_answer import RequestCallbackAnswer -from .send_game import SendGame -from .send_inline_bot_result import SendInlineBotResult -from .set_game_score import SetGameScore - - -class Bots( - AnswerCallbackQuery, - AnswerInlineQuery, - GetInlineBotResults, - RequestCallbackAnswer, - SendInlineBotResult, - SendGame, - SetGameScore, - GetGameHighScores -): - pass diff --git a/pyrogram/client/methods/bots/answer_callback_query.py b/pyrogram/client/methods/bots/answer_callback_query.py deleted file mode 100644 index 12effe47de9..00000000000 --- a/pyrogram/client/methods/bots/answer_callback_query.py +++ /dev/null @@ -1,70 +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 pyrogram.api import functions -from pyrogram.client.ext import BaseClient - - -class AnswerCallbackQuery(BaseClient): - def answer_callback_query( - self, - callback_query_id: str, - text: str = None, - show_alert: bool = None, - url: str = None, - cache_time: int = 0 - ): - """Use this method to send answers to callback queries sent from inline keyboards. - The answer will be displayed to the user as a notification at the top of the chat screen or as an alert. - - Parameters: - callback_query_id (``str``): - Unique identifier for the query to be answered. - - text (``str``): - Text of the notification. If not specified, nothing will be shown to the user, 0-200 characters. - - show_alert (``bool``): - If true, an alert will be shown by the client instead of a notification at the top of the chat screen. - Defaults to False. - - url (``str``): - URL that will be opened by the user's client. - If you have created a Game and accepted the conditions via @Botfather, specify the URL that opens your - game – note that this will only work if the query comes from a callback_game button. - Otherwise, you may use links like t.me/your_bot?start=XXXX that open your bot with a parameter. - - cache_time (``int``): - The maximum amount of time in seconds that the result of the callback query may be cached client-side. - Telegram apps will support caching starting in version 3.14. Defaults to 0. - - Returns: - ``bool``: True, on success. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - return self.send( - functions.messages.SetBotCallbackAnswer( - query_id=int(callback_query_id), - cache_time=cache_time, - alert=show_alert or None, - message=text, - url=url - ) - ) diff --git a/pyrogram/client/methods/bots/get_game_high_scores.py b/pyrogram/client/methods/bots/get_game_high_scores.py deleted file mode 100644 index 64901fea4e8..00000000000 --- a/pyrogram/client/methods/bots/get_game_high_scores.py +++ /dev/null @@ -1,68 +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 typing import Union - -import pyrogram -from pyrogram.api import functions -from pyrogram.client.ext import BaseClient - - -class GetGameHighScores(BaseClient): - def get_game_high_scores( - self, - user_id: Union[int, str], - chat_id: Union[int, str], - message_id: int = None - ) -> "pyrogram.GameHighScores": - """Use this method to get data for high score tables. - - Parameters: - user_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - chat_id (``int`` | ``str``, *optional*): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - Required if inline_message_id is not specified. - - message_id (``int``, *optional*): - Identifier of the sent message. - Required if inline_message_id is not specified. - - Returns: - :obj:`GameHighScores`: On success. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - # TODO: inline_message_id - - return pyrogram.GameHighScores._parse( - self, - self.send( - functions.messages.GetGameHighScores( - peer=self.resolve_peer(chat_id), - id=message_id, - user_id=self.resolve_peer(user_id) - ) - ) - ) diff --git a/pyrogram/client/methods/bots/get_inline_bot_results.py b/pyrogram/client/methods/bots/get_inline_bot_results.py deleted file mode 100644 index 14628b6429f..00000000000 --- a/pyrogram/client/methods/bots/get_inline_bot_results.py +++ /dev/null @@ -1,84 +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 typing import Union - -from pyrogram.api import functions, types -from pyrogram.errors import UnknownError -from pyrogram.client.ext import BaseClient - - -class GetInlineBotResults(BaseClient): - def get_inline_bot_results( - self, - bot: Union[int, str], - query: str, - offset: str = "", - latitude: float = None, - longitude: float = None - ): - """Use this method to get bot results via inline queries. - You can then send a result using :obj:`send_inline_bot_result ` - - Parameters: - bot (``int`` | ``str``): - Unique identifier of the inline bot you want to get results from. You can specify - a @username (str) or a bot ID (int). - - query (``str``): - Text of the query (up to 512 characters). - - offset (``str``, *optional*): - Offset of the results to be returned. - - latitude (``float``, *optional*): - Latitude of the location. - Useful for location-based results only. - - longitude (``float``, *optional*): - Longitude of the location. - Useful for location-based results only. - - Returns: - :obj:`BotResults `: On Success. - - Raises: - RPCError: In case of a Telegram RPC error. - TimeoutError: In case the bot fails to answer within 10 seconds. - """ - # TODO: Don't return the raw type - - try: - return self.send( - functions.messages.GetInlineBotResults( - bot=self.resolve_peer(bot), - peer=types.InputPeerSelf(), - query=query, - offset=offset, - geo_point=types.InputGeoPoint( - lat=latitude, - long=longitude - ) if (latitude is not None and longitude is not None) else None - ) - ) - except UnknownError as e: - # TODO: Add this -503 Timeout error into the Error DB - if e.x.error_code == -503 and e.x.error_message == "Timeout": - raise TimeoutError("The inline bot didn't answer in time") from None - else: - raise e diff --git a/pyrogram/client/methods/bots/request_callback_answer.py b/pyrogram/client/methods/bots/request_callback_answer.py deleted file mode 100644 index 7e57b39b1d7..00000000000 --- a/pyrogram/client/methods/bots/request_callback_answer.py +++ /dev/null @@ -1,67 +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 typing import Union - -from pyrogram.api import functions -from pyrogram.client.ext import BaseClient - - -class RequestCallbackAnswer(BaseClient): - def request_callback_answer( - self, - chat_id: Union[int, str], - message_id: int, - callback_data: bytes, - timeout: int = 10 - ): - """Use this method to request a callback answer from bots. - This is the equivalent of clicking an inline button containing callback data. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - message_id (``int``): - The message id the inline keyboard is attached on. - - callback_data (``bytes``): - Callback data associated with the inline button you want to get the answer from. - - timeout (``int``, *optional*): - Timeout in seconds. - - Returns: - The answer containing info useful for clients to display a notification at the top of the chat screen - or as an alert. - - Raises: - RPCError: In case of a Telegram RPC error. - TimeoutError: In case the bot fails to answer within 10 seconds. - """ - return self.send( - functions.messages.GetBotCallbackAnswer( - peer=self.resolve_peer(chat_id), - msg_id=message_id, - data=callback_data - ), - retries=0, - timeout=timeout - ) diff --git a/pyrogram/client/methods/bots/send_game.py b/pyrogram/client/methods/bots/send_game.py deleted file mode 100644 index 03593a5669b..00000000000 --- a/pyrogram/client/methods/bots/send_game.py +++ /dev/null @@ -1,91 +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 typing import Union - -import pyrogram -from pyrogram.api import functions, types -from pyrogram.client.ext import BaseClient - - -class SendGame(BaseClient): - def send_game( - self, - chat_id: Union[int, str], - game_short_name: str, - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union[ - "pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply" - ] = None - ) -> "pyrogram.Message": - """Use this method to send a game. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - game_short_name (``str``): - Short name of the game, serves as the unique identifier for the game. Set up your games via Botfather. - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - reply_to_message_id (``int``, *optional*): - If the message is a reply, ID of the original message. - - reply_markup (:obj:`InlineKeyboardMarkup`, *optional*): - An object for an inline keyboard. If empty, one ‘Play game_title’ button will be shown automatically. - If not empty, the first button must launch the game. - - Returns: - :obj:`Message`: On success, the sent game message is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - r = self.send( - functions.messages.SendMedia( - peer=self.resolve_peer(chat_id), - media=types.InputMediaGame( - id=types.InputGameShortName( - bot_id=types.InputUserSelf(), - short_name=game_short_name - ), - ), - message="", - silent=disable_notification or None, - reply_to_msg_id=reply_to_message_id, - random_id=self.rnd_id(), - reply_markup=reply_markup.write() if reply_markup else None - ) - ) - - for i in r.updates: - if isinstance(i, (types.UpdateNewMessage, types.UpdateNewChannelMessage)): - return pyrogram.Message._parse( - self, i.message, - {i.id: i for i in r.users}, - {i.id: i for i in r.chats} - ) diff --git a/pyrogram/client/methods/bots/send_inline_bot_result.py b/pyrogram/client/methods/bots/send_inline_bot_result.py deleted file mode 100644 index 49dc11ee0f6..00000000000 --- a/pyrogram/client/methods/bots/send_inline_bot_result.py +++ /dev/null @@ -1,76 +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 typing import Union - -from pyrogram.api import functions -from pyrogram.client.ext import BaseClient - - -class SendInlineBotResult(BaseClient): - def send_inline_bot_result( - self, - chat_id: Union[int, str], - query_id: int, - result_id: str, - disable_notification: bool = None, - reply_to_message_id: int = None, - hide_via: bool = None - ): - """Use this method to send an inline bot result. - Bot results can be retrieved using :obj:`get_inline_bot_results ` - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - query_id (``int``): - Unique identifier for the answered query. - - result_id (``str``): - Unique identifier for the result that was chosen. - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - reply_to_message_id (``bool``, *optional*): - If the message is a reply, ID of the original message. - - hide_via (``bool``): - Sends the message with *via @bot* hidden. - - Returns: - On success, the sent Message is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - return self.send( - functions.messages.SendInlineBotResult( - peer=self.resolve_peer(chat_id), - query_id=query_id, - id=result_id, - random_id=self.rnd_id(), - silent=disable_notification or None, - reply_to_msg_id=reply_to_message_id, - hide_via=hide_via or None - ) - ) diff --git a/pyrogram/client/methods/chats/__init__.py b/pyrogram/client/methods/chats/__init__.py deleted file mode 100644 index 8db44abec60..00000000000 --- a/pyrogram/client/methods/chats/__init__.py +++ /dev/null @@ -1,71 +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 .delete_chat_photo import DeleteChatPhoto -from .export_chat_invite_link import ExportChatInviteLink -from .get_chat import GetChat -from .get_chat_member import GetChatMember -from .get_chat_members import GetChatMembers -from .get_chat_members_count import GetChatMembersCount -from .get_chat_preview import GetChatPreview -from .get_dialogs import GetDialogs -from .iter_chat_members import IterChatMembers -from .iter_dialogs import IterDialogs -from .join_chat import JoinChat -from .kick_chat_member import KickChatMember -from .leave_chat import LeaveChat -from .pin_chat_message import PinChatMessage -from .promote_chat_member import PromoteChatMember -from .restrict_chat import RestrictChat -from .restrict_chat_member import RestrictChatMember -from .set_chat_description import SetChatDescription -from .set_chat_photo import SetChatPhoto -from .set_chat_title import SetChatTitle -from .unban_chat_member import UnbanChatMember -from .unpin_chat_message import UnpinChatMessage -from .update_chat_username import UpdateChatUsername -from .get_dialogs_count import GetDialogsCount - - -class Chats( - GetChat, - ExportChatInviteLink, - LeaveChat, - JoinChat, - KickChatMember, - UnbanChatMember, - RestrictChatMember, - PromoteChatMember, - GetChatMembers, - GetChatMember, - SetChatPhoto, - DeleteChatPhoto, - SetChatTitle, - SetChatDescription, - PinChatMessage, - UnpinChatMessage, - GetDialogs, - GetChatMembersCount, - GetChatPreview, - IterDialogs, - IterChatMembers, - UpdateChatUsername, - RestrictChat, - GetDialogsCount -): - pass diff --git a/pyrogram/client/methods/chats/delete_chat_photo.py b/pyrogram/client/methods/chats/delete_chat_photo.py deleted file mode 100644 index f45a7dc2efd..00000000000 --- a/pyrogram/client/methods/chats/delete_chat_photo.py +++ /dev/null @@ -1,68 +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 typing import Union - -from pyrogram.api import functions, types -from ...ext import BaseClient - - -class DeleteChatPhoto(BaseClient): - def delete_chat_photo( - self, - chat_id: Union[int, str] - ) -> bool: - """Use this method to delete a chat photo. - Photos can't be changed for private chats. - You must be an administrator in the chat for this to work and must have the appropriate admin rights. - - Note: - In regular groups (non-supergroups), this method will only work if the "All Members Are Admins" - setting is off. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - - Returns: - ``bool``: True on success. - - Raises: - RPCError: In case of a Telegram RPC error. - ``ValueError`` if a chat_id belongs to user. - """ - peer = self.resolve_peer(chat_id) - - if isinstance(peer, types.InputPeerChat): - self.send( - functions.messages.EditChatPhoto( - chat_id=peer.chat_id, - photo=types.InputChatPhotoEmpty() - ) - ) - elif isinstance(peer, types.InputPeerChannel): - self.send( - functions.channels.EditPhoto( - channel=peer, - photo=types.InputChatPhotoEmpty() - ) - ) - else: - raise ValueError("The chat_id \"{}\" belongs to a user".format(chat_id)) - - return True diff --git a/pyrogram/client/methods/chats/export_chat_invite_link.py b/pyrogram/client/methods/chats/export_chat_invite_link.py deleted file mode 100644 index e1ca27c232d..00000000000 --- a/pyrogram/client/methods/chats/export_chat_invite_link.py +++ /dev/null @@ -1,65 +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 typing import Union - -from pyrogram.api import functions, types -from ...ext import BaseClient - - -class ExportChatInviteLink(BaseClient): - def export_chat_invite_link( - self, - chat_id: Union[int, str] - ) -> str: - """Use this method to generate a new invite link for a chat; any previously generated link is revoked. - - You must be an administrator in the chat for this to work and have the appropriate admin rights. - - .. note :: - - Each administrator in a chat generates their own invite links. Bots can't use invite links generated by - other administrators. If you want your bot to work with invite links, it will need to generate its own link - using this method – after this the link will become available to the bot via the :meth:`get_chat` method. - If your bot needs to generate a new invite link replacing its previous one, use this method again. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier for the target chat or username of the target channel/supergroup - (in the format @username). - - Returns: - ``str``: On success, the exported invite link is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - peer = self.resolve_peer(chat_id) - - if isinstance(peer, types.InputPeerChat): - return self.send( - functions.messages.ExportChatInvite( - peer=peer.chat_id - ) - ).link - elif isinstance(peer, types.InputPeerChannel): - return self.send( - functions.channels.ExportInvite( - channel=peer - ) - ).link diff --git a/pyrogram/client/methods/chats/get_chat.py b/pyrogram/client/methods/chats/get_chat.py deleted file mode 100644 index bf4c4ceaab5..00000000000 --- a/pyrogram/client/methods/chats/get_chat.py +++ /dev/null @@ -1,79 +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 typing import Union - -import pyrogram -from pyrogram.api import functions, types -from ...ext import BaseClient - - -class GetChat(BaseClient): - def get_chat( - self, - chat_id: Union[int, str] - ) -> "pyrogram.Chat": - """Use this method to get up to date information about the chat. - Information include current name of the user for one-on-one conversations, current username of a user, group or - channel, etc. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - Unique identifier for the target chat in form of a *t.me/joinchat/* link, identifier (int) or username - of the target channel/supergroup (in the format @username). - - Returns: - :obj:`Chat`: On success, a chat object is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - ValueError: In case the chat invite link points to a chat you haven't joined yet. - """ - match = self.INVITE_LINK_RE.match(str(chat_id)) - - if match: - h = match.group(1) - - r = self.send( - functions.messages.CheckChatInvite( - hash=h - ) - ) - - if isinstance(r, types.ChatInvite): - raise ValueError("You haven't joined \"t.me/joinchat/{}\" yet".format(h)) - - self.fetch_peers([r.chat]) - - if isinstance(r.chat, types.Chat): - chat_id = -r.chat.id - - if isinstance(r.chat, types.Channel): - chat_id = int("-100" + str(r.chat.id)) - - peer = self.resolve_peer(chat_id) - - if isinstance(peer, types.InputPeerChannel): - r = self.send(functions.channels.GetFullChannel(channel=peer)) - elif isinstance(peer, (types.InputPeerUser, types.InputPeerSelf)): - r = self.send(functions.users.GetFullUser(id=peer)) - else: - r = self.send(functions.messages.GetFullChat(chat_id=peer.chat_id)) - - return pyrogram.Chat._parse_full(self, r) diff --git a/pyrogram/client/methods/chats/get_chat_member.py b/pyrogram/client/methods/chats/get_chat_member.py deleted file mode 100644 index b625e32f0c5..00000000000 --- a/pyrogram/client/methods/chats/get_chat_member.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 typing import Union - -import pyrogram -from pyrogram.api import functions, types -from pyrogram.errors import UserNotParticipant -from ...ext import BaseClient - - -class GetChatMember(BaseClient): - def get_chat_member( - self, - chat_id: Union[int, str], - user_id: Union[int, str] - ) -> "pyrogram.ChatMember": - """Use this method to get information about one member of a chat. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - - user_id (``int`` | ``str``):: - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - Returns: - :obj:`ChatMember`: On success, a chat member is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - chat_id = self.resolve_peer(chat_id) - user_id = self.resolve_peer(user_id) - - if isinstance(chat_id, types.InputPeerChat): - full_chat = self.send( - functions.messages.GetFullChat( - chat_id=chat_id.chat_id - ) - ) - - for member in pyrogram.ChatMembers._parse(self, full_chat).chat_members: - if member.user.is_self: - return member - else: - raise UserNotParticipant - elif isinstance(chat_id, types.InputPeerChannel): - r = self.send( - functions.channels.GetParticipant( - channel=chat_id, - user_id=user_id - ) - ) - - users = {i.id: i for i in r.users} - - return pyrogram.ChatMember._parse(self, r.participant, users) - else: - raise ValueError("The chat_id \"{}\" belongs to a user".format(chat_id)) diff --git a/pyrogram/client/methods/chats/get_chat_members.py b/pyrogram/client/methods/chats/get_chat_members.py deleted file mode 100644 index 79001614bf0..00000000000 --- a/pyrogram/client/methods/chats/get_chat_members.py +++ /dev/null @@ -1,142 +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 . - -import logging -import time -from typing import Union - -import pyrogram -from pyrogram.api import functions, types -from pyrogram.errors import FloodWait -from ...ext import BaseClient - -log = logging.getLogger(__name__) - - -class Filters: - ALL = "all" - KICKED = "kicked" - RESTRICTED = "restricted" - BOTS = "bots" - RECENT = "recent" - ADMINISTRATORS = "administrators" - - -class GetChatMembers(BaseClient): - def get_chat_members( - self, - chat_id: Union[int, str], - offset: int = 0, - limit: int = 200, - query: str = "", - filter: str = Filters.ALL - ) -> "pyrogram.ChatMembers": - """Use this method to get a chunk of the members list of a chat. - - You can get up to 200 chat members at once. - A chat can be either a basic group, a supergroup or a channel. - You must be admin to retrieve the members list of a channel (also known as "subscribers"). - For a more convenient way of getting chat members see :meth:`iter_chat_members`. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - - offset (``int``, *optional*): - Sequential number of the first member to be returned. - Defaults to 0 [1]_. - - limit (``int``, *optional*): - Limits the number of members to be retrieved. - Defaults to 200, which is also the maximum server limit allowed per method call. - - query (``str``, *optional*): - Query string to filter members based on their display names and usernames. - Defaults to "" (empty string) [2]_. - - filter (``str``, *optional*): - Filter used to select the kind of members you want to retrieve. Only applicable for supergroups - and channels. It can be any of the followings: - *"all"* - all kind of members, - *"kicked"* - kicked (banned) members only, - *"restricted"* - restricted members only, - *"bots"* - bots only, - *"recent"* - recent members only, - *"administrators"* - chat administrators only. - Defaults to *"all"*. - - .. [1] Server limit: on supergroups, you can get up to 10,000 members for a single query and up to 200 members - on channels. - - .. [2] A query string is applicable only for *"all"*, *"kicked"* and *"restricted"* filters only. - - Returns: - :obj:`ChatMembers`: On success, an object containing a list of chat members is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - ValueError: In case you used an invalid filter or a chat id that belongs to a user. - """ - peer = self.resolve_peer(chat_id) - - if isinstance(peer, types.InputPeerChat): - return pyrogram.ChatMembers._parse( - self, - self.send( - functions.messages.GetFullChat( - chat_id=peer.chat_id - ) - ) - ) - elif isinstance(peer, types.InputPeerChannel): - filter = filter.lower() - - if filter == Filters.ALL: - filter = types.ChannelParticipantsSearch(q=query) - elif filter == Filters.KICKED: - filter = types.ChannelParticipantsKicked(q=query) - elif filter == Filters.RESTRICTED: - filter = types.ChannelParticipantsBanned(q=query) - elif filter == Filters.BOTS: - filter = types.ChannelParticipantsBots() - elif filter == Filters.RECENT: - filter = types.ChannelParticipantsRecent() - elif filter == Filters.ADMINISTRATORS: - filter = types.ChannelParticipantsAdmins() - else: - raise ValueError("Invalid filter \"{}\"".format(filter)) - - while True: - try: - return pyrogram.ChatMembers._parse( - self, - self.send( - functions.channels.GetParticipants( - channel=peer, - filter=filter, - offset=offset, - limit=limit, - hash=0 - ) - ) - ) - except FloodWait as e: - log.warning("Sleeping for {}s".format(e.x)) - time.sleep(e.x) - else: - raise ValueError("The chat_id \"{}\" belongs to a user".format(chat_id)) diff --git a/pyrogram/client/methods/chats/get_chat_members_count.py b/pyrogram/client/methods/chats/get_chat_members_count.py deleted file mode 100644 index d40585f53d4..00000000000 --- a/pyrogram/client/methods/chats/get_chat_members_count.py +++ /dev/null @@ -1,58 +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 typing import Union - -from pyrogram.api import functions, types -from ...ext import BaseClient - - -class GetChatMembersCount(BaseClient): - def get_chat_members_count( - self, - chat_id: Union[int, str] - ) -> int: - """Use this method to get the number of members in a chat. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - - Returns: - On success, an integer is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - ValueError: In case a chat id belongs to user. - """ - peer = self.resolve_peer(chat_id) - - if isinstance(peer, types.InputPeerChat): - return self.send( - functions.messages.GetChats( - id=[peer.chat_id] - ) - ).chats[0].participants_count - elif isinstance(peer, types.InputPeerChannel): - return self.send( - functions.channels.GetFullChannel( - channel=peer - ) - ).full_chat.participants_count - else: - raise ValueError("The chat_id \"{}\" belongs to a user".format(chat_id)) diff --git a/pyrogram/client/methods/chats/get_chat_preview.py b/pyrogram/client/methods/chats/get_chat_preview.py deleted file mode 100644 index fd00c3740d1..00000000000 --- a/pyrogram/client/methods/chats/get_chat_preview.py +++ /dev/null @@ -1,67 +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 . - -import pyrogram -from pyrogram.api import functions, types -from ...ext import BaseClient - - -class GetChatPreview(BaseClient): - def get_chat_preview( - self, - invite_link: str - ): - """Use this method to get the preview of a chat using the invite link. - - This method only returns a chat preview, if you want to join a chat use :meth:`join_chat` - - Parameters: - invite_link (``str``): - Unique identifier for the target chat in form of *t.me/joinchat/* links. - - Returns: - :obj:`Chat`: In case you already joined the chat. - - :obj:`ChatPreview` -- In case you haven't joined the chat yet. - - Raises: - RPCError: In case of a Telegram RPC error. - ValueError: In case of an invalid invite link. - """ - match = self.INVITE_LINK_RE.match(invite_link) - - if match: - r = self.send( - functions.messages.CheckChatInvite( - hash=match.group(1) - ) - ) - - if isinstance(r, types.ChatInvite): - return pyrogram.ChatPreview._parse(self, r) - - if isinstance(r, types.ChatInviteAlready): - chat = r.chat - - if isinstance(chat, types.Chat): - return pyrogram.Chat._parse_chat_chat(self, chat) - - if isinstance(chat, types.Channel): - return pyrogram.Chat._parse_channel_chat(self, chat) - else: - raise ValueError("The invite_link is invalid") diff --git a/pyrogram/client/methods/chats/get_dialogs.py b/pyrogram/client/methods/chats/get_dialogs.py deleted file mode 100644 index 83732d0708b..00000000000 --- a/pyrogram/client/methods/chats/get_dialogs.py +++ /dev/null @@ -1,83 +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 . - -import logging -import time - -import pyrogram -from pyrogram.api import functions, types -from pyrogram.errors import FloodWait -from ...ext import BaseClient - -log = logging.getLogger(__name__) - - -class GetDialogs(BaseClient): - def get_dialogs( - self, - offset_date: int = 0, - limit: int = 100, - pinned_only: bool = False - ) -> "pyrogram.Dialogs": - """Use this method to get a chunk of the user's dialogs. - - You can get up to 100 dialogs at once. - For a more convenient way of getting a user's dialogs see :meth:`iter_dialogs`. - - Parameters: - offset_date (``int``): - The offset date in Unix time taken from the top message of a :obj:`Dialog`. - Defaults to 0. Valid for non-pinned dialogs only. - - limit (``str``, *optional*): - Limits the number of dialogs to be retrieved. - Defaults to 100. Valid for non-pinned dialogs only. - - pinned_only (``bool``, *optional*): - Pass True if you want to get only pinned dialogs. - Defaults to False. - - Returns: - :obj:`Dialogs`: On success, an object containing a list of dialogs is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - - while True: - try: - if pinned_only: - r = self.send(functions.messages.GetPinnedDialogs()) - else: - r = self.send( - functions.messages.GetDialogs( - offset_date=offset_date, - offset_id=0, - offset_peer=types.InputPeerEmpty(), - limit=limit, - hash=0, - exclude_pinned=True - ) - ) - except FloodWait as e: - log.warning("Sleeping {}s".format(e.x)) - time.sleep(e.x) - else: - break - - return pyrogram.Dialogs._parse(self, r) diff --git a/pyrogram/client/methods/chats/get_dialogs_count.py b/pyrogram/client/methods/chats/get_dialogs_count.py deleted file mode 100644 index eea327da29a..00000000000 --- a/pyrogram/client/methods/chats/get_dialogs_count.py +++ /dev/null @@ -1,54 +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 pyrogram.api import functions, types -from ...ext import BaseClient - - -class GetDialogsCount(BaseClient): - def get_dialogs_count(self, pinned_only: bool = False) -> int: - """Use this method to get the total count of your dialogs. - - pinned_only (``bool``, *optional*): - Pass True if you want to count only pinned dialogs. - Defaults to False. - - Returns: - ``int``: On success, an integer is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - - if pinned_only: - return len(self.send(functions.messages.GetPinnedDialogs()).dialogs) - else: - r = self.send( - functions.messages.GetDialogs( - offset_date=0, - offset_id=0, - offset_peer=types.InputPeerEmpty(), - limit=1, - hash=0 - ) - ) - - if isinstance(r, types.messages.Dialogs): - return len(r.dialogs) - else: - return r.count diff --git a/pyrogram/client/methods/chats/iter_chat_members.py b/pyrogram/client/methods/chats/iter_chat_members.py deleted file mode 100644 index f735da482fe..00000000000 --- a/pyrogram/client/methods/chats/iter_chat_members.py +++ /dev/null @@ -1,132 +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 string import ascii_lowercase -from typing import Union, Generator - -import pyrogram -from pyrogram.api import types -from ...ext import BaseClient - - -class Filters: - ALL = "all" - KICKED = "kicked" - RESTRICTED = "restricted" - BOTS = "bots" - RECENT = "recent" - ADMINISTRATORS = "administrators" - - -QUERIES = [""] + [str(i) for i in range(10)] + list(ascii_lowercase) -QUERYABLE_FILTERS = (Filters.ALL, Filters.KICKED, Filters.RESTRICTED) - - -class IterChatMembers(BaseClient): - def iter_chat_members( - self, - chat_id: Union[int, str], - limit: int = 0, - query: str = "", - filter: str = Filters.ALL - ) -> Generator["pyrogram.ChatMember", None, None]: - """Use this method to iterate through the members of a chat sequentially. - - This convenience method does the same as repeatedly calling :meth:`get_chat_members` in a loop, thus saving you - from the hassle of setting up boilerplate code. It is useful for getting the whole members list of a chat with - a single call. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - - limit (``int``, *optional*): - Limits the number of members to be retrieved. - By default, no limit is applied and all members are returned. - - query (``str``, *optional*): - Query string to filter members based on their display names and usernames. - Defaults to "" (empty string). - - filter (``str``, *optional*): - Filter used to select the kind of members you want to retrieve. Only applicable for supergroups - and channels. It can be any of the followings: - *"all"* - all kind of members, - *"kicked"* - kicked (banned) members only, - *"restricted"* - restricted members only, - *"bots"* - bots only, - *"recent"* - recent members only, - *"administrators"* - chat administrators only. - Defaults to *"all"*. - - Returns: - ``Generator``: A generator yielding :obj:`ChatMember` objects. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - current = 0 - yielded = set() - queries = [query] if query else QUERIES - total = limit or (1 << 31) - 1 - limit = min(200, total) - resolved_chat_id = self.resolve_peer(chat_id) - - filter = ( - Filters.RECENT - if self.get_chat_members_count(chat_id) <= 10000 and filter == Filters.ALL - else filter - ) - - if filter not in QUERYABLE_FILTERS: - queries = [""] - - for q in queries: - offset = 0 - - while True: - chat_members = self.get_chat_members( - chat_id=chat_id, - offset=offset, - limit=limit, - query=q, - filter=filter - ).chat_members - - if not chat_members: - break - - if isinstance(resolved_chat_id, types.InputPeerChat): - total = len(chat_members) - - offset += len(chat_members) - - for chat_member in chat_members: - user_id = chat_member.user.id - - if user_id in yielded: - continue - - yield chat_member - - yielded.add(chat_member.user.id) - - current += 1 - - if current >= total: - return diff --git a/pyrogram/client/methods/chats/iter_dialogs.py b/pyrogram/client/methods/chats/iter_dialogs.py deleted file mode 100644 index 6fc9f81c9fb..00000000000 --- a/pyrogram/client/methods/chats/iter_dialogs.py +++ /dev/null @@ -1,84 +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 typing import Generator - -import pyrogram -from ...ext import BaseClient - - -class IterDialogs(BaseClient): - def iter_dialogs( - self, - offset_date: int = 0, - limit: int = 0 - ) -> Generator["pyrogram.Dialog", None, None]: - """Use this method to iterate through a user's dialogs sequentially. - - This convenience method does the same as repeatedly calling :meth:`get_dialogs` in a loop, thus saving you from - the hassle of setting up boilerplate code. It is useful for getting the whole dialogs list with a single call. - - Parameters: - offset_date (``int``): - The offset date in Unix time taken from the top message of a :obj:`Dialog`. - Defaults to 0 (most recent dialog). - - limit (``str``, *optional*): - Limits the number of dialogs to be retrieved. - By default, no limit is applied and all dialogs are returned. - - Returns: - ``Generator``: A generator yielding :obj:`Dialog` objects. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - current = 0 - total = limit or (1 << 31) - 1 - limit = min(100, total) - - pinned_dialogs = self.get_dialogs( - pinned_only=True - ).dialogs - - for dialog in pinned_dialogs: - yield dialog - - current += 1 - - if current >= total: - return - - while True: - dialogs = self.get_dialogs( - offset_date=offset_date, - limit=limit - ).dialogs - - if not dialogs: - return - - offset_date = dialogs[-1].top_message.date - - for dialog in dialogs: - yield dialog - - current += 1 - - if current >= total: - return diff --git a/pyrogram/client/methods/chats/join_chat.py b/pyrogram/client/methods/chats/join_chat.py deleted file mode 100644 index 77b2ee50d10..00000000000 --- a/pyrogram/client/methods/chats/join_chat.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 . - -import pyrogram -from pyrogram.api import functions, types -from ...ext import BaseClient - - -class JoinChat(BaseClient): - def join_chat( - self, - chat_id: str - ): - """Use this method to join a group chat or channel. - - Parameters: - chat_id (``str``): - Unique identifier for the target chat in form of a *t.me/joinchat/* link or username of the target - channel/supergroup (in the format @username). - - Returns: - :obj:`Chat`: On success, a chat object is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - match = self.INVITE_LINK_RE.match(chat_id) - - if match: - chat = self.send( - functions.messages.ImportChatInvite( - hash=match.group(1) - ) - ) - if isinstance(chat.chats[0], types.Chat): - return pyrogram.Chat._parse_chat_chat(self, chat.chats[0]) - elif isinstance(chat.chats[0], types.Channel): - return pyrogram.Chat._parse_channel_chat(self, chat.chats[0]) - else: - resolved_peer = self.send( - functions.contacts.ResolveUsername( - username=chat_id.lower().strip("@") - ) - ) - - channel = types.InputPeerChannel( - channel_id=resolved_peer.chats[0].id, - access_hash=resolved_peer.chats[0].access_hash - ) - - chat = self.send( - functions.channels.JoinChannel( - channel=channel - ) - ) - - return pyrogram.Chat._parse_channel_chat(self, chat.chats[0]) diff --git a/pyrogram/client/methods/chats/kick_chat_member.py b/pyrogram/client/methods/chats/kick_chat_member.py deleted file mode 100644 index dbd095ad817..00000000000 --- a/pyrogram/client/methods/chats/kick_chat_member.py +++ /dev/null @@ -1,101 +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 typing import Union - -import pyrogram -from pyrogram.api import functions, types -from ...ext import BaseClient - - -class KickChatMember(BaseClient): - def kick_chat_member( - self, - chat_id: Union[int, str], - user_id: Union[int, str], - until_date: int = 0 - ) -> Union["pyrogram.Message", bool]: - """Use this method to kick a user from a group, a supergroup or a channel. - In the case of supergroups and channels, the user will not be able to return to the group on their own using - invite links, etc., unless unbanned first. You must be an administrator in the chat for this to work and must - have the appropriate admin rights. - - Note: - In regular groups (non-supergroups), this method will only work if the "All Members Are Admins" setting is - off in the target group. Otherwise members may only be removed by the group's creator or by the member - that added them. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - - user_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target user. - For a contact that exists in your Telegram address book you can use his phone number (str). - - until_date (``int``, *optional*): - Date when the user will be unbanned, unix time. - If user is banned for more than 366 days or less than 30 seconds from the current time they are - considered to be banned forever. Defaults to 0 (ban forever). - - Returns: - :obj:`Message`: On success, a service message will be returned (when applicable). - - ``bool`` -- True, in case a message object couldn't be returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - chat_peer = self.resolve_peer(chat_id) - user_peer = self.resolve_peer(user_id) - - if isinstance(chat_peer, types.InputPeerChannel): - r = self.send( - functions.channels.EditBanned( - channel=chat_peer, - user_id=user_peer, - banned_rights=types.ChatBannedRights( - until_date=until_date, - view_messages=True, - send_messages=True, - send_media=True, - send_stickers=True, - send_gifs=True, - send_games=True, - send_inline=True, - embed_links=True - ) - ) - ) - else: - r = self.send( - functions.messages.DeleteChatUser( - chat_id=abs(chat_id), - user_id=user_peer - ) - ) - - for i in r.updates: - if isinstance(i, (types.UpdateNewMessage, types.UpdateNewChannelMessage)): - return pyrogram.Message._parse( - self, i.message, - {i.id: i for i in r.users}, - {i.id: i for i in r.chats} - ) - else: - return True diff --git a/pyrogram/client/methods/chats/leave_chat.py b/pyrogram/client/methods/chats/leave_chat.py deleted file mode 100644 index 57cc1090d36..00000000000 --- a/pyrogram/client/methods/chats/leave_chat.py +++ /dev/null @@ -1,68 +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 typing import Union - -from pyrogram.api import functions, types -from ...ext import BaseClient - - -class LeaveChat(BaseClient): - def leave_chat( - self, - chat_id: Union[int, str], - delete: bool = False - ): - """Use this method to leave a group chat or channel. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier for the target chat or username of the target channel/supergroup - (in the format @username). - - delete (``bool``, *optional*): - Deletes the group chat dialog after leaving (for simple group chats, not supergroups). - - Raises: - RPCError: In case of a Telegram RPC error. - """ - peer = self.resolve_peer(chat_id) - - if isinstance(peer, types.InputPeerChannel): - return self.send( - functions.channels.LeaveChannel( - channel=self.resolve_peer(chat_id) - ) - ) - elif isinstance(peer, types.InputPeerChat): - r = self.send( - functions.messages.DeleteChatUser( - chat_id=peer.chat_id, - user_id=types.InputPeerSelf() - ) - ) - - if delete: - self.send( - functions.messages.DeleteHistory( - peer=peer, - max_id=0 - ) - ) - - return r diff --git a/pyrogram/client/methods/chats/pin_chat_message.py b/pyrogram/client/methods/chats/pin_chat_message.py deleted file mode 100644 index 2c485dabe39..00000000000 --- a/pyrogram/client/methods/chats/pin_chat_message.py +++ /dev/null @@ -1,61 +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 typing import Union - -from pyrogram.api import functions -from ...ext import BaseClient - - -class PinChatMessage(BaseClient): - def pin_chat_message( - self, - chat_id: Union[int, str], - message_id: int, - disable_notification: bool = None - ) -> bool: - """Use this method to pin a message in a group, channel or your own chat. - You must be an administrator in the chat for this to work and must have the "can_pin_messages" admin right in - the supergroup or "can_edit_messages" admin right in the channel. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - - message_id (``int``): - Identifier of a message to pin. - - disable_notification (``bool``): - Pass True, if it is not necessary to send a notification to all chat members about the new pinned - message. Notifications are always disabled in channels. - - Returns: - ``bool``: True on success. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - self.send( - functions.messages.UpdatePinnedMessage( - peer=self.resolve_peer(chat_id), - id=message_id, - silent=disable_notification or None - ) - ) - - return True diff --git a/pyrogram/client/methods/chats/promote_chat_member.py b/pyrogram/client/methods/chats/promote_chat_member.py deleted file mode 100644 index 0b93576abe2..00000000000 --- a/pyrogram/client/methods/chats/promote_chat_member.py +++ /dev/null @@ -1,101 +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 typing import Union - -from pyrogram.api import functions, types -from ...ext import BaseClient - - -class PromoteChatMember(BaseClient): - def promote_chat_member( - self, - chat_id: Union[int, str], - user_id: Union[int, str], - can_change_info: bool = True, - can_post_messages: bool = False, - can_edit_messages: bool = False, - can_delete_messages: bool = True, - can_restrict_members: bool = True, - can_invite_users: bool = True, - can_pin_messages: bool = False, - can_promote_members: bool = False - ) -> bool: - """Use this method to promote or demote a user in a supergroup or a channel. - - You must be an administrator in the chat for this to work and must have the appropriate admin rights. - Pass False for all boolean parameters to demote a user. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - - user_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target user. - For a contact that exists in your Telegram address book you can use his phone number (str). - - can_change_info (``bool``, *optional*): - Pass True, if the administrator can change chat title, photo and other settings. - - can_post_messages (``bool``, *optional*): - Pass True, if the administrator can create channel posts, channels only. - - can_edit_messages (``bool``, *optional*): - Pass True, if the administrator can edit messages of other users and can pin messages, channels only. - - can_delete_messages (``bool``, *optional*): - Pass True, if the administrator can delete messages of other users. - - can_restrict_members (``bool``, *optional*): - Pass True, if the administrator can restrict, ban or unban chat members. - - can_invite_users (``bool``, *optional*): - Pass True, if the administrator can invite new users to the chat. - - can_pin_messages (``bool``, *optional*): - Pass True, if the administrator can pin messages, supergroups only. - - can_promote_members (``bool``, *optional*): - Pass True, if the administrator can add new administrators with a subset of his own privileges or - demote administrators that he has promoted, directly or indirectly (promoted by administrators that - were appointed by him). - - Returns: - ``bool``: True on success. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - self.send( - functions.channels.EditAdmin( - channel=self.resolve_peer(chat_id), - user_id=self.resolve_peer(user_id), - admin_rights=types.ChatAdminRights( - change_info=can_change_info or None, - post_messages=can_post_messages or None, - edit_messages=can_edit_messages or None, - delete_messages=can_delete_messages or None, - ban_users=can_restrict_members or None, - invite_users=can_invite_users or None, - pin_messages=can_pin_messages or None, - add_admins=can_promote_members or None, - ) - ) - ) - - return True diff --git a/pyrogram/client/methods/chats/restrict_chat.py b/pyrogram/client/methods/chats/restrict_chat.py deleted file mode 100644 index 9f6d291001f..00000000000 --- a/pyrogram/client/methods/chats/restrict_chat.py +++ /dev/null @@ -1,143 +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 typing import Union - -from pyrogram.api import functions, types -from ...ext import BaseClient -from ...types.user_and_chats import Chat - - -class RestrictChat(BaseClient): - def restrict_chat( - self, - chat_id: Union[int, str], - can_send_messages: bool = False, - can_send_media_messages: bool = False, - can_send_other_messages: bool = False, - can_add_web_page_previews: bool = False, - can_send_polls: bool = False, - can_change_info: bool = False, - can_invite_users: bool = False, - can_pin_messages: bool = False - ) -> Chat: - """Use this method to restrict a chat. - Pass True for all boolean parameters to lift restrictions from a chat. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - - can_send_messages (``bool``, *optional*): - Pass True, if the user can send text messages, contacts, locations and venues. - - can_send_media_messages (``bool``, *optional*): - Pass True, if the user can send audios, documents, photos, videos, video notes and voice notes, - implies can_send_messages. - - can_send_other_messages (``bool``, *optional*): - Pass True, if the user can send animations, games, stickers and use inline bots, - implies can_send_media_messages. - - can_add_web_page_previews (``bool``, *optional*): - Pass True, if the user may add web page previews to their messages, implies can_send_media_messages. - - can_send_polls (``bool``, *optional*): - Pass True, if the user can send polls, implies can_send_media_messages. - - can_change_info (``bool``, *optional*): - Pass True, if the user can change the chat title, photo and other settings. - - can_invite_users (``bool``, *optional*): - Pass True, if the user can invite new users to the chat. - - can_pin_messages (``bool``, *optional*): - Pass True, if the user can pin messages. - - Returns: - :obj:`Chat`: On success, a chat object is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - send_messages = True - send_media = True - send_stickers = True - send_gifs = True - send_games = True - send_inline = True - embed_links = True - send_polls = True - change_info = True - invite_users = True - pin_messages = True - - if can_send_messages: - send_messages = None - - if can_send_media_messages: - send_messages = None - send_media = None - - if can_send_other_messages: - send_messages = None - send_media = None - send_stickers = None - send_gifs = None - send_games = None - send_inline = None - - if can_add_web_page_previews: - send_messages = None - send_media = None - embed_links = None - - if can_send_polls: - send_messages = None - send_polls = None - - if can_change_info: - change_info = None - - if can_invite_users: - invite_users = None - - if can_pin_messages: - pin_messages = None - - r = self.send( - functions.messages.EditChatDefaultBannedRights( - peer=self.resolve_peer(chat_id), - banned_rights=types.ChatBannedRights( - until_date=0, - send_messages=send_messages, - send_media=send_media, - send_stickers=send_stickers, - send_gifs=send_gifs, - send_games=send_games, - send_inline=send_inline, - embed_links=embed_links, - send_polls=send_polls, - change_info=change_info, - invite_users=invite_users, - pin_messages=pin_messages - ) - ) - ) - - return Chat._parse_chat(self, r.chats[0]) diff --git a/pyrogram/client/methods/chats/restrict_chat_member.py b/pyrogram/client/methods/chats/restrict_chat_member.py deleted file mode 100644 index 1f31d8eb614..00000000000 --- a/pyrogram/client/methods/chats/restrict_chat_member.py +++ /dev/null @@ -1,157 +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 typing import Union - -from pyrogram.api import functions, types -from ...ext import BaseClient -from ...types.user_and_chats import Chat - - -class RestrictChatMember(BaseClient): - def restrict_chat_member( - self, - chat_id: Union[int, str], - user_id: Union[int, str], - until_date: int = 0, - can_send_messages: bool = False, - can_send_media_messages: bool = False, - can_send_other_messages: bool = False, - can_add_web_page_previews: bool = False, - can_send_polls: bool = False, - can_change_info: bool = False, - can_invite_users: bool = False, - can_pin_messages: bool = False - ) -> Chat: - """Use this method to restrict a user in a supergroup. - - The bot must be an administrator in the supergroup for this to work and must have the appropriate admin rights. - Pass True for all boolean parameters to lift restrictions from a user. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - - user_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target user. - For a contact that exists in your Telegram address book you can use his phone number (str). - - until_date (``int``, *optional*): - Date when the user will be unbanned, unix time. - If user is banned for more than 366 days or less than 30 seconds from the current time they are - considered to be banned forever. Defaults to 0 (ban forever). - - can_send_messages (``bool``, *optional*): - Pass True, if the user can send text messages, contacts, locations and venues. - - can_send_media_messages (``bool``, *optional*): - Pass True, if the user can send audios, documents, photos, videos, video notes and voice notes, - implies can_send_messages. - - can_send_other_messages (``bool``, *optional*): - Pass True, if the user can send animations, games, stickers and use inline bots, - implies can_send_media_messages. - - can_add_web_page_previews (``bool``, *optional*): - Pass True, if the user may add web page previews to their messages, implies can_send_media_messages. - - can_send_polls (``bool``, *optional*): - Pass True, if the user can send polls, implies can_send_media_messages. - - can_change_info (``bool``, *optional*): - Pass True, if the user can change the chat title, photo and other settings. - - can_invite_users (``bool``, *optional*): - Pass True, if the user can invite new users to the chat. - - can_pin_messages (``bool``, *optional*): - Pass True, if the user can pin messages. - - Returns: - :obj:`Chat`: On success, a chat object is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - send_messages = True - send_media = True - send_stickers = True - send_gifs = True - send_games = True - send_inline = True - embed_links = True - send_polls = True - change_info = True - invite_users = True - pin_messages = True - - if can_send_messages: - send_messages = None - - if can_send_media_messages: - send_messages = None - send_media = None - - if can_send_other_messages: - send_messages = None - send_media = None - send_stickers = None - send_gifs = None - send_games = None - send_inline = None - - if can_add_web_page_previews: - send_messages = None - send_media = None - embed_links = None - - if can_send_polls: - send_messages = None - send_polls = None - - if can_change_info: - change_info = None - - if can_invite_users: - invite_users = None - - if can_pin_messages: - pin_messages = None - - r = self.send( - functions.channels.EditBanned( - channel=self.resolve_peer(chat_id), - user_id=self.resolve_peer(user_id), - banned_rights=types.ChatBannedRights( - until_date=until_date, - send_messages=send_messages, - send_media=send_media, - send_stickers=send_stickers, - send_gifs=send_gifs, - send_games=send_games, - send_inline=send_inline, - embed_links=embed_links, - send_polls=send_polls, - change_info=change_info, - invite_users=invite_users, - pin_messages=pin_messages - ) - ) - ) - - return Chat._parse_chat(self, r.chats[0]) diff --git a/pyrogram/client/methods/chats/set_chat_description.py b/pyrogram/client/methods/chats/set_chat_description.py deleted file mode 100644 index 6cc3bbd35ce..00000000000 --- a/pyrogram/client/methods/chats/set_chat_description.py +++ /dev/null @@ -1,60 +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 typing import Union - -from pyrogram.api import functions, types -from ...ext import BaseClient - - -class SetChatDescription(BaseClient): - def set_chat_description( - self, - chat_id: Union[int, str], - description: str - ) -> bool: - """Use this method to change the description of a supergroup or a channel. - You must be an administrator in the chat for this to work and must have the appropriate admin rights. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - - description (``str``): - New chat description, 0-255 characters. - - Returns: - ``bool``: True on success. - - Raises: - RPCError: In case of a Telegram RPC error. - ``ValueError`` if a chat_id doesn't belong to a supergroup or a channel. - """ - peer = self.resolve_peer(chat_id) - - if isinstance(peer, (types.InputPeerChannel, types.InputPeerChat)): - self.send( - functions.messages.EditChatAbout( - peer=peer, - about=description - ) - ) - else: - raise ValueError("The chat_id \"{}\" belongs to a user".format(chat_id)) - - return True diff --git a/pyrogram/client/methods/chats/set_chat_photo.py b/pyrogram/client/methods/chats/set_chat_photo.py deleted file mode 100644 index 7248a2651d8..00000000000 --- a/pyrogram/client/methods/chats/set_chat_photo.py +++ /dev/null @@ -1,88 +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 . - -import os -from base64 import b64decode -from struct import unpack -from typing import Union - -from pyrogram.api import functions, types -from ...ext import BaseClient - - -class SetChatPhoto(BaseClient): - def set_chat_photo( - self, - chat_id: Union[int, str], - photo: str - ) -> bool: - """Use this method to set a new profile photo for the chat. - Photos can't be changed for private chats. - You must be an administrator in the chat for this to work and must have the appropriate admin rights. - - Note: - In regular groups (non-supergroups), this method will only work if the "All Members Are Admins" - setting is off. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - - photo (``str``): - New chat photo. You can pass a :class:`Photo` id or a file path to upload a new photo. - - Returns: - ``bool``: True on success. - - Raises: - RPCError: In case of a Telegram RPC error. - ``ValueError`` if a chat_id belongs to user. - """ - peer = self.resolve_peer(chat_id) - - if os.path.exists(photo): - photo = types.InputChatUploadedPhoto(file=self.save_file(photo)) - else: - s = unpack(" -# -# 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 typing import Union - -from pyrogram.api import functions, types -from ...ext import BaseClient - - -class SetChatTitle(BaseClient): - def set_chat_title( - self, - chat_id: Union[int, str], - title: str - ) -> bool: - """Use this method to change the title of a chat. - Titles can't be changed for private chats. - You must be an administrator in the chat for this to work and must have the appropriate admin rights. - - Note: - In regular groups (non-supergroups), this method will only work if the "All Members Are Admins" - setting is off. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - - title (``str``): - New chat title, 1-255 characters. - - Returns: - ``bool``: True on success. - - Raises: - RPCError: In case of a Telegram RPC error. - ValueError: In case a chat id belongs to user. - """ - peer = self.resolve_peer(chat_id) - - if isinstance(peer, types.InputPeerChat): - self.send( - functions.messages.EditChatTitle( - chat_id=peer.chat_id, - title=title - ) - ) - elif isinstance(peer, types.InputPeerChannel): - self.send( - functions.channels.EditTitle( - channel=peer, - title=title - ) - ) - else: - raise ValueError("The chat_id \"{}\" belongs to a user".format(chat_id)) - - return True diff --git a/pyrogram/client/methods/chats/unban_chat_member.py b/pyrogram/client/methods/chats/unban_chat_member.py deleted file mode 100644 index 35ea0343ea3..00000000000 --- a/pyrogram/client/methods/chats/unban_chat_member.py +++ /dev/null @@ -1,59 +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 typing import Union - -from pyrogram.api import functions, types -from ...ext import BaseClient - - -class UnbanChatMember(BaseClient): - def unban_chat_member( - self, - chat_id: Union[int, str], - user_id: Union[int, str] - ) -> bool: - """Use this method to unban a previously kicked user in a supergroup or channel. - The user will **not** return to the group or channel automatically, but will be able to join via link, etc. - You must be an administrator for this to work. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - - user_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target user. - For a contact that exists in your Telegram address book you can use his phone number (str). - - Returns: - ``bool``: True on success. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - self.send( - functions.channels.EditBanned( - channel=self.resolve_peer(chat_id), - user_id=self.resolve_peer(user_id), - banned_rights=types.ChatBannedRights( - until_date=0 - ) - ) - ) - - return True diff --git a/pyrogram/client/methods/chats/unpin_chat_message.py b/pyrogram/client/methods/chats/unpin_chat_message.py deleted file mode 100644 index 4e2531fd78a..00000000000 --- a/pyrogram/client/methods/chats/unpin_chat_message.py +++ /dev/null @@ -1,51 +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 typing import Union - -from pyrogram.api import functions -from ...ext import BaseClient - - -class UnpinChatMessage(BaseClient): - def unpin_chat_message( - self, - chat_id: Union[int, str] - ) -> bool: - """Use this method to unpin a message in a group, channel or your own chat. - You must be an administrator in the chat for this to work and must have the "can_pin_messages" admin - right in the supergroup or "can_edit_messages" admin right in the channel. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - - Returns: - ``bool``: True on success. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - self.send( - functions.messages.UpdatePinnedMessage( - peer=self.resolve_peer(chat_id), - id=0 - ) - ) - - return True diff --git a/pyrogram/client/methods/chats/update_chat_username.py b/pyrogram/client/methods/chats/update_chat_username.py deleted file mode 100644 index 2e8adb05620..00000000000 --- a/pyrogram/client/methods/chats/update_chat_username.py +++ /dev/null @@ -1,61 +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 typing import Union - -from pyrogram.api import functions, types -from ...ext import BaseClient - - -class UpdateChatUsername(BaseClient): - def update_chat_username( - self, - chat_id: Union[int, str], - username: Union[str, None] - ) -> bool: - """Use this method to update a channel or a supergroup username. - - To update your own username (for users only, not bots) you can use :meth:`update_username`. - - Parameters: - chat_id (``int`` | ``str``) - Unique identifier (int) or username (str) of the target chat. - username (``str`` | ``None``): - Username to set. Pass "" (empty string) or None to remove the username. - - Returns: - ``bool``: True on success. - - Raises: - RPCError: In case of a Telegram RPC error. - ValueError: In case a chat id belongs to a user or chat. - """ - - peer = self.resolve_peer(chat_id) - - if isinstance(peer, types.InputPeerChannel): - return bool( - self.send( - functions.channels.UpdateUsername( - channel=peer, - username=username or "" - ) - ) - ) - else: - raise ValueError("The chat_id \"{}\" belongs to a user or chat".format(chat_id)) diff --git a/pyrogram/client/methods/contacts/__init__.py b/pyrogram/client/methods/contacts/__init__.py deleted file mode 100644 index a966d10a1d8..00000000000 --- a/pyrogram/client/methods/contacts/__init__.py +++ /dev/null @@ -1,31 +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 .add_contacts import AddContacts -from .delete_contacts import DeleteContacts -from .get_contacts import GetContacts -from .get_contacts_count import GetContactsCount - - -class Contacts( - GetContacts, - DeleteContacts, - AddContacts, - GetContactsCount -): - pass diff --git a/pyrogram/client/methods/contacts/add_contacts.py b/pyrogram/client/methods/contacts/add_contacts.py deleted file mode 100644 index aa8e1fd5d15..00000000000 --- a/pyrogram/client/methods/contacts/add_contacts.py +++ /dev/null @@ -1,46 +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 typing import List - -import pyrogram -from pyrogram.api import functions -from ...ext import BaseClient - - -class AddContacts(BaseClient): - def add_contacts( - self, - contacts: List["pyrogram.InputPhoneContact"] - ): - """Use this method to add contacts to your Telegram address book. - - Parameters: - contacts (List of :obj:`InputPhoneContact`): - The contact list to be added - - Raises: - RPCError: In case of a Telegram RPC error. - """ - imported_contacts = self.send( - functions.contacts.ImportContacts( - contacts=contacts - ) - ) - - return imported_contacts diff --git a/pyrogram/client/methods/contacts/delete_contacts.py b/pyrogram/client/methods/contacts/delete_contacts.py deleted file mode 100644 index db5b9df1eee..00000000000 --- a/pyrogram/client/methods/contacts/delete_contacts.py +++ /dev/null @@ -1,59 +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 typing import List - -from pyrogram.api import functions, types -from pyrogram.errors import PeerIdInvalid -from ...ext import BaseClient - - -class DeleteContacts(BaseClient): - def delete_contacts( - self, - ids: List[int] - ): - """Use this method to delete contacts from your Telegram address book. - - Parameters: - ids (List of ``int``): - A list of unique identifiers for the target users. - Can be an ID (int), a username (string) or phone number (string). - - Returns: - ``bool``: True on success. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - contacts = [] - - for i in ids: - try: - input_user = self.resolve_peer(i) - except PeerIdInvalid: - continue - else: - if isinstance(input_user, types.InputPeerUser): - contacts.append(input_user) - - return self.send( - functions.contacts.DeleteContacts( - id=contacts - ) - ) diff --git a/pyrogram/client/methods/contacts/get_contacts.py b/pyrogram/client/methods/contacts/get_contacts.py deleted file mode 100644 index 7e607e776cb..00000000000 --- a/pyrogram/client/methods/contacts/get_contacts.py +++ /dev/null @@ -1,49 +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 . - -import logging -import time -from typing import List - -import pyrogram -from pyrogram.api import functions -from pyrogram.errors import FloodWait -from ...ext import BaseClient - -log = logging.getLogger(__name__) - - -class GetContacts(BaseClient): - def get_contacts(self) -> List["pyrogram.User"]: - """Use this method to get contacts from your Telegram address book. - - Returns: - List of :obj:`User`: On success, a list of users is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - while True: - try: - contacts = self.send(functions.contacts.GetContacts(hash=0)) - except FloodWait as e: - log.warning("get_contacts flood: waiting {} seconds".format(e.x)) - time.sleep(e.x) - else: - log.info("Total contacts: {}".format(len(self.peers_by_phone))) - return [pyrogram.User._parse(self, user) for user in contacts.users] diff --git a/pyrogram/client/methods/contacts/get_contacts_count.py b/pyrogram/client/methods/contacts/get_contacts_count.py deleted file mode 100644 index b9e6f6c4f48..00000000000 --- a/pyrogram/client/methods/contacts/get_contacts_count.py +++ /dev/null @@ -1,34 +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 pyrogram.api import functions -from ...ext import BaseClient - - -class GetContactsCount(BaseClient): - def get_contacts_count(self) -> int: - """Use this method to get the total count of contacts from your Telegram address book. - - Returns: - ``int``: On success, an integer is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - - return len(self.send(functions.contacts.GetContacts(hash=0)).contacts) diff --git a/pyrogram/client/methods/decorators/__init__.py b/pyrogram/client/methods/decorators/__init__.py deleted file mode 100644 index 2a2861ae787..00000000000 --- a/pyrogram/client/methods/decorators/__init__.py +++ /dev/null @@ -1,39 +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 .on_callback_query import OnCallbackQuery -from .on_deleted_messages import OnDeletedMessages -from .on_disconnect import OnDisconnect -from .on_inline_query import OnInlineQuery -from .on_message import OnMessage -from .on_poll import OnPoll -from .on_raw_update import OnRawUpdate -from .on_user_status import OnUserStatus - - -class Decorators( - OnMessage, - OnDeletedMessages, - OnCallbackQuery, - OnRawUpdate, - OnDisconnect, - OnUserStatus, - OnInlineQuery, - OnPoll -): - pass diff --git a/pyrogram/client/methods/decorators/on_callback_query.py b/pyrogram/client/methods/decorators/on_callback_query.py deleted file mode 100644 index 9ead25b408e..00000000000 --- a/pyrogram/client/methods/decorators/on_callback_query.py +++ /dev/null @@ -1,59 +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 typing import Tuple - -import pyrogram -from pyrogram.client.filters.filter import Filter -from pyrogram.client.handlers.handler import Handler -from ...ext import BaseClient - - -class OnCallbackQuery(BaseClient): - def on_callback_query( - self=None, - filters=None, - group: int = 0 - ) -> callable: - """Use this decorator to automatically register a function for handling callback queries. - This does the same thing as :meth:`add_handler` using the :class:`CallbackQueryHandler`. - - Parameters: - filters (:obj:`Filters`): - Pass one or more filters to allow only a subset of callback queries to be passed - in your function. - - group (``int``, *optional*): - The group identifier, defaults to 0. - """ - - def decorator(func: callable) -> Tuple[Handler, int]: - if isinstance(func, tuple): - func = func[0].callback - - handler = pyrogram.CallbackQueryHandler(func, filters) - - if isinstance(self, Filter): - return pyrogram.CallbackQueryHandler(func, self), group if filters is None else filters - - if self is not None: - self.add_handler(handler, group) - - return handler, group - - return decorator diff --git a/pyrogram/client/methods/decorators/on_deleted_messages.py b/pyrogram/client/methods/decorators/on_deleted_messages.py deleted file mode 100644 index eb9dfcd0d46..00000000000 --- a/pyrogram/client/methods/decorators/on_deleted_messages.py +++ /dev/null @@ -1,59 +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 typing import Tuple - -import pyrogram -from pyrogram.client.filters.filter import Filter -from pyrogram.client.handlers.handler import Handler -from ...ext import BaseClient - - -class OnDeletedMessages(BaseClient): - def on_deleted_messages( - self=None, - filters=None, - group: int = 0 - ) -> callable: - """Use this decorator to automatically register a function for handling deleted messages. - This does the same thing as :meth:`add_handler` using the :class:`DeletedMessagesHandler`. - - Parameters: - filters (:obj:`Filters`): - Pass one or more filters to allow only a subset of messages to be passed - in your function. - - group (``int``, *optional*): - The group identifier, defaults to 0. - """ - - def decorator(func: callable) -> Tuple[Handler, int]: - if isinstance(func, tuple): - func = func[0].callback - - handler = pyrogram.DeletedMessagesHandler(func, filters) - - if isinstance(self, Filter): - return pyrogram.DeletedMessagesHandler(func, self), group if filters is None else filters - - if self is not None: - self.add_handler(handler, group) - - return handler, group - - return decorator diff --git a/pyrogram/client/methods/decorators/on_disconnect.py b/pyrogram/client/methods/decorators/on_disconnect.py deleted file mode 100644 index 515a28c163f..00000000000 --- a/pyrogram/client/methods/decorators/on_disconnect.py +++ /dev/null @@ -1,38 +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 . - -import pyrogram -from pyrogram.client.handlers.handler import Handler -from ...ext import BaseClient - - -class OnDisconnect(BaseClient): - def on_disconnect(self=None) -> callable: - """Use this decorator to automatically register a function for handling disconnections. - This does the same thing as :meth:`add_handler` using the :class:`DisconnectHandler`. - """ - - def decorator(func: callable) -> Handler: - handler = pyrogram.DisconnectHandler(func) - - if self is not None: - self.add_handler(handler) - - return handler - - return decorator diff --git a/pyrogram/client/methods/decorators/on_inline_query.py b/pyrogram/client/methods/decorators/on_inline_query.py deleted file mode 100644 index e9758c64c10..00000000000 --- a/pyrogram/client/methods/decorators/on_inline_query.py +++ /dev/null @@ -1,59 +0,0 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 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 typing import Tuple - -import pyrogram -from pyrogram.client.filters.filter import Filter -from pyrogram.client.handlers.handler import Handler -from ...ext import BaseClient - - -class OnInlineQuery(BaseClient): - def on_inline_query( - self=None, - filters=None, - group: int = 0 - ) -> callable: - """Use this decorator to automatically register a function for handling inline queries. - This does the same thing as :meth:`add_handler` using the :class:`InlineQueryHandler`. - - Parameters: - filters (:obj:`Filters `): - Pass one or more filters to allow only a subset of inline queries to be passed - in your function. - - group (``int``, *optional*): - The group identifier, defaults to 0. - """ - - def decorator(func: callable) -> Tuple[Handler, int]: - if isinstance(func, tuple): - func = func[0].callback - - handler = pyrogram.InlineQueryHandler(func, filters) - - if isinstance(self, Filter): - return pyrogram.InlineQueryHandler(func, self), group if filters is None else filters - - if self is not None: - self.add_handler(handler, group) - - return handler, group - - return decorator diff --git a/pyrogram/client/methods/decorators/on_message.py b/pyrogram/client/methods/decorators/on_message.py deleted file mode 100644 index ad95cd451e1..00000000000 --- a/pyrogram/client/methods/decorators/on_message.py +++ /dev/null @@ -1,59 +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 typing import Tuple - -import pyrogram -from pyrogram.client.filters.filter import Filter -from pyrogram.client.handlers.handler import Handler -from ...ext import BaseClient - - -class OnMessage(BaseClient): - def on_message( - self=None, - filters=None, - group: int = 0 - ) -> callable: - """Use this decorator to automatically register a function for handling messages. - This does the same thing as :meth:`add_handler` using the :class:`MessageHandler`. - - Parameters: - filters (:obj:`Filters`): - Pass one or more filters to allow only a subset of messages to be passed - in your function. - - group (``int``, *optional*): - The group identifier, defaults to 0. - """ - - def decorator(func: callable) -> Tuple[Handler, int]: - if isinstance(func, tuple): - func = func[0].callback - - handler = pyrogram.MessageHandler(func, filters) - - if isinstance(self, Filter): - return pyrogram.MessageHandler(func, self), group if filters is None else filters - - if self is not None: - self.add_handler(handler, group) - - return handler, group - - return decorator diff --git a/pyrogram/client/methods/decorators/on_poll.py b/pyrogram/client/methods/decorators/on_poll.py deleted file mode 100644 index 68f3d78e687..00000000000 --- a/pyrogram/client/methods/decorators/on_poll.py +++ /dev/null @@ -1,59 +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 typing import Tuple - -import pyrogram -from pyrogram.client.filters.filter import Filter -from pyrogram.client.handlers.handler import Handler -from ...ext import BaseClient - - -class OnPoll(BaseClient): - def on_poll( - self=None, - filters=None, - group: int = 0 - ) -> callable: - """Use this decorator to automatically register a function for handling poll updates. - This does the same thing as :meth:`add_handler` using the :class:`PollHandler`. - - Parameters: - filters (:obj:`Filters `): - Pass one or more filters to allow only a subset of polls to be passed - in your function. - - group (``int``, *optional*): - The group identifier, defaults to 0. - """ - - def decorator(func: callable) -> Tuple[Handler, int]: - if isinstance(func, tuple): - func = func[0].callback - - handler = pyrogram.PollHandler(func, filters) - - if isinstance(self, Filter): - return pyrogram.PollHandler(func, self), group if filters is None else filters - - if self is not None: - self.add_handler(handler, group) - - return handler, group - - return decorator diff --git a/pyrogram/client/methods/decorators/on_raw_update.py b/pyrogram/client/methods/decorators/on_raw_update.py deleted file mode 100644 index 2ab1f61bd07..00000000000 --- a/pyrogram/client/methods/decorators/on_raw_update.py +++ /dev/null @@ -1,53 +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 typing import Tuple - -import pyrogram -from pyrogram.client.handlers.handler import Handler -from ...ext import BaseClient - - -class OnRawUpdate(BaseClient): - def on_raw_update( - self=None, - group: int = 0 - ) -> callable: - """Use this decorator to automatically register a function for handling raw updates. - This does the same thing as :meth:`add_handler` using the :class:`RawUpdateHandler`. - - Parameters: - group (``int``, *optional*): - The group identifier, defaults to 0. - """ - - def decorator(func: callable) -> Tuple[Handler, int]: - if isinstance(func, tuple): - func = func[0].callback - - handler = pyrogram.RawUpdateHandler(func) - - if isinstance(self, int): - return handler, group if self is None else group - - if self is not None: - self.add_handler(handler, group) - - return handler, group - - return decorator diff --git a/pyrogram/client/methods/decorators/on_user_status.py b/pyrogram/client/methods/decorators/on_user_status.py deleted file mode 100644 index 5b49bb3cd72..00000000000 --- a/pyrogram/client/methods/decorators/on_user_status.py +++ /dev/null @@ -1,58 +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 typing import Tuple - -import pyrogram -from pyrogram.client.filters.filter import Filter -from pyrogram.client.handlers.handler import Handler -from ...ext import BaseClient - - -class OnUserStatus(BaseClient): - def on_user_status( - self=None, - filters=None, - group: int = 0 - ) -> callable: - """Use this decorator to automatically register a function for handling user status updates. - This does the same thing as :meth:`add_handler` using the :class:`UserStatusHandler`. - - Parameters: - filters (:obj:`Filters`): - Pass one or more filters to allow only a subset of UserStatus updated to be passed in your function. - - group (``int``, *optional*): - The group identifier, defaults to 0. - """ - - def decorator(func: callable) -> Tuple[Handler, int]: - if isinstance(func, tuple): - func = func[0].callback - - handler = pyrogram.UserStatusHandler(func, filters) - - if isinstance(self, Filter): - return pyrogram.UserStatusHandler(func, self), group if filters is None else filters - - if self is not None: - self.add_handler(handler, group) - - return handler, group - - return decorator diff --git a/pyrogram/client/methods/messages/__init__.py b/pyrogram/client/methods/messages/__init__.py deleted file mode 100644 index e843aa7cd75..00000000000 --- a/pyrogram/client/methods/messages/__init__.py +++ /dev/null @@ -1,83 +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 .delete_messages import DeleteMessages -from .download_media import DownloadMedia -from .edit_message_caption import EditMessageCaption -from .edit_message_media import EditMessageMedia -from .edit_message_reply_markup import EditMessageReplyMarkup -from .edit_message_text import EditMessageText -from .forward_messages import ForwardMessages -from .get_history import GetHistory -from .get_history_count import GetHistoryCount -from .get_messages import GetMessages -from .iter_history import IterHistory -from .retract_vote import RetractVote -from .send_animation import SendAnimation -from .send_audio import SendAudio -from .send_cached_media import SendCachedMedia -from .send_chat_action import SendChatAction -from .send_contact import SendContact -from .send_document import SendDocument -from .send_location import SendLocation -from .send_media_group import SendMediaGroup -from .send_message import SendMessage -from .send_photo import SendPhoto -from .send_poll import SendPoll -from .send_sticker import SendSticker -from .send_venue import SendVenue -from .send_video import SendVideo -from .send_video_note import SendVideoNote -from .send_voice import SendVoice -from .stop_poll import StopPoll -from .vote_poll import VotePoll - - -class Messages( - DeleteMessages, - EditMessageCaption, - EditMessageReplyMarkup, - EditMessageMedia, - EditMessageText, - ForwardMessages, - GetHistory, - GetMessages, - SendAudio, - SendChatAction, - SendContact, - SendDocument, - SendAnimation, - SendLocation, - SendMediaGroup, - SendMessage, - SendPhoto, - SendSticker, - SendVenue, - SendVideo, - SendVideoNote, - SendVoice, - SendPoll, - VotePoll, - StopPoll, - RetractVote, - DownloadMedia, - IterHistory, - SendCachedMedia, - GetHistoryCount -): - pass diff --git a/pyrogram/client/methods/messages/delete_messages.py b/pyrogram/client/methods/messages/delete_messages.py deleted file mode 100644 index 067d2fae40d..00000000000 --- a/pyrogram/client/methods/messages/delete_messages.py +++ /dev/null @@ -1,76 +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 typing import Union, Iterable - -from pyrogram.api import functions, types -from pyrogram.client.ext import BaseClient - - -class DeleteMessages(BaseClient): - def delete_messages( - self, - chat_id: Union[int, str], - message_ids: Iterable[int], - revoke: bool = True - ) -> bool: - """Use this method to delete messages, including service messages. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - message_ids (``iterable``): - A list of Message identifiers to delete or a single message id. - Iterators and Generators are also accepted. - - revoke (``bool``, *optional*): - Deletes messages on both parts. - This is only for private cloud chats and normal groups, messages on - channels and supergroups are always revoked (i.e.: deleted for everyone). - Defaults to True. - - Returns: - ``bool``: True on success, False otherwise. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - peer = self.resolve_peer(chat_id) - message_ids = list(message_ids) if not isinstance(message_ids, int) else [message_ids] - - if isinstance(peer, types.InputPeerChannel): - r = self.send( - functions.channels.DeleteMessages( - channel=peer, - id=message_ids - ) - ) - else: - r = self.send( - functions.messages.DeleteMessages( - id=message_ids, - revoke=revoke or None - ) - ) - - # Deleting messages you don't have right onto, won't raise any error. - # Check for pts_count, which is 0 in case deletes fail. - return bool(r.pts_count) diff --git a/pyrogram/client/methods/messages/download_media.py b/pyrogram/client/methods/messages/download_media.py deleted file mode 100644 index 04a5ec57ff7..00000000000 --- a/pyrogram/client/methods/messages/download_media.py +++ /dev/null @@ -1,150 +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 threading import Event -from typing import Union - -import pyrogram -from pyrogram.client.ext import BaseClient - - -class DownloadMedia(BaseClient): - def download_media( - self, - message: Union["pyrogram.Message", str], - file_name: str = "", - block: bool = True, - progress: callable = None, - progress_args: tuple = () - ) -> Union[str, None]: - """Use this method to download the media from a message. - - Parameters: - message (:obj:`Message` | ``str``): - Pass a Message containing the media, the media itself (message.audio, message.video, ...) or - the file id as string. - - file_name (``str``, *optional*): - A custom *file_name* to be used instead of the one provided by Telegram. - By default, all files are downloaded in the *downloads* folder in your working directory. - You can also specify a path for downloading files in a custom location: paths that end with "/" - are considered directories. All non-existent folders will be created automatically. - - block (``bool``, *optional*): - Blocks the code execution until the file has been downloaded. - Defaults to True. - - progress (``callable``): - Pass a callback function to view the download progress. - The function must take *(client, current, total, \*args)* as positional arguments (look at the section - below for a detailed description). - - progress_args (``tuple``): - Extra custom arguments for the progress callback function. Useful, for example, if you want to pass - a chat_id and a message_id in order to edit a message with the updated progress. - - Other Parameters: - client (:obj:`Client`): - The Client itself, useful when you want to call other API methods inside the callback function. - - current (``int``): - The amount of bytes downloaded so far. - - total (``int``): - The size of the file. - - *args (``tuple``, *optional*): - Extra custom arguments as defined in the *progress_args* parameter. - You can either keep *\*args* or add every single extra argument in your function signature. - - Returns: - ``str``: On success, the absolute path of the downloaded file is returned. - - ``None`` -- In case the download is deliberately stopped with :meth:`stop_transmission`. - - Raises: - RPCError: In case of a Telegram RPC error. - ``ValueError`` if the message doesn't contain any downloadable media - """ - error_message = "This message doesn't contain any downloadable media" - - if isinstance(message, pyrogram.Message): - if message.photo: - media = pyrogram.Document( - file_id=message.photo.sizes[-1].file_id, - file_size=message.photo.sizes[-1].file_size, - mime_type="", - date=message.photo.date, - client=self - ) - elif message.audio: - media = message.audio - elif message.document: - media = message.document - elif message.video: - media = message.video - elif message.voice: - media = message.voice - elif message.video_note: - media = message.video_note - elif message.sticker: - media = message.sticker - elif message.animation: - media = message.animation - else: - raise ValueError(error_message) - elif isinstance(message, ( - pyrogram.Photo, - pyrogram.PhotoSize, - pyrogram.Audio, - pyrogram.Document, - pyrogram.Video, - pyrogram.Voice, - pyrogram.VideoNote, - pyrogram.Sticker, - pyrogram.Animation - )): - if isinstance(message, pyrogram.Photo): - media = pyrogram.Document( - file_id=message.sizes[-1].file_id, - file_size=message.sizes[-1].file_size, - mime_type="", - date=message.date, - client=self - ) - else: - media = message - elif isinstance(message, str): - media = pyrogram.Document( - file_id=message, - file_size=0, - mime_type="", - client=self - ) - else: - raise ValueError(error_message) - - done = Event() - path = [None] - - self.download_queue.put((media, file_name, done, progress, progress_args, path)) - - if block: - done.wait() - - return path[0] diff --git a/pyrogram/client/methods/messages/edit_message_caption.py b/pyrogram/client/methods/messages/edit_message_caption.py deleted file mode 100644 index fe19dcc9777..00000000000 --- a/pyrogram/client/methods/messages/edit_message_caption.py +++ /dev/null @@ -1,79 +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 typing import Union - -import pyrogram -from pyrogram.api import functions, types -from pyrogram.client.ext import BaseClient - - -class EditMessageCaption(BaseClient): - def edit_message_caption( - self, - chat_id: Union[int, str], - message_id: int, - caption: str, - parse_mode: str = "", - reply_markup: "pyrogram.InlineKeyboardMarkup" = None - ) -> "pyrogram.Message": - """Use this method to edit captions of messages. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - message_id (``int``): - Message identifier in the chat specified in chat_id. - - caption (``str``): - New caption of the message. - - parse_mode (``str``, *optional*): - Pass "markdown" or "html" if you want Telegram apps to show bold, italic, fixed-width text or inline - URLs in your caption. Defaults to "markdown". - - reply_markup (:obj:`InlineKeyboardMarkup`, *optional*): - An InlineKeyboardMarkup object. - - Returns: - :obj:`Message`: On success, the edited message is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - style = self.html if parse_mode.lower() == "html" else self.markdown - - r = self.send( - functions.messages.EditMessage( - peer=self.resolve_peer(chat_id), - id=message_id, - reply_markup=reply_markup.write() if reply_markup else None, - **style.parse(caption) - ) - ) - - for i in r.updates: - if isinstance(i, (types.UpdateEditMessage, types.UpdateEditChannelMessage)): - return pyrogram.Message._parse( - self, i.message, - {i.id: i for i in r.users}, - {i.id: i for i in r.chats} - ) diff --git a/pyrogram/client/methods/messages/edit_message_media.py b/pyrogram/client/methods/messages/edit_message_media.py deleted file mode 100644 index b03fb1e08f0..00000000000 --- a/pyrogram/client/methods/messages/edit_message_media.py +++ /dev/null @@ -1,366 +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 . - -import binascii -import os -import struct -from typing import Union - -import pyrogram -from pyrogram.api import functions, types -from pyrogram.errors import FileIdInvalid -from pyrogram.client.ext import BaseClient, utils -from pyrogram.client.types import ( - InputMediaPhoto, InputMediaVideo, InputMediaAudio, - InputMediaAnimation, InputMediaDocument -) -from pyrogram.client.types.input_media import InputMedia - - -class EditMessageMedia(BaseClient): - def edit_message_media( - self, - chat_id: Union[int, str], - message_id: int, - media: InputMedia, - reply_markup: "pyrogram.InlineKeyboardMarkup" = None - ) -> "pyrogram.Message": - """Use this method to edit audio, document, photo, or video messages. - - If a message is a part of a message album, then it can be edited only to a photo or a video. Otherwise, - message type can be changed arbitrarily. When inline message is edited, new file can't be uploaded. - Use previously uploaded file via its file_id or specify a URL. On success, if the edited message was sent - by the bot, the edited Message is returned, otherwise True is returned. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - message_id (``int``): - Message identifier in the chat specified in chat_id. - - media (:obj:`InputMedia`) - One of the InputMedia objects describing an animation, audio, document, photo or video. - - reply_markup (:obj:`InlineKeyboardMarkup`, *optional*): - An InlineKeyboardMarkup object. - - Returns: - :obj:`Message`: On success, the edited message is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - style = self.html if media.parse_mode.lower() == "html" else self.markdown - caption = media.caption - - if isinstance(media, InputMediaPhoto): - if os.path.exists(media.media): - media = self.send( - functions.messages.UploadMedia( - peer=self.resolve_peer(chat_id), - media=types.InputMediaUploadedPhoto( - file=self.save_file(media.media) - ) - ) - ) - - media = types.InputMediaPhoto( - id=types.InputPhoto( - id=media.photo.id, - access_hash=media.photo.access_hash, - file_reference=b"" - ) - ) - elif media.media.startswith("http"): - media = types.InputMediaPhotoExternal( - url=media.media - ) - else: - try: - decoded = utils.decode(media.media) - fmt = " 24 else " 24 else " 24 else " 24 else " 24 else " -# -# 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 typing import Union - -import pyrogram -from pyrogram.api import functions, types -from pyrogram.client.ext import BaseClient - - -class EditMessageReplyMarkup(BaseClient): - def edit_message_reply_markup( - self, - chat_id: Union[int, str], - message_id: int, - reply_markup: "pyrogram.InlineKeyboardMarkup" = None - ) -> "pyrogram.Message": - """Use this method to edit only the reply markup of messages sent by the bot or via the bot (for inline bots). - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - message_id (``int``): - Message identifier in the chat specified in chat_id. - - reply_markup (:obj:`InlineKeyboardMarkup`, *optional*): - An InlineKeyboardMarkup object. - - Returns: - :obj:`Message`: In case the edited message is sent by the bot. - - ``bool`` -- True, in case the edited message is sent by the user. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - - r = self.send( - functions.messages.EditMessage( - peer=self.resolve_peer(chat_id), - id=message_id, - reply_markup=reply_markup.write() if reply_markup else None - ) - ) - - for i in r.updates: - if isinstance(i, (types.UpdateEditMessage, types.UpdateEditChannelMessage)): - return pyrogram.Message._parse( - self, i.message, - {i.id: i for i in r.users}, - {i.id: i for i in r.chats} - ) diff --git a/pyrogram/client/methods/messages/edit_message_text.py b/pyrogram/client/methods/messages/edit_message_text.py deleted file mode 100644 index 30b58b68938..00000000000 --- a/pyrogram/client/methods/messages/edit_message_text.py +++ /dev/null @@ -1,84 +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 typing import Union - -import pyrogram -from pyrogram.api import functions, types -from pyrogram.client.ext import BaseClient - - -class EditMessageText(BaseClient): - def edit_message_text( - self, - chat_id: Union[int, str], - message_id: int, - text: str, - parse_mode: str = "", - disable_web_page_preview: bool = None, - reply_markup: "pyrogram.InlineKeyboardMarkup" = None - ) -> "pyrogram.Message": - """Use this method to edit text messages. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - message_id (``int``): - Message identifier in the chat specified in chat_id. - - text (``str``): - New text of the message. - - parse_mode (``str``, *optional*): - Pass "markdown" or "html" if you want Telegram apps to show bold, italic, fixed-width text or inline - URLs in your message. Defaults to "markdown". - - disable_web_page_preview (``bool``, *optional*): - Disables link previews for links in this message. - - reply_markup (:obj:`InlineKeyboardMarkup`, *optional*): - An InlineKeyboardMarkup object. - - Returns: - :obj:`Message`: On success, the edited message is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - style = self.html if parse_mode.lower() == "html" else self.markdown - - r = self.send( - functions.messages.EditMessage( - peer=self.resolve_peer(chat_id), - id=message_id, - no_webpage=disable_web_page_preview or None, - reply_markup=reply_markup.write() if reply_markup else None, - **style.parse(text) - ) - ) - - for i in r.updates: - if isinstance(i, (types.UpdateEditMessage, types.UpdateEditChannelMessage)): - return pyrogram.Message._parse( - self, i.message, - {i.id: i for i in r.users}, - {i.id: i for i in r.chats} - ) diff --git a/pyrogram/client/methods/messages/forward_messages.py b/pyrogram/client/methods/messages/forward_messages.py deleted file mode 100644 index 08cc48ea4b6..00000000000 --- a/pyrogram/client/methods/messages/forward_messages.py +++ /dev/null @@ -1,130 +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 typing import Union, Iterable - -import pyrogram -from pyrogram.api import functions, types -from ...ext import BaseClient - - -class ForwardMessages(BaseClient): - def forward_messages( - self, - chat_id: Union[int, str], - from_chat_id: Union[int, str], - message_ids: Iterable[int], - disable_notification: bool = None, - as_copy: bool = False, - remove_caption: bool = False - ) -> "pyrogram.Messages": - """Use this method to forward messages of any kind. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - from_chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the source chat where the original message was sent. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - message_ids (``iterable``): - A list of Message identifiers in the chat specified in *from_chat_id* or a single message id. - Iterators and Generators are also accepted. - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - as_copy (``bool``, *optional*): - Pass True to forward messages without the forward header (i.e.: send a copy of the message content). - Defaults to False. - - remove_caption (``bool``, *optional*): - If set to True and *as_copy* is enabled as well, media captions are not preserved when copying the - message. Has no effect if *as_copy* is not enabled. - Defaults to False. - - Returns: - :obj:`Message`: In case *message_ids* was an integer, the single forwarded message is - returned. - - :obj:`Messages` -- In case *message_ids* was an iterable, the returned value will be an - object containing a list of messages, even if such iterable contained just a single element. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - - is_iterable = not isinstance(message_ids, int) - message_ids = list(message_ids) if is_iterable else [message_ids] - - if as_copy: - forwarded_messages = [] - - for chunk in [message_ids[i:i + 200] for i in range(0, len(message_ids), 200)]: - messages = self.get_messages(chat_id=from_chat_id, message_ids=chunk) # type: pyrogram.Messages - - for message in messages.messages: - forwarded_messages.append( - message.forward( - chat_id, - disable_notification=disable_notification, - as_copy=True, - remove_caption=remove_caption - ) - ) - - return pyrogram.Messages( - client=self, - total_count=len(forwarded_messages), - messages=forwarded_messages - ) if is_iterable else forwarded_messages[0] - else: - r = self.send( - functions.messages.ForwardMessages( - to_peer=self.resolve_peer(chat_id), - from_peer=self.resolve_peer(from_chat_id), - id=message_ids, - silent=disable_notification or None, - random_id=[self.rnd_id() for _ in message_ids] - ) - ) - - forwarded_messages = [] - - users = {i.id: i for i in r.users} - chats = {i.id: i for i in r.chats} - - for i in r.updates: - if isinstance(i, (types.UpdateNewMessage, types.UpdateNewChannelMessage)): - forwarded_messages.append( - pyrogram.Message._parse( - self, i.message, - users, chats - ) - ) - - return pyrogram.Messages( - client=self, - total_count=len(forwarded_messages), - messages=forwarded_messages - ) if is_iterable else forwarded_messages[0] diff --git a/pyrogram/client/methods/messages/get_history.py b/pyrogram/client/methods/messages/get_history.py deleted file mode 100644 index ffd8f9e0a6a..00000000000 --- a/pyrogram/client/methods/messages/get_history.py +++ /dev/null @@ -1,102 +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 . - -import logging -import time -from typing import Union - -import pyrogram -from pyrogram.api import functions -from pyrogram.errors import FloodWait -from ...ext import BaseClient - -log = logging.getLogger(__name__) - - -class GetHistory(BaseClient): - def get_history( - self, - chat_id: Union[int, str], - limit: int = 100, - offset: int = 0, - offset_id: int = 0, - offset_date: int = 0, - reverse: bool = False - ) -> "pyrogram.Messages": - """Use this method to retrieve a chunk of the history of a chat. - - You can get up to 100 messages at once. - For a more convenient way of getting a chat history see :meth:`iter_history`. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - limit (``int``, *optional*): - Limits the number of messages to be retrieved. - By default, the first 100 messages are returned. - - offset (``int``, *optional*): - Sequential number of the first message to be returned. Defaults to 0 (most recent message). - Negative values are also accepted and become useful in case you set offset_id or offset_date. - - offset_id (``int``, *optional*): - Pass a message identifier as offset to retrieve only older messages starting from that message. - - offset_date (``int``, *optional*): - Pass a date in Unix time as offset to retrieve only older messages starting from that date. - - reverse (``bool``, *optional*): - Pass True to retrieve the messages in reversed order (from older to most recent). - - Returns: - :obj:`Messages` - On success, an object containing a list of the retrieved messages. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - - while True: - try: - messages = pyrogram.Messages._parse( - self, - self.send( - functions.messages.GetHistory( - peer=self.resolve_peer(chat_id), - offset_id=offset_id, - offset_date=offset_date, - add_offset=offset * (-1 if reverse else 1) - (limit if reverse else 0), - limit=limit, - max_id=0, - min_id=0, - hash=0 - ) - ) - ) - except FloodWait as e: - log.warning("Sleeping for {}s".format(e.x)) - time.sleep(e.x) - else: - break - - if reverse: - messages.messages.reverse() - - return messages diff --git a/pyrogram/client/methods/messages/get_history_count.py b/pyrogram/client/methods/messages/get_history_count.py deleted file mode 100644 index b4812d26c24..00000000000 --- a/pyrogram/client/methods/messages/get_history_count.py +++ /dev/null @@ -1,84 +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 . - -import logging -import time -from typing import Union - -from pyrogram.api import types, functions -from pyrogram.client.ext import BaseClient -from pyrogram.errors import FloodWait - -log = logging.getLogger(__name__) - - -class GetHistoryCount(BaseClient): - def get_history_count( - self, - chat_id: Union[int, str] - ) -> int: - """Use this method to get the total count of messages in a chat. - - .. note:: - - Due to Telegram latest internal changes, the server can't reliably find anymore the total count of messages - a **private** or a **basic group** chat has with a single method call. To overcome this limitation, Pyrogram - has to iterate over all the messages. Channels and supergroups are not affected by this limitation. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - - Returns: - ``int``: On success, an integer is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - - peer = self.resolve_peer(chat_id) - - if not isinstance(peer, types.InputPeerChannel): - offset = 0 - limit = 100 - - while True: - try: - r = self.send( - functions.messages.GetHistory( - peer=peer, - offset_id=1, - offset_date=0, - add_offset=-offset - limit, - limit=limit, - max_id=0, - min_id=0, - hash=0 - ) - ) - except FloodWait as e: - log.warning("Sleeping for {}s".format(e.x)) - time.sleep(e.x) - continue - - if not r.messages: - return offset - - offset += len(r.messages) - - return self.get_history(chat_id=chat_id, limit=1).total_count diff --git a/pyrogram/client/methods/messages/get_messages.py b/pyrogram/client/methods/messages/get_messages.py deleted file mode 100644 index 391c251c820..00000000000 --- a/pyrogram/client/methods/messages/get_messages.py +++ /dev/null @@ -1,106 +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 . - -import logging -import time -from typing import Union, Iterable - -import pyrogram -from pyrogram.api import functions, types -from pyrogram.errors import FloodWait -from ...ext import BaseClient - -log = logging.getLogger(__name__) - - -class GetMessages(BaseClient): - def get_messages( - self, - chat_id: Union[int, str], - message_ids: Union[int, Iterable[int]] = None, - reply_to_message_ids: Union[int, Iterable[int]] = None, - replies: int = 1 - ) -> Union["pyrogram.Message", "pyrogram.Messages"]: - """Use this method to get one or more messages that belong to a specific chat. - You can retrieve up to 200 messages at once. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - message_ids (``iterable``, *optional*): - Pass a single message identifier or a list of message ids (as integers) to get the content of the - message themselves. Iterators and Generators are also accepted. - - reply_to_message_ids (``iterable``, *optional*): - Pass a single message identifier or a list of message ids (as integers) to get the content of - the previous message you replied to using this message. Iterators and Generators are also accepted. - If *message_ids* is set, this argument will be ignored. - - replies (``int``, *optional*): - The number of subsequent replies to get for each message. - Pass 0 for no reply at all or -1 for unlimited replies. - Defaults to 1. - - Returns: - :obj:`Message`: In case *message_ids* was an integer, the single forwarded message is - returned. - - :obj:`Messages` -- In case *message_ids* was an iterable, the returned value will be an - object containing a list of messages, even if such iterable contained just a single element. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - ids, ids_type = ( - (message_ids, types.InputMessageID) if message_ids - else (reply_to_message_ids, types.InputMessageReplyTo) if reply_to_message_ids - else (None, None) - ) - - if ids is None: - raise ValueError("No argument supplied") - - peer = self.resolve_peer(chat_id) - - is_iterable = not isinstance(ids, int) - ids = list(ids) if is_iterable else [ids] - ids = [ids_type(id=i) for i in ids] - - if replies < 0: - replies = (1 << 31) - 1 - - if isinstance(peer, types.InputPeerChannel): - rpc = functions.channels.GetMessages(channel=peer, id=ids) - else: - rpc = functions.messages.GetMessages(id=ids) - - while True: - try: - r = self.send(rpc) - except FloodWait as e: - log.warning("Sleeping for {}s".format(e.x)) - time.sleep(e.x) - else: - break - - messages = pyrogram.Messages._parse(self, r, replies=replies) - - return messages if is_iterable else messages.messages[0] diff --git a/pyrogram/client/methods/messages/iter_history.py b/pyrogram/client/methods/messages/iter_history.py deleted file mode 100644 index b15463183a4..00000000000 --- a/pyrogram/client/methods/messages/iter_history.py +++ /dev/null @@ -1,95 +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 typing import Union, Generator - -import pyrogram -from ...ext import BaseClient - - -class IterHistory(BaseClient): - def iter_history( - self, - chat_id: Union[int, str], - limit: int = 0, - offset: int = 0, - offset_id: int = 0, - offset_date: int = 0, - reverse: bool = False - ) -> Generator["pyrogram.Message", None, None]: - """Use this method to iterate through a chat history sequentially. - - This convenience method does the same as repeatedly calling :meth:`get_history` in a loop, thus saving you from - the hassle of setting up boilerplate code. It is useful for getting the whole chat history with a single call. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - limit (``int``, *optional*): - Limits the number of messages to be retrieved. - By default, no limit is applied and all messages are returned. - - offset (``int``, *optional*): - Sequential number of the first message to be returned.. - Negative values are also accepted and become useful in case you set offset_id or offset_date. - - offset_id (``int``, *optional*): - Identifier of the first message to be returned. - - offset_date (``int``, *optional*): - Pass a date in Unix time as offset to retrieve only older messages starting from that date. - - reverse (``bool``, *optional*): - Pass True to retrieve the messages in reversed order (from older to most recent). - - Returns: - ``Generator``: A generator yielding :obj:`Message` objects. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - offset_id = offset_id or (1 if reverse else 0) - current = 0 - total = limit or (1 << 31) - 1 - limit = min(100, total) - - while True: - messages = self.get_history( - chat_id=chat_id, - limit=limit, - offset=offset, - offset_id=offset_id, - offset_date=offset_date, - reverse=reverse - ).messages - - if not messages: - return - - offset_id = messages[-1].message_id + (1 if reverse else 0) - - for message in messages: - yield message - - current += 1 - - if current >= total: - return diff --git a/pyrogram/client/methods/messages/retract_vote.py b/pyrogram/client/methods/messages/retract_vote.py deleted file mode 100644 index 929298df08b..00000000000 --- a/pyrogram/client/methods/messages/retract_vote.py +++ /dev/null @@ -1,57 +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 typing import Union - -import pyrogram -from pyrogram.api import functions -from pyrogram.client.ext import BaseClient - - -class RetractVote(BaseClient): - def retract_vote( - self, - chat_id: Union[int, str], - message_id: int - ) -> "pyrogram.Poll": - """Use this method to retract your vote in a poll. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - message_id (``int``): - Identifier of the original message with the poll. - - Returns: - :obj:`Poll`: On success, the poll with the retracted vote is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - r = self.send( - functions.messages.SendVote( - peer=self.resolve_peer(chat_id), - msg_id=message_id, - options=[] - ) - ) - - return pyrogram.Poll._parse(self, r.updates[0]) diff --git a/pyrogram/client/methods/messages/send_animation.py b/pyrogram/client/methods/messages/send_animation.py deleted file mode 100644 index 4e21e56cfa8..00000000000 --- a/pyrogram/client/methods/messages/send_animation.py +++ /dev/null @@ -1,204 +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 . - -import binascii -import os -import struct -from typing import Union - -import pyrogram -from pyrogram.api import functions, types -from pyrogram.errors import FileIdInvalid, FilePartMissing -from pyrogram.client.ext import BaseClient, utils - - -class SendAnimation(BaseClient): - def send_animation( - self, - chat_id: Union[int, str], - animation: str, - caption: str = "", - parse_mode: str = "", - duration: int = 0, - width: int = 0, - height: int = 0, - thumb: str = None, - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union[ - "pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply" - ] = None, - progress: callable = None, - progress_args: tuple = () - ) -> Union["pyrogram.Message", None]: - """Use this method to send animation files (animation or H.264/MPEG-4 AVC video without sound). - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - animation (``str``): - Animation to send. - Pass a file_id as string to send an animation that exists on the Telegram servers, - pass an HTTP URL as a string for Telegram to get an animation from the Internet, or - pass a file path as string to upload a new animation that exists on your local machine. - - caption (``str``, *optional*): - Animation caption, 0-1024 characters. - - parse_mode (``str``, *optional*): - Pass "markdown" or "html" if you want Telegram apps to show bold, italic, fixed-width text or inline - URLs in your caption. Defaults to "markdown". - - duration (``int``, *optional*): - Duration of sent animation in seconds. - - width (``int``, *optional*): - Animation width. - - height (``int``, *optional*): - Animation height. - - thumb (``str``, *optional*): - Thumbnail of the animation file sent. - The thumbnail should be in JPEG format and less than 200 KB in size. - A thumbnail's width and height should not exceed 320 pixels. - Thumbnails can't be reused and can be only uploaded as a new file. - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - reply_to_message_id (``int``, *optional*): - If the message is a reply, ID of the original message. - - reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*): - Additional interface options. An object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. - - progress (``callable``, *optional*): - Pass a callback function to view the upload progress. - The function must take *(client, current, total, \*args)* as positional arguments (look at the section - below for a detailed description). - - progress_args (``tuple``, *optional*): - Extra custom arguments for the progress callback function. Useful, for example, if you want to pass - a chat_id and a message_id in order to edit a message with the updated progress. - - Other Parameters: - client (:obj:`Client`): - The Client itself, useful when you want to call other API methods inside the callback function. - - current (``int``): - The amount of bytes uploaded so far. - - total (``int``): - The size of the file. - - *args (``tuple``, *optional*): - Extra custom arguments as defined in the *progress_args* parameter. - You can either keep *\*args* or add every single extra argument in your function signature. - - Returns: - :obj:`Message`: On success, the sent animation message is returned. - - ``None`` -- In case the upload is deliberately stopped with :meth:`stop_transmission`. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - file = None - style = self.html if parse_mode.lower() == "html" else self.markdown - - try: - if os.path.exists(animation): - thumb = None if thumb is None else self.save_file(thumb) - file = self.save_file(animation, progress=progress, progress_args=progress_args) - media = types.InputMediaUploadedDocument( - mime_type=self.guess_mime_type(animation) or "video/mp4", - file=file, - thumb=thumb, - attributes=[ - types.DocumentAttributeVideo( - supports_streaming=True, - duration=duration, - w=width, - h=height - ), - types.DocumentAttributeFilename(file_name=os.path.basename(animation)), - types.DocumentAttributeAnimated() - ] - ) - elif animation.startswith("http"): - media = types.InputMediaDocumentExternal( - url=animation - ) - else: - try: - decoded = utils.decode(animation) - fmt = " 24 else " -# -# 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 . - -import binascii -import os -import struct -from typing import Union - -import pyrogram -from pyrogram.api import functions, types -from pyrogram.errors import FileIdInvalid, FilePartMissing -from pyrogram.client.ext import BaseClient, utils - - -class SendAudio(BaseClient): - def send_audio( - self, - chat_id: Union[int, str], - audio: str, - caption: str = "", - parse_mode: str = "", - duration: int = 0, - performer: str = None, - title: str = None, - thumb: str = None, - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union[ - "pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply" - ] = None, - progress: callable = None, - progress_args: tuple = () - ) -> Union["pyrogram.Message", None]: - """Use this method to send audio files. - - For sending voice messages, use the :obj:`send_voice()` method instead. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - audio (``str``): - Audio file to send. - Pass a file_id as string to send an audio file that exists on the Telegram servers, - pass an HTTP URL as a string for Telegram to get an audio file from the Internet, or - pass a file path as string to upload a new audio file that exists on your local machine. - - caption (``str``, *optional*): - Audio caption, 0-1024 characters. - - parse_mode (``str``, *optional*): - Pass "markdown" or "html" if you want Telegram apps to show bold, italic, fixed-width text or inline - URLs in your caption. Defaults to "markdown". - - duration (``int``, *optional*): - Duration of the audio in seconds. - - performer (``str``, *optional*): - Performer. - - title (``str``, *optional*): - Track name. - - thumb (``str``, *optional*): - Thumbnail of the music file album cover. - The thumbnail should be in JPEG format and less than 200 KB in size. - A thumbnail's width and height should not exceed 320 pixels. - Thumbnails can't be reused and can be only uploaded as a new file. - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - reply_to_message_id (``int``, *optional*): - If the message is a reply, ID of the original message. - - reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*): - Additional interface options. An object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. - - progress (``callable``, *optional*): - Pass a callback function to view the upload progress. - The function must take *(client, current, total, \*args)* as positional arguments (look at the section - below for a detailed description). - - progress_args (``tuple``, *optional*): - Extra custom arguments for the progress callback function. Useful, for example, if you want to pass - a chat_id and a message_id in order to edit a message with the updated progress. - - Other Parameters: - client (:obj:`Client`): - The Client itself, useful when you want to call other API methods inside the callback function. - - current (``int``): - The amount of bytes uploaded so far. - - total (``int``): - The size of the file. - - *args (``tuple``, *optional*): - Extra custom arguments as defined in the *progress_args* parameter. - You can either keep *\*args* or add every single extra argument in your function signature. - - Returns: - :obj:`Message`: On success, the sent audio message is returned. - - ``None`` -- In case the upload is deliberately stopped with :meth:`stop_transmission`. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - file = None - style = self.html if parse_mode.lower() == "html" else self.markdown - - try: - if os.path.exists(audio): - thumb = None if thumb is None else self.save_file(thumb) - file = self.save_file(audio, progress=progress, progress_args=progress_args) - media = types.InputMediaUploadedDocument( - mime_type=self.guess_mime_type(audio) or "audio/mpeg", - file=file, - thumb=thumb, - attributes=[ - types.DocumentAttributeAudio( - duration=duration, - performer=performer, - title=title - ), - types.DocumentAttributeFilename(file_name=os.path.basename(audio)) - ] - ) - elif audio.startswith("http"): - media = types.InputMediaDocumentExternal( - url=audio - ) - else: - try: - decoded = utils.decode(audio) - fmt = " 24 else " -# -# 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 . - -import binascii -import struct -from typing import Union - -import pyrogram -from pyrogram.api import functions, types -from pyrogram.errors import FileIdInvalid -from pyrogram.client.ext import BaseClient, utils - - -class SendCachedMedia(BaseClient): - def send_cached_media( - self, - chat_id: Union[int, str], - file_id: str, - caption: str = "", - parse_mode: str = "", - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union[ - "pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply" - ] = None - ) -> Union["pyrogram.Message", None]: - """Use this method to send any media stored on the Telegram servers using a file_id. - - This convenience method works with any valid file_id only. - It does the same as calling the relevant method for sending media using a file_id, thus saving you from the - hassle of using the correct method for the media the file_id is pointing to. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - file_id (``str``): - Media to send. - Pass a file_id as string to send a media that exists on the Telegram servers. - - caption (``bool``, *optional*): - Media caption, 0-1024 characters. - - parse_mode (``str``, *optional*): - Pass "markdown" or "html" if you want Telegram apps to show bold, italic, fixed-width text or inline - URLs in your caption. Defaults to "markdown". - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - reply_to_message_id (``int``, *optional*): - If the message is a reply, ID of the original message. - - reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*): - Additional interface options. An object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. - - Returns: - :obj:`Message`: On success, the sent media message is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - style = self.html if parse_mode.lower() == "html" else self.markdown - - try: - decoded = utils.decode(file_id) - fmt = " 24 else " -# -# 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 typing import Union - -from pyrogram.api import functions, types -from pyrogram.client.ext import BaseClient -import json - - -class ChatAction: - TYPING = types.SendMessageTypingAction - UPLOAD_PHOTO = types.SendMessageUploadPhotoAction - RECORD_VIDEO = types.SendMessageRecordVideoAction - UPLOAD_VIDEO = types.SendMessageUploadVideoAction - RECORD_AUDIO = types.SendMessageRecordAudioAction - UPLOAD_AUDIO = types.SendMessageUploadAudioAction - UPLOAD_DOCUMENT = types.SendMessageUploadDocumentAction - FIND_LOCATION = types.SendMessageGeoLocationAction - RECORD_VIDEO_NOTE = types.SendMessageRecordRoundAction - UPLOAD_VIDEO_NOTE = types.SendMessageUploadRoundAction - PLAYING = types.SendMessageGamePlayAction - CHOOSE_CONTACT = types.SendMessageChooseContactAction - CANCEL = types.SendMessageCancelAction - - -POSSIBLE_VALUES = list(map(lambda x: x.lower(), filter(lambda x: not x.startswith("__"), ChatAction.__dict__.keys()))) - - -class SendChatAction(BaseClient): - def send_chat_action(self, chat_id: Union[int, str], action: str) -> bool: - """Use this method when you need to tell the other party that something is happening on your side. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - action (``str``): - Type of action to broadcast. Choose one, depending on what the user is about to receive: *"typing"* for - text messages, *"upload_photo"* for photos, *"record_video"* or *"upload_video"* for videos, - *"record_audio"* or *"upload_audio"* for audio files, *"upload_document"* for general files, - *"find_location"* for location data, *"record_video_note"* or *"upload_video_note"* for video notes, - *"choose_contact"* for contacts, *"playing"* for games or *"cancel"* to cancel any chat action currently - displayed. - - Returns: - ``bool``: On success, True is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - ValueError: In case the provided string is not a valid ChatAction. - """ - - try: - action = ChatAction.__dict__[action.upper()] - except KeyError: - raise ValueError("Invalid chat action '{}'. Possible values are: {}".format( - action, json.dumps(POSSIBLE_VALUES, indent=4))) from None - - if "Upload" in action.__name__: - action = action(progress=0) - else: - action = action() - - return self.send( - functions.messages.SetTyping( - peer=self.resolve_peer(chat_id), - action=action - ) - ) diff --git a/pyrogram/client/methods/messages/send_contact.py b/pyrogram/client/methods/messages/send_contact.py deleted file mode 100644 index 27df6505f88..00000000000 --- a/pyrogram/client/methods/messages/send_contact.py +++ /dev/null @@ -1,103 +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 typing import Union - -import pyrogram -from pyrogram.api import functions, types -from pyrogram.client.ext import BaseClient - - -class SendContact(BaseClient): - def send_contact( - self, - chat_id: Union[int, str], - phone_number: str, - first_name: str, - last_name: str = None, - vcard: str = None, - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union[ - "pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply" - ] = None - ) -> "pyrogram.Message": - """Use this method to send phone contacts. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - phone_number (``str``): - Contact's phone number. - - first_name (``str``): - Contact's first name. - - last_name (``str``, *optional*): - Contact's last name. - - vcard (``str``, *optional*): - Additional data about the contact in the form of a vCard, 0-2048 bytes - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - reply_to_message_id (``int``, *optional*): - If the message is a reply, ID of the original message. - - reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*): - Additional interface options. An object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. - - Returns: - :obj:`Message`: On success, the sent contact message is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - r = self.send( - functions.messages.SendMedia( - peer=self.resolve_peer(chat_id), - media=types.InputMediaContact( - phone_number=phone_number, - first_name=first_name, - last_name=last_name or "", - vcard=vcard or "" - ), - message="", - silent=disable_notification or None, - reply_to_msg_id=reply_to_message_id, - random_id=self.rnd_id(), - reply_markup=reply_markup.write() if reply_markup else None - ) - ) - - for i in r.updates: - if isinstance(i, (types.UpdateNewMessage, types.UpdateNewChannelMessage)): - return pyrogram.Message._parse( - self, i.message, - {i.id: i for i in r.users}, - {i.id: i for i in r.chats} - ) diff --git a/pyrogram/client/methods/messages/send_document.py b/pyrogram/client/methods/messages/send_document.py deleted file mode 100644 index e2ac0d23b06..00000000000 --- a/pyrogram/client/methods/messages/send_document.py +++ /dev/null @@ -1,185 +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 . - -import binascii -import os -import struct -from typing import Union - -import pyrogram -from pyrogram.api import functions, types -from pyrogram.errors import FileIdInvalid, FilePartMissing -from pyrogram.client.ext import BaseClient, utils - - -class SendDocument(BaseClient): - def send_document( - self, - chat_id: Union[int, str], - document: str, - thumb: str = None, - caption: str = "", - parse_mode: str = "", - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union[ - "pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply" - ] = None, - progress: callable = None, - progress_args: tuple = () - ) -> Union["pyrogram.Message", None]: - """Use this method to send general files. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - document (``str``): - File to send. - Pass a file_id as string to send a file that exists on the Telegram servers, - pass an HTTP URL as a string for Telegram to get a file from the Internet, or - pass a file path as string to upload a new file that exists on your local machine. - - thumb (``str``, *optional*): - Thumbnail of the file sent. - The thumbnail should be in JPEG format and less than 200 KB in size. - A thumbnail's width and height should not exceed 320 pixels. - Thumbnails can't be reused and can be only uploaded as a new file. - - caption (``str``, *optional*): - Document caption, 0-1024 characters. - - parse_mode (``str``, *optional*): - Pass "markdown" or "html" if you want Telegram apps to show bold, italic, fixed-width text or inline - URLs in your caption. Defaults to "markdown". - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - reply_to_message_id (``int``, *optional*): - If the message is a reply, ID of the original message. - - reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*): - Additional interface options. An object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. - - progress (``callable``, *optional*): - Pass a callback function to view the upload progress. - The function must take *(client, current, total, \*args)* as positional arguments (look at the section - below for a detailed description). - - progress_args (``tuple``, *optional*): - Extra custom arguments for the progress callback function. Useful, for example, if you want to pass - a chat_id and a message_id in order to edit a message with the updated progress. - - Other Parameters: - client (:obj:`Client`): - The Client itself, useful when you want to call other API methods inside the callback function. - - current (``int``): - The amount of bytes uploaded so far. - - total (``int``): - The size of the file. - - *args (``tuple``, *optional*): - Extra custom arguments as defined in the *progress_args* parameter. - You can either keep *\*args* or add every single extra argument in your function signature. - - Returns: - :obj:`Message`: On success, the sent document message is returned. - - ``None`` -- In case the upload is deliberately stopped with :meth:`stop_transmission`. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - file = None - style = self.html if parse_mode.lower() == "html" else self.markdown - - try: - if os.path.exists(document): - thumb = None if thumb is None else self.save_file(thumb) - file = self.save_file(document, progress=progress, progress_args=progress_args) - media = types.InputMediaUploadedDocument( - mime_type=self.guess_mime_type(document) or "application/zip", - file=file, - thumb=thumb, - attributes=[ - types.DocumentAttributeFilename(file_name=os.path.basename(document)) - ] - ) - elif document.startswith("http"): - media = types.InputMediaDocumentExternal( - url=document - ) - else: - try: - decoded = utils.decode(document) - fmt = " 24 else " -# -# 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 typing import Union - -import pyrogram -from pyrogram.api import functions, types -from pyrogram.client.ext import BaseClient - - -class SendLocation(BaseClient): - def send_location( - self, - chat_id: Union[int, str], - latitude: float, - longitude: float, - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union[ - "pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply" - ] = None - ) -> "pyrogram.Message": - """Use this method to send points on the map. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - latitude (``float``): - Latitude of the location. - - longitude (``float``): - Longitude of the location. - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - reply_to_message_id (``int``, *optional*): - If the message is a reply, ID of the original message - - reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*): - Additional interface options. An object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. - - Returns: - :obj:`Message`: On success, the sent location message is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - r = self.send( - functions.messages.SendMedia( - peer=self.resolve_peer(chat_id), - media=types.InputMediaGeoPoint( - geo_point=types.InputGeoPoint( - lat=latitude, - long=longitude - ) - ), - message="", - silent=disable_notification or None, - reply_to_msg_id=reply_to_message_id, - random_id=self.rnd_id(), - reply_markup=reply_markup.write() if reply_markup else None - ) - ) - - for i in r.updates: - if isinstance(i, (types.UpdateNewMessage, types.UpdateNewChannelMessage)): - return pyrogram.Message._parse( - self, i.message, - {i.id: i for i in r.users}, - {i.id: i for i in r.chats} - ) diff --git a/pyrogram/client/methods/messages/send_media_group.py b/pyrogram/client/methods/messages/send_media_group.py deleted file mode 100644 index fe6725f0450..00000000000 --- a/pyrogram/client/methods/messages/send_media_group.py +++ /dev/null @@ -1,215 +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 . - -import binascii -import logging -import os -import struct -import time -from typing import Union, List - -import pyrogram -from pyrogram.api import functions, types -from pyrogram.errors import FileIdInvalid, FloodWait -from pyrogram.client.ext import BaseClient, utils - -log = logging.getLogger(__name__) - - -class SendMediaGroup(BaseClient): - # TODO: Add progress parameter - # TODO: Figure out how to send albums using URLs - def send_media_group( - self, - chat_id: Union[int, str], - media: List[Union["pyrogram.InputMediaPhoto", "pyrogram.InputMediaVideo"]], - disable_notification: bool = None, - reply_to_message_id: int = None - ): - """Use this method to send a group of photos or videos as an album. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - media (List of :obj:`InputMediaPhoto` and :obj:`InputMediaVideo`): - A list describing photos and videos to be sent, must include 2–10 items. - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - reply_to_message_id (``int``, *optional*): - If the message is a reply, ID of the original message. - - Returns: - :obj:`Messages`: On success, an object is returned containing all the single messages sent. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - multi_media = [] - - for i in media: - style = self.html if i.parse_mode.lower() == "html" else self.markdown - - if isinstance(i, pyrogram.InputMediaPhoto): - if os.path.exists(i.media): - while True: - try: - media = self.send( - functions.messages.UploadMedia( - peer=self.resolve_peer(chat_id), - media=types.InputMediaUploadedPhoto( - file=self.save_file(i.media) - ) - ) - ) - except FloodWait as e: - log.warning("Sleeping for {}s".format(e.x)) - time.sleep(e.x) - else: - break - - media = types.InputMediaPhoto( - id=types.InputPhoto( - id=media.photo.id, - access_hash=media.photo.access_hash, - file_reference=b"" - ) - ) - else: - try: - decoded = utils.decode(i.media) - fmt = " 24 else " 24 else " -# -# 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 typing import Union - -import pyrogram -from pyrogram.api import functions, types -from ...ext import BaseClient - - -class SendMessage(BaseClient): - def send_message( - self, - chat_id: Union[int, str], - text: str, - parse_mode: str = "", - disable_web_page_preview: bool = None, - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union[ - "pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply" - ] = None - ) -> "pyrogram.Message": - """Use this method to send text messages. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - text (``str``): - Text of the message to be sent. - - parse_mode (``str``, *optional*): - Pass "markdown" or "html" if you want Telegram apps to show bold, italic, fixed-width text or inline - URLs in your message. Defaults to "markdown". - - disable_web_page_preview (``bool``, *optional*): - Disables link previews for links in this message. - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - reply_to_message_id (``int``, *optional*): - If the message is a reply, ID of the original message. - - reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*): - Additional interface options. An object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. - - Returns: - :obj:`Message`: On success, the sent text message is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - style = self.html if parse_mode.lower() == "html" else self.markdown - message, entities = style.parse(text).values() - - r = self.send( - functions.messages.SendMessage( - peer=self.resolve_peer(chat_id), - no_webpage=disable_web_page_preview or None, - silent=disable_notification or None, - reply_to_msg_id=reply_to_message_id, - random_id=self.rnd_id(), - reply_markup=reply_markup.write() if reply_markup else None, - message=message, - entities=entities - ) - ) - - if isinstance(r, types.UpdateShortSentMessage): - peer = self.resolve_peer(chat_id) - - peer_id = ( - peer.user_id - if isinstance(peer, types.InputPeerUser) - else -peer.chat_id - ) - - return pyrogram.Message( - message_id=r.id, - chat=pyrogram.Chat( - id=peer_id, - type="private", - client=self - ), - text=message, - date=r.date, - outgoing=r.out, - entities=entities, - client=self - ) - - for i in r.updates: - if isinstance(i, (types.UpdateNewMessage, types.UpdateNewChannelMessage)): - return pyrogram.Message._parse( - self, i.message, - {i.id: i for i in r.users}, - {i.id: i for i in r.chats} - ) diff --git a/pyrogram/client/methods/messages/send_photo.py b/pyrogram/client/methods/messages/send_photo.py deleted file mode 100644 index 5487761e9d3..00000000000 --- a/pyrogram/client/methods/messages/send_photo.py +++ /dev/null @@ -1,181 +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 . - -import binascii -import os -import struct -from typing import Union - -import pyrogram -from pyrogram.api import functions, types -from pyrogram.errors import FileIdInvalid, FilePartMissing -from pyrogram.client.ext import BaseClient, utils - - -class SendPhoto(BaseClient): - def send_photo( - self, - chat_id: Union[int, str], - photo: str, - caption: str = "", - parse_mode: str = "", - ttl_seconds: int = None, - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union[ - "pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply" - ] = None, - progress: callable = None, - progress_args: tuple = () - ) -> Union["pyrogram.Message", None]: - """Use this method to send photos. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - photo (``str``): - Photo to send. - Pass a file_id as string to send a photo that exists on the Telegram servers, - pass an HTTP URL as a string for Telegram to get a photo from the Internet, or - pass a file path as string to upload a new photo that exists on your local machine. - - caption (``bool``, *optional*): - Photo caption, 0-1024 characters. - - parse_mode (``str``, *optional*): - Pass "markdown" or "html" if you want Telegram apps to show bold, italic, fixed-width text or inline - URLs in your caption. Defaults to "markdown". - - ttl_seconds (``int``, *optional*): - Self-Destruct Timer. - If you set a timer, the photo will self-destruct in *ttl_seconds* - seconds after it was viewed. - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - reply_to_message_id (``int``, *optional*): - If the message is a reply, ID of the original message. - - reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*): - Additional interface options. An object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. - - progress (``callable``, *optional*): - Pass a callback function to view the upload progress. - The function must take *(client, current, total, \*args)* as positional arguments (look at the section - below for a detailed description). - - progress_args (``tuple``, *optional*): - Extra custom arguments for the progress callback function. Useful, for example, if you want to pass - a chat_id and a message_id in order to edit a message with the updated progress. - - Other Parameters: - client (:obj:`Client`): - The Client itself, useful when you want to call other API methods inside the callback function. - - current (``int``): - The amount of bytes uploaded so far. - - total (``int``): - The size of the file. - - *args (``tuple``, *optional*): - Extra custom arguments as defined in the *progress_args* parameter. - You can either keep *\*args* or add every single extra argument in your function signature. - - Returns: - :obj:`Message`: On success, the sent photo message is returned. - - ``None`` -- In case the upload is deliberately stopped with :meth:`stop_transmission`. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - file = None - style = self.html if parse_mode.lower() == "html" else self.markdown - - try: - if os.path.exists(photo): - file = self.save_file(photo, progress=progress, progress_args=progress_args) - media = types.InputMediaUploadedPhoto( - file=file, - ttl_seconds=ttl_seconds - ) - elif photo.startswith("http"): - media = types.InputMediaPhotoExternal( - url=photo, - ttl_seconds=ttl_seconds - ) - else: - try: - decoded = utils.decode(photo) - fmt = " 24 else " -# -# 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 typing import Union, List - -import pyrogram -from pyrogram.api import functions, types -from pyrogram.client.ext import BaseClient - - -class SendPoll(BaseClient): - def send_poll( - self, - chat_id: Union[int, str], - question: str, - options: List[str], - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union[ - "pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply" - ] = None - ) -> "pyrogram.Message": - """Use this method to send a new poll. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - question (``str``): - Poll question, 1-255 characters. - - options (List of ``str``): - List of answer options, 2-10 strings 1-100 characters each. - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - reply_to_message_id (``int``, *optional*): - If the message is a reply, ID of the original message. - - reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*): - Additional interface options. An object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. - - Returns: - :obj:`Message`: On success, the sent poll message is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - r = self.send( - functions.messages.SendMedia( - peer=self.resolve_peer(chat_id), - media=types.InputMediaPoll( - poll=types.Poll( - id=0, - question=question, - answers=[ - types.PollAnswer(text=o, option=bytes([i])) - for i, o in enumerate(options) - ] - ) - ), - message="", - silent=disable_notification or None, - reply_to_msg_id=reply_to_message_id, - random_id=self.rnd_id(), - reply_markup=reply_markup.write() if reply_markup else None - ) - ) - - for i in r.updates: - if isinstance(i, (types.UpdateNewMessage, types.UpdateNewChannelMessage)): - return pyrogram.Message._parse( - self, i.message, - {i.id: i for i in r.users}, - {i.id: i for i in r.chats} - ) diff --git a/pyrogram/client/methods/messages/send_sticker.py b/pyrogram/client/methods/messages/send_sticker.py deleted file mode 100644 index 0e314641991..00000000000 --- a/pyrogram/client/methods/messages/send_sticker.py +++ /dev/null @@ -1,166 +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 . - -import binascii -import os -import struct -from typing import Union - -import pyrogram -from pyrogram.api import functions, types -from pyrogram.errors import FileIdInvalid, FilePartMissing -from pyrogram.client.ext import BaseClient, utils - - -class SendSticker(BaseClient): - def send_sticker( - self, - chat_id: Union[int, str], - sticker: str, - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union[ - "pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply" - ] = None, - progress: callable = None, - progress_args: tuple = () - ) -> Union["pyrogram.Message", None]: - """Use this method to send .webp stickers. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - sticker (``str``): - Sticker to send. - Pass a file_id as string to send a sticker that exists on the Telegram servers, - pass an HTTP URL as a string for Telegram to get a .webp sticker file from the Internet, or - pass a file path as string to upload a new sticker that exists on your local machine. - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - reply_to_message_id (``int``, *optional*): - If the message is a reply, ID of the original message. - - reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*): - Additional interface options. An object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. - - progress (``callable``, *optional*): - Pass a callback function to view the upload progress. - The function must take *(client, current, total, \*args)* as positional arguments (look at the section - below for a detailed description). - - progress_args (``tuple``, *optional*): - Extra custom arguments for the progress callback function. Useful, for example, if you want to pass - a chat_id and a message_id in order to edit a message with the updated progress. - - Other Parameters: - client (:obj:`Client`): - The Client itself, useful when you want to call other API methods inside the callback function. - - current (``int``): - The amount of bytes uploaded so far. - - total (``int``): - The size of the file. - - *args (``tuple``, *optional*): - Extra custom arguments as defined in the *progress_args* parameter. - You can either keep *\*args* or add every single extra argument in your function signature. - - Returns: - :obj:`Message`: On success, the sent sticker message is returned. - - ``None`` -- In case the upload is deliberately stopped with :meth:`stop_transmission`. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - file = None - - try: - if os.path.exists(sticker): - file = self.save_file(sticker, progress=progress, progress_args=progress_args) - media = types.InputMediaUploadedDocument( - mime_type=self.guess_mime_type(sticker) or "image/webp", - file=file, - attributes=[ - types.DocumentAttributeFilename(file_name=os.path.basename(sticker)) - ] - ) - elif sticker.startswith("http"): - media = types.InputMediaDocumentExternal( - url=sticker - ) - else: - try: - decoded = utils.decode(sticker) - fmt = " 24 else " -# -# 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 typing import Union - -import pyrogram -from pyrogram.api import functions, types -from pyrogram.client.ext import BaseClient - - -class SendVenue(BaseClient): - def send_venue( - self, - chat_id: Union[int, str], - latitude: float, - longitude: float, - title: str, - address: str, - foursquare_id: str = "", - foursquare_type: str = "", - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union[ - "pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply" - ] = None - ) -> "pyrogram.Message": - """Use this method to send information about a venue. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - latitude (``float``): - Latitude of the venue. - - longitude (``float``): - Longitude of the venue. - - title (``str``): - Name of the venue. - - address (``str``): - Address of the venue. - - foursquare_id (``str``, *optional*): - Foursquare identifier of the venue. - - foursquare_type (``str``, *optional*): - Foursquare type of the venue, if known. - (For example, "arts_entertainment/default", "arts_entertainment/aquarium" or "food/icecream".) - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - reply_to_message_id (``int``, *optional*): - If the message is a reply, ID of the original message - - reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*): - Additional interface options. An object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. - - Returns: - :obj:`Message`: On success, the sent venue message is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - r = self.send( - functions.messages.SendMedia( - peer=self.resolve_peer(chat_id), - media=types.InputMediaVenue( - geo_point=types.InputGeoPoint( - lat=latitude, - long=longitude - ), - title=title, - address=address, - provider="", - venue_id=foursquare_id, - venue_type=foursquare_type - ), - message="", - silent=disable_notification or None, - reply_to_msg_id=reply_to_message_id, - random_id=self.rnd_id(), - reply_markup=reply_markup.write() if reply_markup else None - ) - ) - - for i in r.updates: - if isinstance(i, (types.UpdateNewMessage, types.UpdateNewChannelMessage)): - return pyrogram.Message._parse( - self, i.message, - {i.id: i for i in r.users}, - {i.id: i for i in r.chats} - ) diff --git a/pyrogram/client/methods/messages/send_video.py b/pyrogram/client/methods/messages/send_video.py deleted file mode 100644 index baa91d29634..00000000000 --- a/pyrogram/client/methods/messages/send_video.py +++ /dev/null @@ -1,207 +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 . - -import binascii -import os -import struct -from typing import Union - -import pyrogram -from pyrogram.api import functions, types -from pyrogram.errors import FileIdInvalid, FilePartMissing -from pyrogram.client.ext import BaseClient, utils - - -class SendVideo(BaseClient): - def send_video( - self, - chat_id: Union[int, str], - video: str, - caption: str = "", - parse_mode: str = "", - duration: int = 0, - width: int = 0, - height: int = 0, - thumb: str = None, - supports_streaming: bool = True, - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union[ - "pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply" - ] = None, - progress: callable = None, - progress_args: tuple = () - ) -> Union["pyrogram.Message", None]: - """Use this method to send video files. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - video (``str``): - Video to send. - Pass a file_id as string to send a video that exists on the Telegram servers, - pass an HTTP URL as a string for Telegram to get a video from the Internet, or - pass a file path as string to upload a new video that exists on your local machine. - - caption (``str``, *optional*): - Video caption, 0-1024 characters. - - parse_mode (``str``, *optional*): - Pass "markdown" or "html" if you want Telegram apps to show bold, italic, fixed-width text or inline - URLs in your caption. Defaults to "markdown". - - duration (``int``, *optional*): - Duration of sent video in seconds. - - width (``int``, *optional*): - Video width. - - height (``int``, *optional*): - Video height. - - thumb (``str``, *optional*): - Thumbnail of the video sent. - The thumbnail should be in JPEG format and less than 200 KB in size. - A thumbnail's width and height should not exceed 320 pixels. - Thumbnails can't be reused and can be only uploaded as a new file. - - supports_streaming (``bool``, *optional*): - Pass True, if the uploaded video is suitable for streaming. - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - reply_to_message_id (``int``, *optional*): - If the message is a reply, ID of the original message. - - reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*): - Additional interface options. An object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. - - progress (``callable``, *optional*): - Pass a callback function to view the upload progress. - The function must take *(client, current, total, \*args)* as positional arguments (look at the section - below for a detailed description). - - progress_args (``tuple``, *optional*): - Extra custom arguments for the progress callback function. Useful, for example, if you want to pass - a chat_id and a message_id in order to edit a message with the updated progress. - - Other Parameters: - client (:obj:`Client`): - The Client itself, useful when you want to call other API methods inside the callback function. - - current (``int``): - The amount of bytes uploaded so far. - - total (``int``): - The size of the file. - - *args (``tuple``, *optional*): - Extra custom arguments as defined in the *progress_args* parameter. - You can either keep *\*args* or add every single extra argument in your function signature. - - Returns: - :obj:`Message`: On success, the sent video message is returned. - - ``None`` -- In case the upload is deliberately stopped with :meth:`stop_transmission`. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - file = None - style = self.html if parse_mode.lower() == "html" else self.markdown - - try: - if os.path.exists(video): - thumb = None if thumb is None else self.save_file(thumb) - file = self.save_file(video, progress=progress, progress_args=progress_args) - media = types.InputMediaUploadedDocument( - mime_type=self.guess_mime_type(video) or "video/mp4", - file=file, - thumb=thumb, - attributes=[ - types.DocumentAttributeVideo( - supports_streaming=supports_streaming or None, - duration=duration, - w=width, - h=height - ), - types.DocumentAttributeFilename(file_name=os.path.basename(video)) - ] - ) - elif video.startswith("http"): - media = types.InputMediaDocumentExternal( - url=video - ) - else: - try: - decoded = utils.decode(video) - fmt = " 24 else " -# -# 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 . - -import binascii -import os -import struct -from typing import Union - -import pyrogram -from pyrogram.api import functions, types -from pyrogram.errors import FileIdInvalid, FilePartMissing -from pyrogram.client.ext import BaseClient, utils - - -class SendVideoNote(BaseClient): - def send_video_note( - self, - chat_id: Union[int, str], - video_note: str, - duration: int = 0, - length: int = 1, - thumb: str = None, - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union[ - "pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply" - ] = None, - progress: callable = None, - progress_args: tuple = () - ) -> Union["pyrogram.Message", None]: - """Use this method to send video messages. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - video_note (``str``): - Video note to send. - Pass a file_id as string to send a video note that exists on the Telegram servers, or - pass a file path as string to upload a new video note that exists on your local machine. - Sending video notes by a URL is currently unsupported. - - duration (``int``, *optional*): - Duration of sent video in seconds. - - length (``int``, *optional*): - Video width and height. - - thumb (``str``, *optional*): - Thumbnail of the video sent. - The thumbnail should be in JPEG format and less than 200 KB in size. - A thumbnail's width and height should not exceed 320 pixels. - Thumbnails can't be reused and can be only uploaded as a new file. - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - reply_to_message_id (``int``, *optional*): - If the message is a reply, ID of the original message - - reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*): - Additional interface options. An object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. - - progress (``callable``, *optional*): - Pass a callback function to view the upload progress. - The function must take *(client, current, total, \*args)* as positional arguments (look at the section - below for a detailed description). - - progress_args (``tuple``, *optional*): - Extra custom arguments for the progress callback function. Useful, for example, if you want to pass - a chat_id and a message_id in order to edit a message with the updated progress. - - Other Parameters: - client (:obj:`Client`): - The Client itself, useful when you want to call other API methods inside the callback function. - - current (``int``): - The amount of bytes uploaded so far. - - total (``int``): - The size of the file. - - *args (``tuple``, *optional*): - Extra custom arguments as defined in the *progress_args* parameter. - You can either keep *\*args* or add every single extra argument in your function signature. - - Returns: - :obj:`Message`: On success, the sent video note message is returned. - - ``None`` -- In case the upload is deliberately stopped with :meth:`stop_transmission`. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - file = None - - try: - if os.path.exists(video_note): - thumb = None if thumb is None else self.save_file(thumb) - file = self.save_file(video_note, progress=progress, progress_args=progress_args) - media = types.InputMediaUploadedDocument( - mime_type=self.guess_mime_type(video_note) or "video/mp4", - file=file, - thumb=thumb, - attributes=[ - types.DocumentAttributeVideo( - round_message=True, - duration=duration, - w=length, - h=length - ) - ] - ) - else: - try: - decoded = utils.decode(video_note) - fmt = " 24 else " -# -# 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 . - -import binascii -import os -import struct -from typing import Union - -import pyrogram -from pyrogram.api import functions, types -from pyrogram.errors import FileIdInvalid, FilePartMissing -from pyrogram.client.ext import BaseClient, utils - - -class SendVoice(BaseClient): - def send_voice( - self, - chat_id: Union[int, str], - voice: str, - caption: str = "", - parse_mode: str = "", - duration: int = 0, - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union[ - "pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply" - ] = None, - progress: callable = None, - progress_args: tuple = () - ) -> Union["pyrogram.Message", None]: - """Use this method to send audio files. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - voice (``str``): - Audio file to send. - Pass a file_id as string to send an audio that exists on the Telegram servers, - pass an HTTP URL as a string for Telegram to get an audio from the Internet, or - pass a file path as string to upload a new audio that exists on your local machine. - - caption (``str``, *optional*): - Voice message caption, 0-1024 characters. - - parse_mode (``str``, *optional*): - Pass "markdown" or "html" if you want Telegram apps to show bold, italic, fixed-width text or inline - URLs in your caption. Defaults to "markdown". - - duration (``int``, *optional*): - Duration of the voice message in seconds. - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - reply_to_message_id (``int``, *optional*): - If the message is a reply, ID of the original message - - reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*): - Additional interface options. An object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. - - progress (``callable``, *optional*): - Pass a callback function to view the upload progress. - The function must take *(client, current, total, \*args)* as positional arguments (look at the section - below for a detailed description). - - progress_args (``tuple``, *optional*): - Extra custom arguments for the progress callback function. Useful, for example, if you want to pass - a chat_id and a message_id in order to edit a message with the updated progress. - - Other Parameters: - client (:obj:`Client`): - The Client itself, useful when you want to call other API methods inside the callback function. - - current (``int``): - The amount of bytes uploaded so far. - - total (``int``): - The size of the file. - - *args (``tuple``, *optional*): - Extra custom arguments as defined in the *progress_args* parameter. - You can either keep *\*args* or add every single extra argument in your function signature. - - Returns: - :obj:`Message`: On success, the sent voice message is returned. - - ``None`` -- In case the upload is deliberately stopped with :meth:`stop_transmission`. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - file = None - style = self.html if parse_mode.lower() == "html" else self.markdown - - try: - if os.path.exists(voice): - file = self.save_file(voice, progress=progress, progress_args=progress_args) - media = types.InputMediaUploadedDocument( - mime_type=self.guess_mime_type(voice) or "audio/mpeg", - file=file, - attributes=[ - types.DocumentAttributeAudio( - voice=True, - duration=duration - ) - ] - ) - elif voice.startswith("http"): - media = types.InputMediaDocumentExternal( - url=voice - ) - else: - try: - decoded = utils.decode(voice) - fmt = " 24 else " -# -# 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 typing import Union - -import pyrogram -from pyrogram.api import functions, types -from pyrogram.client.ext import BaseClient - - -class StopPoll(BaseClient): - def stop_poll( - self, - chat_id: Union[int, str], - message_id: int, - reply_markup: "pyrogram.InlineKeyboardMarkup" = None - ) -> "pyrogram.Poll": - """Use this method to stop a poll which was sent by you. - - Stopped polls can't be reopened and nobody will be able to vote in it anymore. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - message_id (``int``): - Identifier of the original message with the poll. - - reply_markup (:obj:`InlineKeyboardMarkup`, *optional*): - An InlineKeyboardMarkup object. - - Returns: - :obj:`Poll`: On success, the stopped poll with the final results is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - poll = self.get_messages(chat_id, message_id).poll - - r = self.send( - functions.messages.EditMessage( - peer=self.resolve_peer(chat_id), - id=message_id, - media=types.InputMediaPoll( - poll=types.Poll( - id=poll.id, - closed=True, - question="", - answers=[] - ) - ), - reply_markup=reply_markup.write() if reply_markup else None - ) - ) - - return pyrogram.Poll._parse(self, r.updates[0]) diff --git a/pyrogram/client/methods/messages/vote_poll.py b/pyrogram/client/methods/messages/vote_poll.py deleted file mode 100644 index 5e7b8ce863a..00000000000 --- a/pyrogram/client/methods/messages/vote_poll.py +++ /dev/null @@ -1,64 +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 typing import Union - -import pyrogram -from pyrogram.api import functions -from pyrogram.client.ext import BaseClient - - -class VotePoll(BaseClient): - def vote_poll( - self, - chat_id: Union[int, str], - message_id: id, - option: int - ) -> "pyrogram.Poll": - """Use this method to vote a poll. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - message_id (``int``): - Identifier of the original message with the poll. - - option (``int``): - Index of the poll option you want to vote for (0 to 9). - - Returns: - :obj:`Poll` - On success, the poll with the chosen option is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - - poll = self.get_messages(chat_id, message_id).poll - - r = self.send( - functions.messages.SendVote( - peer=self.resolve_peer(chat_id), - msg_id=message_id, - options=[poll.options[option]._data] - ) - ) - - return pyrogram.Poll._parse(self, r.updates[0]) diff --git a/pyrogram/client/methods/password/__init__.py b/pyrogram/client/methods/password/__init__.py deleted file mode 100644 index 8a29b0a49d4..00000000000 --- a/pyrogram/client/methods/password/__init__.py +++ /dev/null @@ -1,29 +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 .change_cloud_password import ChangeCloudPassword -from .enable_cloud_password import EnableCloudPassword -from .remove_cloud_password import RemoveCloudPassword - - -class Password( - RemoveCloudPassword, - ChangeCloudPassword, - EnableCloudPassword -): - pass diff --git a/pyrogram/client/methods/password/change_cloud_password.py b/pyrogram/client/methods/password/change_cloud_password.py deleted file mode 100644 index b9ee37d5597..00000000000 --- a/pyrogram/client/methods/password/change_cloud_password.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 . - -import os - -from pyrogram.api import functions, types -from .utils import compute_hash, compute_check, btoi, itob -from ...ext import BaseClient - - -class ChangeCloudPassword(BaseClient): - def change_cloud_password( - self, - current_password: str, - new_password: str, - new_hint: str = "" - ) -> bool: - """Use this method to change your Two-Step Verification password (Cloud Password) with a new one. - - Parameters: - current_password (``str``): - Your current password. - - new_password (``str``): - Your new password. - - new_hint (``str``, *optional*): - A new password hint. - - Returns: - ``bool``: True on success. - - Raises: - RPCError: In case of a Telegram RPC error. - ValueError: In case there is no cloud password to change. - """ - r = self.send(functions.account.GetPassword()) - - if not r.has_password: - raise ValueError("There is no cloud password to change") - - r.new_algo.salt1 += os.urandom(32) - new_hash = btoi(compute_hash(r.new_algo, new_password)) - new_hash = itob(pow(r.new_algo.g, new_hash, btoi(r.new_algo.p))) - - self.send( - functions.account.UpdatePasswordSettings( - password=compute_check(r, current_password), - new_settings=types.account.PasswordInputSettings( - new_algo=r.new_algo, - new_password_hash=new_hash, - hint=new_hint - ) - ) - ) - - return True diff --git a/pyrogram/client/methods/password/enable_cloud_password.py b/pyrogram/client/methods/password/enable_cloud_password.py deleted file mode 100644 index e2e05633116..00000000000 --- a/pyrogram/client/methods/password/enable_cloud_password.py +++ /dev/null @@ -1,75 +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 . - -import os - -from pyrogram.api import functions, types -from .utils import compute_hash, btoi, itob -from ...ext import BaseClient - - -class EnableCloudPassword(BaseClient): - def enable_cloud_password( - self, - password: str, - hint: str = "", - email: str = None - ) -> bool: - """Use this method to enable the Two-Step Verification security feature (Cloud Password) on your account. - - This password will be asked when you log-in on a new device in addition to the SMS code. - - Parameters: - password (``str``): - Your password. - - hint (``str``, *optional*): - A password hint. - - email (``str``, *optional*): - Recovery e-mail. - - Returns: - ``bool``: True on success. - - Raises: - RPCError: In case of a Telegram RPC error. - ValueError: In case there is already a cloud password enabled. - """ - r = self.send(functions.account.GetPassword()) - - if r.has_password: - raise ValueError("There is already a cloud password enabled") - - r.new_algo.salt1 += os.urandom(32) - new_hash = btoi(compute_hash(r.new_algo, password)) - new_hash = itob(pow(r.new_algo.g, new_hash, btoi(r.new_algo.p))) - - self.send( - functions.account.UpdatePasswordSettings( - password=types.InputCheckPasswordEmpty(), - new_settings=types.account.PasswordInputSettings( - new_algo=r.new_algo, - new_password_hash=new_hash, - hint=hint, - email=email - ) - ) - ) - - return True diff --git a/pyrogram/client/methods/password/remove_cloud_password.py b/pyrogram/client/methods/password/remove_cloud_password.py deleted file mode 100644 index 37160530fb4..00000000000 --- a/pyrogram/client/methods/password/remove_cloud_password.py +++ /dev/null @@ -1,58 +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 pyrogram.api import functions, types -from .utils import compute_check -from ...ext import BaseClient - - -class RemoveCloudPassword(BaseClient): - def remove_cloud_password( - self, - password: str - ) -> bool: - """Use this method to turn off the Two-Step Verification security feature (Cloud Password) on your account. - - Parameters: - password (``str``): - Your current password. - - Returns: - ``bool``: True on success. - - Raises: - RPCError: In case of a Telegram RPC error. - ValueError: In case there is no cloud password to remove. - """ - r = self.send(functions.account.GetPassword()) - - if not r.has_password: - raise ValueError("There is no cloud password to remove") - - self.send( - functions.account.UpdatePasswordSettings( - password=compute_check(r, password), - new_settings=types.account.PasswordInputSettings( - new_algo=types.PasswordKdfAlgoUnknown(), - new_password_hash=b"", - hint="" - ) - ) - ) - - return True diff --git a/pyrogram/client/methods/password/utils.py b/pyrogram/client/methods/password/utils.py deleted file mode 100644 index 3a29976a6b8..00000000000 --- a/pyrogram/client/methods/password/utils.py +++ /dev/null @@ -1,104 +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 . - -import hashlib -import os - -from pyrogram.api import types - - -def btoi(b: bytes) -> int: - return int.from_bytes(b, "big") - - -def itob(i: int) -> bytes: - return i.to_bytes(256, "big") - - -def sha256(data: bytes) -> bytes: - return hashlib.sha256(data).digest() - - -def xor(a: bytes, b: bytes) -> bytes: - return bytes(i ^ j for i, j in zip(a, b)) - - -def compute_hash(algo: types.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow, password: str) -> bytes: - hash1 = sha256(algo.salt1 + password.encode() + algo.salt1) - hash2 = sha256(algo.salt2 + hash1 + algo.salt2) - hash3 = hashlib.pbkdf2_hmac("sha512", hash2, algo.salt1, 100000) - - return sha256(algo.salt2 + hash3 + algo.salt2) - - -# noinspection PyPep8Naming -def compute_check(r: types.account.Password, password: str) -> types.InputCheckPasswordSRP: - algo = r.current_algo - - p_bytes = algo.p - p = btoi(algo.p) - - g_bytes = itob(algo.g) - g = algo.g - - B_bytes = r.srp_B - B = btoi(B_bytes) - - srp_id = r.srp_id - - x_bytes = compute_hash(algo, password) - x = btoi(x_bytes) - - g_x = pow(g, x, p) - - k_bytes = sha256(p_bytes + g_bytes) - k = btoi(k_bytes) - - kg_x = (k * g_x) % p - - while True: - a_bytes = os.urandom(256) - a = btoi(a_bytes) - - A = pow(g, a, p) - A_bytes = itob(A) - - u = btoi(sha256(A_bytes + B_bytes)) - - if u > 0: - break - - g_b = (B - kg_x) % p - - ux = u * x - a_ux = a + ux - S = pow(g_b, a_ux, p) - S_bytes = itob(S) - - K_bytes = sha256(S_bytes) - - M1_bytes = sha256( - xor(sha256(p_bytes), sha256(g_bytes)) - + sha256(algo.salt1) - + sha256(algo.salt2) - + A_bytes - + B_bytes - + K_bytes - ) - - return types.InputCheckPasswordSRP(srp_id=srp_id, A=A_bytes, M1=M1_bytes) diff --git a/pyrogram/client/methods/users/__init__.py b/pyrogram/client/methods/users/__init__.py deleted file mode 100644 index d67a18bda2b..00000000000 --- a/pyrogram/client/methods/users/__init__.py +++ /dev/null @@ -1,37 +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 .delete_user_profile_photos import DeleteUserProfilePhotos -from .get_me import GetMe -from .get_user_profile_photos import GetUserProfilePhotos -from .get_user_profile_photos_count import GetUserProfilePhotosCount -from .get_users import GetUsers -from .set_user_profile_photo import SetUserProfilePhoto -from .update_username import UpdateUsername - - -class Users( - GetUserProfilePhotos, - SetUserProfilePhoto, - DeleteUserProfilePhotos, - GetUsers, - GetMe, - UpdateUsername, - GetUserProfilePhotosCount -): - pass diff --git a/pyrogram/client/methods/users/delete_user_profile_photos.py b/pyrogram/client/methods/users/delete_user_profile_photos.py deleted file mode 100644 index 4e962245758..00000000000 --- a/pyrogram/client/methods/users/delete_user_profile_photos.py +++ /dev/null @@ -1,63 +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 base64 import b64decode -from struct import unpack -from typing import List, Union - -from pyrogram.api import functions, types -from ...ext import BaseClient - - -class DeleteUserProfilePhotos(BaseClient): - def delete_user_profile_photos( - self, - id: Union[str, List[str]] - ) -> bool: - """Use this method to delete your own profile photos. - - Parameters: - id (``str`` | ``list``): - A single :obj:`Photo` id as string or multiple ids as list of strings for deleting - more than one photos at once. - - Returns: - ``bool``: True on success. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - id = id if isinstance(id, list) else [id] - input_photos = [] - - for i in id: - s = unpack(" -# -# 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 . - -import pyrogram -from pyrogram.api import functions, types -from ...ext import BaseClient - - -class GetMe(BaseClient): - def get_me(self) -> "pyrogram.User": - """A simple method for testing your authorization. Requires no parameters. - - Returns: - :obj:`User`: Basic information about the user or bot. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - return pyrogram.User._parse( - self, - self.send( - functions.users.GetFullUser( - id=types.InputPeerSelf() - ) - ).user - ) diff --git a/pyrogram/client/methods/users/get_user_profile_photos.py b/pyrogram/client/methods/users/get_user_profile_photos.py deleted file mode 100644 index 41dd2593cbf..00000000000 --- a/pyrogram/client/methods/users/get_user_profile_photos.py +++ /dev/null @@ -1,65 +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 typing import Union - -import pyrogram -from pyrogram.api import functions -from ...ext import BaseClient - - -class GetUserProfilePhotos(BaseClient): - def get_user_profile_photos( - self, - user_id: Union[int, str], - offset: int = 0, - limit: int = 100 - ) -> "pyrogram.UserProfilePhotos": - """Use this method to get a list of profile pictures for a user. - - Parameters: - user_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - offset (``int``, *optional*): - Sequential number of the first photo to be returned. - By default, all photos are returned. - - limit (``int``, *optional*): - Limits the number of photos to be retrieved. - Values between 1—100 are accepted. Defaults to 100. - - Returns: - :obj:`UserProfilePhotos`: On success, an object containing a list of the profile photos is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - return pyrogram.UserProfilePhotos._parse( - self, - self.send( - functions.photos.GetUserPhotos( - user_id=self.resolve_peer(user_id), - offset=offset, - max_id=0, - limit=limit - ) - ) - ) diff --git a/pyrogram/client/methods/users/get_user_profile_photos_count.py b/pyrogram/client/methods/users/get_user_profile_photos_count.py deleted file mode 100644 index 595352c3da3..00000000000 --- a/pyrogram/client/methods/users/get_user_profile_photos_count.py +++ /dev/null @@ -1,54 +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 typing import Union - -from pyrogram.api import functions, types -from ...ext import BaseClient - - -class GetUserProfilePhotosCount(BaseClient): - def get_user_profile_photos_count(self, user_id: Union[int, str]) -> int: - """Use this method to get the total count of profile pictures for a user. - - Parameters: - user_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - Returns: - ``int``: On success, an integer is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - - r = self.send( - functions.photos.GetUserPhotos( - user_id=self.resolve_peer(user_id), - offset=0, - max_id=0, - limit=1 - ) - ) - - if isinstance(r, types.photos.Photos): - return len(r.photos) - else: - return r.count diff --git a/pyrogram/client/methods/users/get_users.py b/pyrogram/client/methods/users/get_users.py deleted file mode 100644 index bbcdebae55e..00000000000 --- a/pyrogram/client/methods/users/get_users.py +++ /dev/null @@ -1,63 +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 typing import Iterable, Union, List - -import pyrogram -from pyrogram.api import functions -from ...ext import BaseClient - - -class GetUsers(BaseClient): - def get_users( - self, - user_ids: Union[Iterable[Union[int, str]], int, str] - ) -> Union["pyrogram.User", List["pyrogram.User"]]: - """Use this method to get information about a user. - You can retrieve up to 200 users at once. - - Parameters: - user_ids (``iterable``): - A list of User identifiers (id or username) or a single user id/username. - For a contact that exists in your Telegram address book you can use his phone number (str). - Iterators and Generators are also accepted. - - Returns: - :obj:`User`: In case *user_ids* was an integer or string. - - List of :obj:`User` -- In case *user_ids* was an iterable, even if the iterable contained one item only. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - is_iterable = not isinstance(user_ids, (int, str)) - user_ids = list(user_ids) if is_iterable else [user_ids] - user_ids = [self.resolve_peer(i) for i in user_ids] - - r = self.send( - functions.users.GetUsers( - id=user_ids - ) - ) - - users = [] - - for i in r: - users.append(pyrogram.User._parse(self, i)) - - return users if is_iterable else users[0] diff --git a/pyrogram/client/methods/users/set_user_profile_photo.py b/pyrogram/client/methods/users/set_user_profile_photo.py deleted file mode 100644 index e873cf40dc7..00000000000 --- a/pyrogram/client/methods/users/set_user_profile_photo.py +++ /dev/null @@ -1,51 +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 pyrogram.api import functions -from ...ext import BaseClient - - -class SetUserProfilePhoto(BaseClient): - def set_user_profile_photo( - self, - photo: str - ) -> bool: - """Use this method to set a new profile photo. - - This method only works for Users. - Bots profile photos must be set using BotFather. - - Parameters: - photo (``str``): - Profile photo to set. - Pass a file path as string to upload a new photo that exists on your local machine. - - Returns: - ``bool``: True on success. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - - return bool( - self.send( - functions.photos.UploadProfilePhoto( - file=self.save_file(photo) - ) - ) - ) diff --git a/pyrogram/client/methods/users/update_username.py b/pyrogram/client/methods/users/update_username.py deleted file mode 100644 index 8b5b37ae01c..00000000000 --- a/pyrogram/client/methods/users/update_username.py +++ /dev/null @@ -1,53 +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 typing import Union - -from pyrogram.api import functions -from ...ext import BaseClient - - -class UpdateUsername(BaseClient): - def update_username( - self, - username: Union[str, None] - ) -> bool: - """Use this method to update your own username. - - This method only works for users, not bots. Bot usernames must be changed via Bot Support or by recreating - them from scratch using BotFather. To update a channel or supergroup username you can use - :meth:`update_chat_username`. - - Parameters: - username (``str`` | ``None``): - Username to set. "" (empty string) or None to remove it. - - Returns: - ``bool``: True on success. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - - return bool( - self.send( - functions.account.UpdateUsername( - username=username or "" - ) - ) - ) diff --git a/pyrogram/client/style/__init__.py b/pyrogram/client/style/__init__.py deleted file mode 100644 index 768cee7bb1a..00000000000 --- a/pyrogram/client/style/__init__.py +++ /dev/null @@ -1,20 +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 .html import HTML -from .markdown import Markdown diff --git a/pyrogram/client/style/html.py b/pyrogram/client/style/html.py deleted file mode 100644 index 9c0a372c496..00000000000 --- a/pyrogram/client/style/html.py +++ /dev/null @@ -1,127 +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 . - -import re -from collections import OrderedDict - -import pyrogram -from pyrogram.api.types import ( - MessageEntityBold as Bold, - MessageEntityItalic as Italic, - MessageEntityCode as Code, - MessageEntityTextUrl as Url, - MessageEntityPre as Pre, - MessageEntityMentionName as MentionInvalid, - InputMessageEntityMentionName as Mention, -) -from pyrogram.errors import PeerIdInvalid -from . import utils - - -class HTML: - HTML_RE = re.compile(r"<(\w+)(?: href=([\"'])([^<]+)\2)?>([^>]+)") - MENTION_RE = re.compile(r"tg://user\?id=(\d+)") - - def __init__(self, client: "pyrogram.BaseClient" = None): - self.client = client - - def parse(self, message: str): - entities = [] - message = utils.add_surrogates(str(message or "")) - offset = 0 - - for match in self.HTML_RE.finditer(message): - start = match.start() - offset - style, url, body = match.group(1, 3, 4) - - if url: - mention = self.MENTION_RE.match(url) - - if mention: - user_id = int(mention.group(1)) - - try: - input_user = self.client.resolve_peer(user_id) - except PeerIdInvalid: - input_user = None - - entity = ( - Mention(offset=start, length=len(body), user_id=input_user) - if input_user else MentionInvalid(offset=start, length=len(body), user_id=user_id) - ) - else: - entity = Url(offset=start, length=len(body), url=url) - else: - if style == "b" or style == "strong": - entity = Bold(offset=start, length=len(body)) - elif style == "i" or style == "em": - entity = Italic(offset=start, length=len(body)) - elif style == "code": - entity = Code(offset=start, length=len(body)) - elif style == "pre": - entity = Pre(offset=start, length=len(body), language="") - else: - continue - - entities.append(entity) - message = message.replace(match.group(), body) - offset += len(style) * 2 + 5 + (len(url) + 8 if url else 0) - - # TODO: OrderedDict to be removed in Python3.6 - return OrderedDict([ - ("message", utils.remove_surrogates(message)), - ("entities", entities) - ]) - - def unparse(self, message: str, entities: list): - message = utils.add_surrogates(message).strip() - offset = 0 - - for entity in entities: - start = entity.offset + offset - type = entity.type - url = entity.url - user = entity.user - sub = message[start: start + entity.length] - - if type == "bold": - style = "b" - elif type == "italic": - style = "i" - elif type == "code": - style = "code" - elif type == "pre": - style = "pre" - elif type == "text_link": - offset += 15 + len(url) - message = message[:start] + message[start:].replace( - sub, "{}".format(url, sub), 1) - continue - elif type == "text_mention": - offset += 28 + len(str(user.id)) - message = message[:start] + message[start:].replace( - sub, "{}".format(user.id, sub), 1) - continue - else: - continue - - offset += len(style) * 2 + 5 - message = message[:start] + message[start:].replace( - sub, "<{0}>{1}".format(style, sub), 1) - - return utils.remove_surrogates(message) diff --git a/pyrogram/client/style/markdown.py b/pyrogram/client/style/markdown.py deleted file mode 100644 index adb86e94fc1..00000000000 --- a/pyrogram/client/style/markdown.py +++ /dev/null @@ -1,148 +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 . - -import re -from collections import OrderedDict - -import pyrogram -from pyrogram.api.types import ( - MessageEntityBold as Bold, - MessageEntityItalic as Italic, - MessageEntityCode as Code, - MessageEntityTextUrl as Url, - MessageEntityPre as Pre, - MessageEntityMentionName as MentionInvalid, - InputMessageEntityMentionName as Mention -) -from pyrogram.errors import PeerIdInvalid -from . import utils - - -class Markdown: - BOLD_DELIMITER = "**" - ITALIC_DELIMITER = "__" - CODE_DELIMITER = "`" - PRE_DELIMITER = "```" - - MARKDOWN_RE = re.compile(r"({d})([\w\W]*?)\1|\[([^[]+?)\]\(([^(]+?)\)".format( - d="|".join( - ["".join(i) for i in [ - ["\{}".format(j) for j in i] - for i in [ - PRE_DELIMITER, - CODE_DELIMITER, - ITALIC_DELIMITER, - BOLD_DELIMITER - ] - ]] - ) - )) - MENTION_RE = re.compile(r"tg://user\?id=(\d+)") - - def __init__(self, client: "pyrogram.BaseClient" = None): - self.client = client - - def parse(self, message: str): - message = utils.add_surrogates(str(message or "")).strip() - entities = [] - offset = 0 - - for match in self.MARKDOWN_RE.finditer(message): - start = match.start() - offset - style, body, text, url = match.groups() - - if url: - mention = self.MENTION_RE.match(url) - - if mention: - user_id = int(mention.group(1)) - - try: - input_user = self.client.resolve_peer(user_id) - except PeerIdInvalid: - input_user = None - - entity = ( - Mention(offset=start, length=len(text), user_id=input_user) - if input_user else MentionInvalid(offset=start, length=len(text), user_id=user_id) - ) - else: - entity = Url(offset=start, length=len(text), url=url) - - body = text - offset += len(url) + 4 - else: - if style == self.BOLD_DELIMITER: - entity = Bold(offset=start, length=len(body)) - elif style == self.ITALIC_DELIMITER: - entity = Italic(offset=start, length=len(body)) - elif style == self.CODE_DELIMITER: - entity = Code(offset=start, length=len(body)) - elif style == self.PRE_DELIMITER: - entity = Pre(offset=start, length=len(body), language="") - else: - continue - - offset += len(style) * 2 - - entities.append(entity) - message = message.replace(match.group(), body) - - # TODO: OrderedDict to be removed in Python3.6 - return OrderedDict([ - ("message", utils.remove_surrogates(message)), - ("entities", entities) - ]) - - def unparse(self, message: str, entities: list): - message = utils.add_surrogates(message).strip() - offset = 0 - - for entity in entities: - start = entity.offset + offset - type = entity.type - url = entity.url - user = entity.user - sub = message[start: start + entity.length] - - if type == "bold": - style = self.BOLD_DELIMITER - elif type == "italic": - style = self.ITALIC_DELIMITER - elif type == "code": - style = self.CODE_DELIMITER - elif type == "pre": - style = self.PRE_DELIMITER - elif type == "text_link": - offset += 4 + len(url) - message = message[:start] + message[start:].replace( - sub, "[{}]({})".format(sub, url), 1) - continue - elif type == "text_mention": - offset += 17 + len(str(user.id)) - message = message[:start] + message[start:].replace( - sub, "[{}](tg://user?id={})".format(sub, user.id), 1) - continue - else: - continue - - offset += len(style) * 2 - message = message[:start] + message[start:].replace( - sub, "{0}{1}{0}".format(style, sub), 1) - - return utils.remove_surrogates(message) diff --git a/pyrogram/client/style/utils.py b/pyrogram/client/style/utils.py deleted file mode 100644 index b001f1cf700..00000000000 --- a/pyrogram/client/style/utils.py +++ /dev/null @@ -1,37 +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 . - -import re -from struct import unpack - -# SMP = Supplementary Multilingual Plane: https://en.wikipedia.org/wiki/Plane_(Unicode)#Overview -SMP_RE = re.compile(r"[\U00010000-\U0010FFFF]") - - -def add_surrogates(text): - # Replace each SMP code point with a surrogate pair - return SMP_RE.sub( - lambda match: # Split SMP in two surrogates - "".join(chr(i) for i in unpack(" -# -# 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 .keyboards import * -from .inline_mode import * -from .input_media import * -from .input_message_content import * -from .messages_and_media import * -from .update import * -from .user_and_chats import * diff --git a/pyrogram/client/types/inline_mode/__init__.py b/pyrogram/client/types/inline_mode/__init__.py deleted file mode 100644 index 7a3b30238f3..00000000000 --- a/pyrogram/client/types/inline_mode/__init__.py +++ /dev/null @@ -1,25 +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 .inline_query import InlineQuery -from .inline_query_result import InlineQueryResult -from .inline_query_result_article import InlineQueryResultArticle - -__all__ = [ - "InlineQuery", "InlineQueryResult", "InlineQueryResultArticle" -] diff --git a/pyrogram/client/types/inline_mode/inline_query_result.py b/pyrogram/client/types/inline_mode/inline_query_result.py deleted file mode 100644 index 3e7fcb02b25..00000000000 --- a/pyrogram/client/types/inline_mode/inline_query_result.py +++ /dev/null @@ -1,59 +0,0 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 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 ..pyrogram_type import PyrogramType - -"""- :obj:`InlineQueryResultCachedAudio` - - :obj:`InlineQueryResultCachedDocument` - - :obj:`InlineQueryResultCachedGif` - - :obj:`InlineQueryResultCachedMpeg4Gif` - - :obj:`InlineQueryResultCachedPhoto` - - :obj:`InlineQueryResultCachedSticker` - - :obj:`InlineQueryResultCachedVideo` - - :obj:`InlineQueryResultCachedVoice` - - :obj:`InlineQueryResultAudio` - - :obj:`InlineQueryResultContact` - - :obj:`InlineQueryResultGame` - - :obj:`InlineQueryResultDocument` - - :obj:`InlineQueryResultGif` - - :obj:`InlineQueryResultLocation` - - :obj:`InlineQueryResultMpeg4Gif` - - :obj:`InlineQueryResultPhoto` - - :obj:`InlineQueryResultVenue` - - :obj:`InlineQueryResultVideo` - - :obj:`InlineQueryResultVoice`""" - - -class InlineQueryResult(PyrogramType): - """This object represents one result of an inline query. - - Pyrogram currently supports results of the following 20 types: - - - :obj:`InlineQueryResultArticle` - """ - - __slots__ = ["type", "id"] - - def __init__(self, type: str, id: str): - super().__init__(None) - - self.type = type - self.id = id - - def write(self): - pass diff --git a/pyrogram/client/types/inline_mode/inline_query_result_article.py b/pyrogram/client/types/inline_mode/inline_query_result_article.py deleted file mode 100644 index bc0b4e2004c..00000000000 --- a/pyrogram/client/types/inline_mode/inline_query_result_article.py +++ /dev/null @@ -1,106 +0,0 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 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 typing import Any - -from pyrogram.api import types -from .inline_query_result import InlineQueryResult - - -class InlineQueryResultArticle(InlineQueryResult): - """Represents a link to an article or web page. - - TODO: Hide url? - - Parameters: - id (``str``): - Unique identifier for this result, 1-64 bytes. - - title (``str``): - Title for the result. - - input_message_content (:obj:`InputMessageContent `): - Content of the message to be sent. - - reply_markup (:obj:`InlineKeyboardMarkup `, *optional*): - Inline keyboard attached to the message. - - url (``str``, *optional*): - URL of the result. - - description (``str``, *optional*): - Short description of the result. - - thumb_url (``str``, *optional*): - Url of the thumbnail for the result. - - thumb_width (``int``, *optional*): - Thumbnail width. - - thumb_height (``int``, *optional*): - Thumbnail height. - """ - - __slots__ = [ - "title", "input_message_content", "reply_markup", "url", "description", "thumb_url", "thumb_width", - "thumb_height" - ] - - def __init__( - self, - id: Any, - title: str, - input_message_content, - reply_markup=None, - url: str = None, - description: str = None, - thumb_url: str = None, - thumb_width: int = 0, - thumb_height: int = 0 - ): - super().__init__("article", id) - - self.title = title - self.input_message_content = input_message_content - self.reply_markup = reply_markup - self.url = url - self.description = description - self.thumb_url = thumb_url - self.thumb_width = thumb_width - self.thumb_height = thumb_height - - def write(self): - return types.InputBotInlineResult( - id=str(self.id), - type=self.type, - send_message=self.input_message_content.write(self.reply_markup), - title=self.title, - description=self.description, - url=self.url, - thumb=types.InputWebDocument( - url=self.thumb_url, - size=0, - mime_type="image/jpeg", - attributes=[ - types.DocumentAttributeImageSize( - w=self.thumb_width, - h=self.thumb_height - ) - ] - ) if self.thumb_url else None - ) diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_audio.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_audio.py deleted file mode 100644 index 0ef30b20a72..00000000000 --- a/pyrogram/client/types/inline_mode/todo/inline_query_result_audio.py +++ /dev/null @@ -1,71 +0,0 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 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 pyrogram.client.types.pyrogram_type import PyrogramType - - -class InlineQueryResultAudio(PyrogramType): - """Represents a link to an mp3 audio file. By default, this audio file will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the audio. - - Attributes: - ID: ``0xb0700004`` - - Parameters: - type (``str``): - Type of the result, must be audio. - - id (``str``): - Unique identifier for this result, 1-64 bytes. - - audio_url (``str``): - A valid URL for the audio file. - - title (``str``): - Title. - - caption (``str``, optional): - Caption, 0-200 characters. - - parse_mode (``str``, optional): - Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. - - performer (``str``, optional): - Performer. - - audio_duration (``int`` ``32-bit``, optional): - Audio duration in seconds. - - reply_markup (:obj:`InlineKeyboardMarkup `, optional): - Inline keyboard attached to the message. - - input_message_content (:obj:`InputMessageContent `, optional): - Content of the message to be sent instead of the audio. - - """ - - def __init__(self, type: str, id: str, audio_url: str, title: str, caption: str = None, parse_mode: str = None, performer: str = None, audio_duration: int = None, reply_markup=None, input_message_content=None): - self.type = type # string - self.id = id # string - self.audio_url = audio_url # string - self.title = title # string - self.caption = caption # flags.0?string - self.parse_mode = parse_mode # flags.1?string - self.performer = performer # flags.2?string - self.audio_duration = audio_duration # flags.3?int - self.reply_markup = reply_markup # flags.4?InlineKeyboardMarkup - self.input_message_content = input_message_content # flags.5?InputMessageContent diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_audio.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_audio.py deleted file mode 100644 index dc51139b516..00000000000 --- a/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_audio.py +++ /dev/null @@ -1,103 +0,0 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 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 . - -import binascii -import struct - -from pyrogram.api import types -from pyrogram.errors import FileIdInvalid -from pyrogram.client.ext import utils, BaseClient -from pyrogram.client.style import HTML, Markdown -from pyrogram.client.types.pyrogram_type import PyrogramType - - -class InlineQueryResultCachedAudio(PyrogramType): - """Represents a link to an audio file stored on the Telegram servers. - By default, this audio file will be sent by the user. Alternatively, you can use *input_message_content* to send a - message with the specified content instead of the audio. - - Parameters: - id (``str``): - Unique identifier for this result, 1-64 bytes. - - audio_file_id (``str``): - A valid file identifier for the audio file. - - caption (``str``, *optional*): - Caption, 0-200 characters. - - parse_mode (``str``, *optional*): - Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in - the media caption. - - reply_markup (:obj:`InlineKeyboardMarkup `, *optional*): - Inline keyboard attached to the message. - - input_message_content (:obj:`InputMessageContent `, *optional*): - Content of the message to be sent instead of the audio. - - """ - - def __init__( - self, - id: str, - audio_file_id: str, - caption: str = "", - parse_mode: str = "", - reply_markup=None, - input_message_content=None - ): - self.id = id - self.audio_file_id = audio_file_id - self.caption = caption - self.parse_mode = parse_mode - self.reply_markup = reply_markup - self.input_message_content = input_message_content - - self.style = HTML() if parse_mode.lower() == "html" else Markdown() - - def write(self): - try: - decoded = utils.decode(self.audio_file_id) - fmt = " 24 else " -# -# 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 pyrogram.client.types.pyrogram_type import PyrogramType - - -class InlineQueryResultCachedDocument(PyrogramType): - """Represents a link to a file stored on the Telegram servers. By default, this file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the file. - - Attributes: - ID: ``0xb0700015`` - - Parameters: - type (``str``): - Type of the result, must be document. - - id (``str``): - Unique identifier for this result, 1-64 bytes. - - title (``str``): - Title for the result. - - document_file_id (``str``): - A valid file identifier for the file. - - description (``str``, optional): - Short description of the result. - - caption (``str``, optional): - Caption of the document to be sent, 0-200 characters. - - parse_mode (``str``, optional): - Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. - - reply_markup (:obj:`InlineKeyboardMarkup `, optional): - Inline keyboard attached to the message. - - input_message_content (:obj:`InputMessageContent `, optional): - Content of the message to be sent instead of the file. - - """ - ID = 0xb0700015 - - def __init__(self, type: str, id: str, title: str, document_file_id: str, description: str = None, caption: str = None, parse_mode: str = None, reply_markup=None, input_message_content=None): - self.type = type # string - self.id = id # string - self.title = title # string - self.document_file_id = document_file_id # string - self.description = description # flags.0?string - self.caption = caption # flags.1?string - self.parse_mode = parse_mode # flags.2?string - self.reply_markup = reply_markup # flags.3?InlineKeyboardMarkup - self.input_message_content = input_message_content # flags.4?InputMessageContent diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_gif.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_gif.py deleted file mode 100644 index 8b523dbde14..00000000000 --- a/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_gif.py +++ /dev/null @@ -1,64 +0,0 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 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 pyrogram.client.types.pyrogram_type import PyrogramType - - -class InlineQueryResultCachedGif(PyrogramType): - """Represents a link to an animated GIF file stored on the Telegram servers. By default, this animated GIF file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with specified content instead of the animation. - - Attributes: - ID: ``0xb0700012`` - - Parameters: - type (``str``): - Type of the result, must be gif. - - id (``str``): - Unique identifier for this result, 1-64 bytes. - - gif_file_id (``str``): - A valid file identifier for the GIF file. - - title (``str``, optional): - Title for the result. - - caption (``str``, optional): - Caption of the GIF file to be sent, 0-200 characters. - - parse_mode (``str``, optional): - Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. - - reply_markup (:obj:`InlineKeyboardMarkup `, optional): - Inline keyboard attached to the message. - - input_message_content (:obj:`InputMessageContent `, optional): - Content of the message to be sent instead of the GIF animation. - - """ - ID = 0xb0700012 - - def __init__(self, type: str, id: str, gif_file_id: str, title: str = None, caption: str = None, parse_mode: str = None, reply_markup=None, input_message_content=None): - self.type = type # string - self.id = id # string - self.gif_file_id = gif_file_id # string - self.title = title # flags.0?string - self.caption = caption # flags.1?string - self.parse_mode = parse_mode # flags.2?string - self.reply_markup = reply_markup # flags.3?InlineKeyboardMarkup - self.input_message_content = input_message_content # flags.4?InputMessageContent diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_mpeg4_gif.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_mpeg4_gif.py deleted file mode 100644 index 2082150d339..00000000000 --- a/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_mpeg4_gif.py +++ /dev/null @@ -1,64 +0,0 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 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 pyrogram.client.types.pyrogram_type import PyrogramType - - -class InlineQueryResultCachedMpeg4Gif(PyrogramType): - """Represents a link to a video animation (H.264/MPEG-4 AVC video without sound) stored on the Telegram servers. By default, this animated MPEG-4 file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the animation. - - Attributes: - ID: ``0xb0700013`` - - Parameters: - type (``str``): - Type of the result, must be mpeg4_gif. - - id (``str``): - Unique identifier for this result, 1-64 bytes. - - mpeg4_file_id (``str``): - A valid file identifier for the MP4 file. - - title (``str``, optional): - Title for the result. - - caption (``str``, optional): - Caption of the MPEG-4 file to be sent, 0-200 characters. - - parse_mode (``str``, optional): - Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. - - reply_markup (:obj:`InlineKeyboardMarkup `, optional): - Inline keyboard attached to the message. - - input_message_content (:obj:`InputMessageContent`, optional): - Content of the message to be sent instead of the video animation. - - """ - ID = 0xb0700013 - - def __init__(self, type: str, id: str, mpeg4_file_id: str, title: str = None, caption: str = None, parse_mode: str = None, reply_markup=None, input_message_content=None): - self.type = type # string - self.id = id # string - self.mpeg4_file_id = mpeg4_file_id # string - self.title = title # flags.0?string - self.caption = caption # flags.1?string - self.parse_mode = parse_mode # flags.2?string - self.reply_markup = reply_markup # flags.3?InlineKeyboardMarkup - self.input_message_content = input_message_content # flags.4?InputMessageContent diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_photo.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_photo.py deleted file mode 100644 index 20c1adbcb84..00000000000 --- a/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_photo.py +++ /dev/null @@ -1,68 +0,0 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 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 pyrogram.client.types.pyrogram_type import PyrogramType - - -class InlineQueryResultCachedPhoto(PyrogramType): - """Represents a link to a photo stored on the Telegram servers. By default, this photo will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the photo. - - Attributes: - ID: ``0xb0700011`` - - Parameters: - type (``str``): - Type of the result, must be photo. - - id (``str``): - Unique identifier for this result, 1-64 bytes. - - photo_file_id (``str``): - A valid file identifier of the photo. - - title (``str``, optional): - Title for the result. - - description (``str``, optional): - Short description of the result. - - caption (``str``, optional): - Caption of the photo to be sent, 0-200 characters. - - parse_mode (``str``, optional): - Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. - - reply_markup (:obj:`InlineKeyboardMarkup `, optional): - Inline keyboard attached to the message. - - input_message_content (:obj:`InputMessageContent `, optional): - Content of the message to be sent instead of the photo. - - """ - ID = 0xb0700011 - - def __init__(self, type: str, id: str, photo_file_id: str, title: str = None, description: str = None, caption: str = None, parse_mode: str = None, reply_markup=None, input_message_content=None): - self.type = type # string - self.id = id # string - self.photo_file_id = photo_file_id # string - self.title = title # flags.0?string - self.description = description # flags.1?string - self.caption = caption # flags.2?string - self.parse_mode = parse_mode # flags.3?string - self.reply_markup = reply_markup # flags.4?InlineKeyboardMarkup - self.input_message_content = input_message_content # flags.5?InputMessageContent diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_sticker.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_sticker.py deleted file mode 100644 index 06944debca1..00000000000 --- a/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_sticker.py +++ /dev/null @@ -1,52 +0,0 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 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 pyrogram.client.types.pyrogram_type import PyrogramType - - -class InlineQueryResultCachedSticker(PyrogramType): - """Represents a link to a sticker stored on the Telegram servers. By default, this sticker will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the sticker. - - Attributes: - ID: ``0xb0700014`` - - Parameters: - type (``str``): - Type of the result, must be sticker. - - id (``str``): - Unique identifier for this result, 1-64 bytes. - - sticker_file_id (``str``): - A valid file identifier of the sticker. - - reply_markup (:obj:`InlineKeyboardMarkup `, optional): - Inline keyboard attached to the message. - - input_message_content (:obj:`InputMessageContent `, optional): - Content of the message to be sent instead of the sticker. - - """ - ID = 0xb0700014 - - def __init__(self, type: str, id: str, sticker_file_id: str, reply_markup=None, input_message_content=None): - self.type = type # string - self.id = id # string - self.sticker_file_id = sticker_file_id # string - self.reply_markup = reply_markup # flags.0?InlineKeyboardMarkup - self.input_message_content = input_message_content # flags.1?InputMessageContent diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_video.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_video.py deleted file mode 100644 index e47cae1b8bd..00000000000 --- a/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_video.py +++ /dev/null @@ -1,68 +0,0 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 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 pyrogram.client.types.pyrogram_type import PyrogramType - - -class InlineQueryResultCachedVideo(PyrogramType): - """Represents a link to a video file stored on the Telegram servers. By default, this video file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the video. - - Attributes: - ID: ``0xb0700016`` - - Parameters: - type (``str``): - Type of the result, must be video. - - id (``str``): - Unique identifier for this result, 1-64 bytes. - - video_file_id (``str``): - A valid file identifier for the video file. - - title (``str``): - Title for the result. - - description (``str``, optional): - Short description of the result. - - caption (``str``, optional): - Caption of the video to be sent, 0-200 characters. - - parse_mode (``str``, optional): - Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. - - reply_markup (:obj:`InlineKeyboardMarkup `, optional): - Inline keyboard attached to the message. - - input_message_content (:obj:`InputMessageContent `, optional): - Content of the message to be sent instead of the video. - - """ - ID = 0xb0700016 - - def __init__(self, type: str, id: str, video_file_id: str, title: str, description: str = None, caption: str = None, parse_mode: str = None, reply_markup=None, input_message_content=None): - self.type = type # string - self.id = id # string - self.video_file_id = video_file_id # string - self.title = title # string - self.description = description # flags.0?string - self.caption = caption # flags.1?string - self.parse_mode = parse_mode # flags.2?string - self.reply_markup = reply_markup # flags.3?InlineKeyboardMarkup - self.input_message_content = input_message_content # flags.4?InputMessageContent diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_voice.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_voice.py deleted file mode 100644 index 3f8bb6a39a2..00000000000 --- a/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_voice.py +++ /dev/null @@ -1,64 +0,0 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 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 pyrogram.client.types.pyrogram_type import PyrogramType - - -class InlineQueryResultCachedVoice(PyrogramType): - """Represents a link to a voice message stored on the Telegram servers. By default, this voice message will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the voice message. - - Attributes: - ID: ``0xb0700017`` - - Parameters: - type (``str``): - Type of the result, must be voice. - - id (``str``): - Unique identifier for this result, 1-64 bytes. - - voice_file_id (``str``): - A valid file identifier for the voice message. - - title (``str``): - Voice message title. - - caption (``str``, optional): - Caption, 0-200 characters. - - parse_mode (``str``, optional): - Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. - - reply_markup (:obj:`InlineKeyboardMarkup `, optional): - Inline keyboard attached to the message. - - input_message_content (:obj:`InputMessageContent `, optional): - Content of the message to be sent instead of the voice message. - - """ - ID = 0xb0700017 - - def __init__(self, type: str, id: str, voice_file_id: str, title: str, caption: str = None, parse_mode: str = None, reply_markup=None, input_message_content=None): - self.type = type # string - self.id = id # string - self.voice_file_id = voice_file_id # string - self.title = title # string - self.caption = caption # flags.0?string - self.parse_mode = parse_mode # flags.1?string - self.reply_markup = reply_markup # flags.2?InlineKeyboardMarkup - self.input_message_content = input_message_content # flags.3?InputMessageContent diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_contact.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_contact.py deleted file mode 100644 index 60512f0df52..00000000000 --- a/pyrogram/client/types/inline_mode/todo/inline_query_result_contact.py +++ /dev/null @@ -1,76 +0,0 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 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 pyrogram.client.types.pyrogram_type import PyrogramType - - -class InlineQueryResultContact(PyrogramType): - """Represents a contact with a phone number. By default, this contact will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the contact. - - Attributes: - ID: ``0xb0700009`` - - Parameters: - type (``str``): - Type of the result, must be contact. - - id (``str``): - Unique identifier for this result, 1-64 Bytes. - - phone_number (``str``): - Contact's phone number. - - first_name (``str``): - Contact's first name. - - last_name (``str``, optional): - Contact's last name. - - vcard (``str``, optional): - Additional data about the contact in the form of a vCard, 0-2048 bytes. - - reply_markup (:obj:`InlineKeyboardMarkup `, optional): - Inline keyboard attached to the message. - - input_message_content (:obj:`InputMessageContent `, optional): - Content of the message to be sent instead of the contact. - - thumb_url (``str``, optional): - Url of the thumbnail for the result. - - thumb_width (``int`` ``32-bit``, optional): - Thumbnail width. - - thumb_height (``int`` ``32-bit``, optional): - Thumbnail height. - - """ - ID = 0xb0700009 - - def __init__(self, type: str, id: str, phone_number: str, first_name: str, last_name: str = None, vcard: str = None, reply_markup=None, input_message_content=None, thumb_url: str = None, thumb_width: int = None, thumb_height: int = None): - self.type = type # string - self.id = id # string - self.phone_number = phone_number # string - self.first_name = first_name # string - self.last_name = last_name # flags.0?string - self.vcard = vcard # flags.1?string - self.reply_markup = reply_markup # flags.2?InlineKeyboardMarkup - self.input_message_content = input_message_content # flags.3?InputMessageContent - self.thumb_url = thumb_url # flags.4?string - self.thumb_width = thumb_width # flags.5?int - self.thumb_height = thumb_height # flags.6?int diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_document.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_document.py deleted file mode 100644 index 148ec01d6d1..00000000000 --- a/pyrogram/client/types/inline_mode/todo/inline_query_result_document.py +++ /dev/null @@ -1,84 +0,0 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 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 pyrogram.client.types.pyrogram_type import PyrogramType - - -class InlineQueryResultDocument(PyrogramType): - """Represents a link to a file. By default, this file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the file. Currently, only .PDF and .ZIP files can be sent using this method. - - Attributes: - ID: ``0xb0700006`` - - Parameters: - type (``str``): - Type of the result, must be document. - - id (``str``): - Unique identifier for this result, 1-64 bytes. - - title (``str``): - Title for the result. - - document_url (``str``, optional): - Caption of the document to be sent, 0-200 characters. - - mime_type (``str``, optional): - Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. - - caption (``str``): - A valid URL for the file. - - parse_mode (``str``): - Mime type of the content of the file, either "application/pdf" or "application/zip". - - description (``str``, optional): - Short description of the result. - - reply_markup (:obj:`InlineKeyboardMarkup `, optional): - Inline keyboard attached to the message. - - input_message_content (:obj:`InputMessageContent `, optional): - Content of the message to be sent instead of the file. - - thumb_url (``str``, optional): - URL of the thumbnail (jpeg only) for the file. - - thumb_width (``int`` ``32-bit``, optional): - Thumbnail width. - - thumb_height (``int`` ``32-bit``, optional): - Thumbnail height. - - """ - ID = 0xb0700006 - - def __init__(self, type: str, id: str, title: str, document_url: str, mime_type: str, caption: str = None, parse_mode: str = None, description: str = None, reply_markup=None, input_message_content=None, thumb_url: str = None, thumb_width: int = None, thumb_height: int = None): - self.type = type # string - self.id = id # string - self.title = title # string - self.caption = caption # flags.0?string - self.parse_mode = parse_mode # flags.1?string - self.document_url = document_url # string - self.mime_type = mime_type # string - self.description = description # flags.2?string - self.reply_markup = reply_markup # flags.3?InlineKeyboardMarkup - self.input_message_content = input_message_content # flags.4?InputMessageContent - self.thumb_url = thumb_url # flags.5?string - self.thumb_width = thumb_width # flags.6?int - self.thumb_height = thumb_height # flags.7?int diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_game.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_game.py deleted file mode 100644 index faebacea94d..00000000000 --- a/pyrogram/client/types/inline_mode/todo/inline_query_result_game.py +++ /dev/null @@ -1,48 +0,0 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 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 pyrogram.client.types.pyrogram_type import PyrogramType - - -class InlineQueryResultGame(PyrogramType): - """Represents a Game. - - Attributes: - ID: ``0xb0700010`` - - Parameters: - type (``str``): - Type of the result, must be game. - - id (``str``): - Unique identifier for this result, 1-64 bytes. - - game_short_name (``str``): - Short name of the game. - - reply_markup (:obj:`InlineKeyboardMarkup `, optional): - Inline keyboard attached to the message. - - """ - ID = 0xb0700010 - - def __init__(self, type: str, id: str, game_short_name: str, reply_markup=None): - self.type = type # string - self.id = id # string - self.game_short_name = game_short_name # string - self.reply_markup = reply_markup # flags.0?InlineKeyboardMarkup diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_gif.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_gif.py deleted file mode 100644 index d84e098b4d6..00000000000 --- a/pyrogram/client/types/inline_mode/todo/inline_query_result_gif.py +++ /dev/null @@ -1,80 +0,0 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 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 pyrogram.client.types.pyrogram_type import PyrogramType - - -class InlineQueryResultGif(PyrogramType): - """Represents a link to an animated GIF file. By default, this animated GIF file will be sent by the user with optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the animation. - - Attributes: - ID: ``0xb0700001`` - - Parameters: - type (``str``): - Type of the result, must be gif. - - id (``str``): - Unique identifier for this result, 1-64 bytes. - - gif_url (``str``): - A valid URL for the GIF file. File size must not exceed 1MB. - - thumb_url (``str``, optional): - Width of the GIF. - - gif_width (``int`` ``32-bit``, optional): - Height of the GIF. - - gif_height (``int`` ``32-bit``, optional): - Duration of the GIF. - - gif_duration (``int`` ``32-bit``): - URL of the static thumbnail for the result (jpeg or gif). - - title (``str``, optional): - Title for the result. - - caption (``str``, optional): - Caption of the GIF file to be sent, 0-200 characters. - - parse_mode (``str``, optional): - Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. - - reply_markup (:obj:`InlineKeyboardMarkup `, optional): - Inline keyboard attached to the message. - - input_message_content (:obj:`InputMessageContent `, optional): - Content of the message to be sent instead of the GIF animation. - - """ - ID = 0xb0700001 - - def __init__(self, type: str, id: str, gif_url: str, thumb_url: str, gif_width: int = None, gif_height: int = None, gif_duration: int = None, title: str = None, caption: str = None, parse_mode: str = None, reply_markup=None, input_message_content=None): - self.type = type # string - self.id = id # string - self.gif_url = gif_url # string - self.gif_width = gif_width # flags.0?int - self.gif_height = gif_height # flags.1?int - self.gif_duration = gif_duration # flags.2?int - self.thumb_url = thumb_url # string - self.title = title # flags.3?string - self.caption = caption # flags.4?string - self.parse_mode = parse_mode # flags.5?string - self.reply_markup = reply_markup # flags.6?InlineKeyboardMarkup - self.input_message_content = input_message_content # flags.7?InputMessageContent diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_location.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_location.py deleted file mode 100644 index cd05c8329f9..00000000000 --- a/pyrogram/client/types/inline_mode/todo/inline_query_result_location.py +++ /dev/null @@ -1,76 +0,0 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 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 pyrogram.client.types.pyrogram_type import PyrogramType - - -class InlineQueryResultLocation(PyrogramType): - """Represents a location on a map. By default, the location will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the location. - - Attributes: - ID: ``0xb0700007`` - - Parameters: - type (``str``): - Type of the result, must be location. - - id (``str``): - Unique identifier for this result, 1-64 Bytes. - - latitude (``float`` ``64-bit``): - Location latitude in degrees. - - longitude (``float`` ``64-bit``): - Location longitude in degrees. - - title (``str``): - Location title. - - live_period (``int`` ``32-bit``, optional): - Period in seconds for which the location can be updated, should be between 60 and 86400. - - reply_markup (:obj:`InlineKeyboardMarkup `, optional): - Inline keyboard attached to the message. - - input_message_content (:obj:`InputMessageContent `, optional): - Content of the message to be sent instead of the location. - - thumb_url (``str``, optional): - Url of the thumbnail for the result. - - thumb_width (``int`` ``32-bit``, optional): - Thumbnail width. - - thumb_height (``int`` ``32-bit``, optional): - Thumbnail height. - - """ - ID = 0xb0700007 - - def __init__(self, type: str, id: str, latitude: float, longitude: float, title: str, live_period: int = None, reply_markup=None, input_message_content=None, thumb_url: str = None, thumb_width: int = None, thumb_height: int = None): - self.type = type # string - self.id = id # string - self.latitude = latitude # double - self.longitude = longitude # double - self.title = title # string - self.live_period = live_period # flags.0?int - self.reply_markup = reply_markup # flags.1?InlineKeyboardMarkup - self.input_message_content = input_message_content # flags.2?InputMessageContent - self.thumb_url = thumb_url # flags.3?string - self.thumb_width = thumb_width # flags.4?int - self.thumb_height = thumb_height # flags.5?int diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_mpeg4_gif.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_mpeg4_gif.py deleted file mode 100644 index cb704eeb99a..00000000000 --- a/pyrogram/client/types/inline_mode/todo/inline_query_result_mpeg4_gif.py +++ /dev/null @@ -1,80 +0,0 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 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 pyrogram.client.types.pyrogram_type import PyrogramType - - -class InlineQueryResultMpeg4Gif(PyrogramType): - """Represents a link to a video animation (H.264/MPEG-4 AVC video without sound). By default, this animated MPEG-4 file will be sent by the user with optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the animation. - - Attributes: - ID: ``0xb0700002`` - - Parameters: - type (``str``): - Type of the result, must be mpeg4_gif. - - id (``str``): - Unique identifier for this result, 1-64 bytes. - - mpeg4_url (``str``): - A valid URL for the MP4 file. File size must not exceed 1MB. - - thumb_url (``str``, optional): - Video width. - - mpeg4_width (``int`` ``32-bit``, optional): - Video height. - - mpeg4_height (``int`` ``32-bit``, optional): - Video duration. - - mpeg4_duration (``int`` ``32-bit``): - URL of the static thumbnail (jpeg or gif) for the result. - - title (``str``, optional): - Title for the result. - - caption (``str``, optional): - Caption of the MPEG-4 file to be sent, 0-200 characters. - - parse_mode (``str``, optional): - Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. - - reply_markup (:obj:`InlineKeyboardMarkup `, optional): - Inline keyboard attached to the message. - - input_message_content (:obj:`InputMessageContent `, optional): - Content of the message to be sent instead of the video animation. - - """ - ID = 0xb0700002 - - def __init__(self, type: str, id: str, mpeg4_url: str, thumb_url: str, mpeg4_width: int = None, mpeg4_height: int = None, mpeg4_duration: int = None, title: str = None, caption: str = None, parse_mode: str = None, reply_markup=None, input_message_content=None): - self.type = type # string - self.id = id # string - self.mpeg4_url = mpeg4_url # string - self.mpeg4_width = mpeg4_width # flags.0?int - self.mpeg4_height = mpeg4_height # flags.1?int - self.mpeg4_duration = mpeg4_duration # flags.2?int - self.thumb_url = thumb_url # string - self.title = title # flags.3?string - self.caption = caption # flags.4?string - self.parse_mode = parse_mode # flags.5?string - self.reply_markup = reply_markup # flags.6?InlineKeyboardMarkup - self.input_message_content = input_message_content # flags.7?InputMessageContent diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_photo.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_photo.py deleted file mode 100644 index 3412b3f44b4..00000000000 --- a/pyrogram/client/types/inline_mode/todo/inline_query_result_photo.py +++ /dev/null @@ -1,126 +0,0 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 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 pyrogram.api import types -from pyrogram.client.style import HTML, Markdown -from pyrogram.client.types.pyrogram_type import PyrogramType - - -class InlineQueryResultPhoto(PyrogramType): - """Represents a link to a photo. By default, this photo will be sent by the user with optional caption. - Alternatively, you can use input_message_content to send a message with the specified content instead of the photo. - - Parameters: - id (``str``): - Unique identifier for this result, 1-64 bytes. - - photo_url (``str``): - A valid URL of the photo. Photo must be in jpeg format. Photo size must not exceed 5MB. - - thumb_url (``str``): - URL of the thumbnail for the photo. - - photo_width (``int``, *optional*): - Width of the photo. - - photo_height (``int``, *optional*): - Height of the photo. - - title (``str``, *optional*): - Title for the result. - - description (``str``, *optional*): - Short description of the result. - - caption (``str``, *optional*): - Caption of the photo to be sent, 0-200 characters. - - parse_mode (``str``, *optional*): - Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in - the media caption. - - reply_markup (:obj:`InlineKeyboardMarkup `, *optional*): - Inline keyboard attached to the message. - - input_message_content (:obj:`InputMessageContent `, *optional*): - Content of the message to be sent instead of the photo. - - """ - - def __init__( - self, - id: str, - photo_url: str, - thumb_url: str, - photo_width: int = 0, - photo_height: int = 0, - title: str = None, - description: str = None, - caption: str = "", - parse_mode: str = "", - reply_markup=None, - input_message_content=None - ): - self.id = id # string - self.photo_url = photo_url # string - self.thumb_url = thumb_url # string - self.photo_width = photo_width # flags.0?int - self.photo_height = photo_height # flags.1?int - self.title = title # flags.2?string - self.description = description # flags.3?string - self.caption = caption # flags.4?string - self.parse_mode = parse_mode # flags.5?string - self.reply_markup = reply_markup # flags.6?InlineKeyboardMarkup - self.input_message_content = input_message_content # flags.7?InputMessageContent - - self.style = HTML() if parse_mode.lower() == "html" else Markdown() - - def write(self): - return types.InputBotInlineResult( - id=self.id, - type="photo", - send_message=types.InputBotInlineMessageMediaAuto( - reply_markup=self.reply_markup.write() if self.reply_markup else None, - **self.style.parse(self.caption) - ), - title=self.title, - description=self.description, - url=self.photo_url, - thumb=types.InputWebDocument( - url=self.thumb_url, - size=0, - mime_type="image/jpeg", - attributes=[ - types.DocumentAttributeImageSize( - w=0, - h=0 - ) - ] - ), - content=types.InputWebDocument( - url=self.thumb_url, - size=0, - mime_type="image/jpeg", - attributes=[ - types.DocumentAttributeImageSize( - w=self.photo_width, - h=self.photo_height - ) - ] - ) - ) diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_venue.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_venue.py deleted file mode 100644 index 1dee9509e2f..00000000000 --- a/pyrogram/client/types/inline_mode/todo/inline_query_result_venue.py +++ /dev/null @@ -1,84 +0,0 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 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 pyrogram.client.types.pyrogram_type import PyrogramType - - -class InlineQueryResultVenue(PyrogramType): - """Represents a venue. By default, the venue will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the venue. - - Attributes: - ID: ``0xb0700008`` - - Parameters: - type (``str``): - Type of the result, must be venue. - - id (``str``): - Unique identifier for this result, 1-64 Bytes. - - latitude (``float`` ``64-bit``): - Latitude of the venue location in degrees. - - longitude (``float`` ``64-bit``): - Longitude of the venue location in degrees. - - title (``str``): - Title of the venue. - - address (``str``): - Address of the venue. - - foursquare_id (``str``, optional): - Foursquare identifier of the venue if known. - - foursquare_type (``str``, optional): - Foursquare type of the venue, if known. (For example, "arts_entertainment/default", "arts_entertainment/aquarium" or "food/icecream".). - - reply_markup (:obj:`InlineKeyboardMarkup `, optional): - Inline keyboard attached to the message. - - input_message_content (:obj:`InputMessageContent `, optional): - Content of the message to be sent instead of the venue. - - thumb_url (``str``, optional): - Url of the thumbnail for the result. - - thumb_width (``int`` ``32-bit``, optional): - Thumbnail width. - - thumb_height (``int`` ``32-bit``, optional): - Thumbnail height. - - """ - ID = 0xb0700008 - - def __init__(self, type: str, id: str, latitude: float, longitude: float, title: str, address: str, foursquare_id: str = None, foursquare_type: str = None, reply_markup=None, input_message_content=None, thumb_url: str = None, thumb_width: int = None, thumb_height: int = None): - self.type = type # string - self.id = id # string - self.latitude = latitude # double - self.longitude = longitude # double - self.title = title # string - self.address = address # string - self.foursquare_id = foursquare_id # flags.0?string - self.foursquare_type = foursquare_type # flags.1?string - self.reply_markup = reply_markup # flags.2?InlineKeyboardMarkup - self.input_message_content = input_message_content # flags.3?InputMessageContent - self.thumb_url = thumb_url # flags.4?string - self.thumb_width = thumb_width # flags.5?int - self.thumb_height = thumb_height # flags.6?int diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_video.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_video.py deleted file mode 100644 index 22d810e1087..00000000000 --- a/pyrogram/client/types/inline_mode/todo/inline_query_result_video.py +++ /dev/null @@ -1,88 +0,0 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 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 pyrogram.client.types.pyrogram_type import PyrogramType - - -class InlineQueryResultVideo(PyrogramType): - """Represents a link to a page containing an embedded video player or a video file. By default, this video file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the video. - - Attributes: - ID: ``0xb0700003`` - - Parameters: - type (``str``): - Type of the result, must be video. - - id (``str``): - Unique identifier for this result, 1-64 bytes. - - video_url (``str``): - A valid URL for the embedded video player or video file. - - mime_type (``str``): - Mime type of the content of video url, "text/html" or "video/mp4". - - thumb_url (``str``): - URL of the thumbnail (jpeg only) for the video. - - title (``str``): - Title for the result. - - caption (``str``, optional): - Caption of the video to be sent, 0-200 characters. - - parse_mode (``str``, optional): - Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. - - video_width (``int`` ``32-bit``, optional): - Video width. - - video_height (``int`` ``32-bit``, optional): - Video height. - - video_duration (``int`` ``32-bit``, optional): - Video duration in seconds. - - description (``str``, optional): - Short description of the result. - - reply_markup (:obj:`InlineKeyboardMarkup `, optional): - Inline keyboard attached to the message. - - input_message_content (:obj:`InputMessageContent `, optional): - Content of the message to be sent instead of the video. This field is required if InlineQueryResultVideo is used to send an HTML-page as a result (e.g., a YouTube video). - - """ - ID = 0xb0700003 - - def __init__(self, type: str, id: str, video_url: str, mime_type: str, thumb_url: str, title: str, caption: str = None, parse_mode: str = None, video_width: int = None, video_height: int = None, video_duration: int = None, description: str = None, reply_markup=None, input_message_content=None): - self.type = type # string - self.id = id # string - self.video_url = video_url # string - self.mime_type = mime_type # string - self.thumb_url = thumb_url # string - self.title = title # string - self.caption = caption # flags.0?string - self.parse_mode = parse_mode # flags.1?string - self.video_width = video_width # flags.2?int - self.video_height = video_height # flags.3?int - self.video_duration = video_duration # flags.4?int - self.description = description # flags.5?string - self.reply_markup = reply_markup # flags.6?InlineKeyboardMarkup - self.input_message_content = input_message_content # flags.7?InputMessageContent diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_voice.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_voice.py deleted file mode 100644 index 73d7fb1d9a1..00000000000 --- a/pyrogram/client/types/inline_mode/todo/inline_query_result_voice.py +++ /dev/null @@ -1,68 +0,0 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 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 pyrogram.client.types.pyrogram_type import PyrogramType - - -class InlineQueryResultVoice(PyrogramType): - """Represents a link to a voice recording in an .ogg container encoded with OPUS. By default, this voice recording will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the the voice message. - - Attributes: - ID: ``0xb0700005`` - - Parameters: - type (``str``): - Type of the result, must be voice. - - id (``str``): - Unique identifier for this result, 1-64 bytes. - - voice_url (``str``): - A valid URL for the voice recording. - - title (``str``): - Recording title. - - caption (``str``, optional): - Caption, 0-200 characters. - - parse_mode (``str``, optional): - Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. - - voice_duration (``int`` ``32-bit``, optional): - Recording duration in seconds. - - reply_markup (:obj:`InlineKeyboardMarkup `, optional): - Inline keyboard attached to the message. - - input_message_content (:obj:`InputMessageContent `, optional): - Content of the message to be sent instead of the voice recording. - - """ - ID = 0xb0700005 - - def __init__(self, type: str, id: str, voice_url: str, title: str, caption: str = None, parse_mode: str = None, voice_duration: int = None, reply_markup=None, input_message_content=None): - self.type = type # string - self.id = id # string - self.voice_url = voice_url # string - self.title = title # string - self.caption = caption # flags.0?string - self.parse_mode = parse_mode # flags.1?string - self.voice_duration = voice_duration # flags.2?int - self.reply_markup = reply_markup # flags.3?InlineKeyboardMarkup - self.input_message_content = input_message_content # flags.4?InputMessageContent diff --git a/pyrogram/client/types/input_media/__init__.py b/pyrogram/client/types/input_media/__init__.py deleted file mode 100644 index c97b9539a33..00000000000 --- a/pyrogram/client/types/input_media/__init__.py +++ /dev/null @@ -1,30 +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 .input_media import InputMedia -from .input_media_animation import InputMediaAnimation -from .input_media_audio import InputMediaAudio -from .input_media_document import InputMediaDocument -from .input_media_photo import InputMediaPhoto -from .input_media_video import InputMediaVideo -from .input_phone_contact import InputPhoneContact - -__all__ = [ - "InputMedia", "InputMediaAnimation", "InputMediaAudio", "InputMediaDocument", "InputMediaPhoto", "InputMediaVideo", - "InputPhoneContact" -] diff --git a/pyrogram/client/types/input_media/input_media.py b/pyrogram/client/types/input_media/input_media.py deleted file mode 100644 index 1e0fbc01544..00000000000 --- a/pyrogram/client/types/input_media/input_media.py +++ /dev/null @@ -1,38 +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 ..pyrogram_type import PyrogramType - - -class InputMedia(PyrogramType): - """This object represents the content of a media message to be sent. It should be one of: - - - :obj:`InputMediaAnimation` - - :obj:`InputMediaDocument` - - :obj:`InputMediaAudio` - - :obj:`InputMediaPhoto` - - :obj:`InputMediaVideo` - """ - __slots__ = ["media", "caption", "parse_mode"] - - def __init__(self, media: str, caption: str, parse_mode: str): - super().__init__(None) - - self.media = media - self.caption = caption - self.parse_mode = parse_mode diff --git a/pyrogram/client/types/input_media/input_media_animation.py b/pyrogram/client/types/input_media/input_media_animation.py deleted file mode 100644 index 177bd9ad916..00000000000 --- a/pyrogram/client/types/input_media/input_media_animation.py +++ /dev/null @@ -1,71 +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 . import InputMedia - - -class InputMediaAnimation(InputMedia): - """This object represents an animation file (GIF or H.264/MPEG-4 AVC video without sound) to be sent. - - Parameters: - media (``str``): - Animation to send. - Pass a file_id as string to send a file that exists on the Telegram servers or - pass a file path as string to upload a new file that exists on your local machine. - - thumb (``str``, *optional*): - Thumbnail of the animation file sent. - The thumbnail should be in JPEG format and less than 200 KB in size. - A thumbnail's width and height should not exceed 320 pixels. - Thumbnails can't be reused and can be only uploaded as a new file. - - caption (``str``, *optional*): - Caption of the animation to be sent, 0-1024 characters - - parse_mode (``str``, *optional*): - Pass "markdown" or "html" if you want Telegram apps to show bold, italic, fixed-width text or inline URLs - in your caption. Defaults to "markdown". - - width (``int``, *optional*): - Animation width. - - height (``int``, *optional*): - Animation height. - - duration (``int``, *optional*): - Animation duration. - """ - - __slots__ = ["thumb", "width", "height", "duration"] - - def __init__( - self, - media: str, - thumb: str = None, - caption: str = "", - parse_mode: str = "", - width: int = 0, - height: int = 0, - duration: int = 0 - ): - super().__init__(media, caption, parse_mode) - - self.thumb = thumb - self.width = width - self.height = height - self.duration = duration diff --git a/pyrogram/client/types/input_media/input_media_audio.py b/pyrogram/client/types/input_media/input_media_audio.py deleted file mode 100644 index 152532fa6a6..00000000000 --- a/pyrogram/client/types/input_media/input_media_audio.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 . import InputMedia - - -class InputMediaAudio(InputMedia): - """This object represents an audio to be sent inside an album. - It is intended to be used with :obj:`send_media_group() `. - - Parameters: - media (``str``): - Audio to send. - Pass a file_id as string to send an audio that exists on the Telegram servers or - pass a file path as string to upload a new audio that exists on your local machine. - - thumb (``str``, *optional*): - Thumbnail of the music file album cover. - The thumbnail should be in JPEG format and less than 200 KB in size. - A thumbnail's width and height should not exceed 320 pixels. - Thumbnails can't be reused and can be only uploaded as a new file. - - caption (``str``, *optional*): - Caption of the audio to be sent, 0-1024 characters - - parse_mode (``str``, *optional*): - Pass "markdown" or "html" if you want Telegram apps to show bold, italic, fixed-width text or inline URLs - in your caption. Defaults to "markdown". - - duration (``int``, *optional*): - Duration of the audio in seconds - - performer (``int``, *optional*): - Performer of the audio - - title (``int``, *optional*): - Title of the audio - """ - - __slots__ = ["thumb", "duration", "performer", "title"] - - def __init__( - self, - media: str, - thumb: str = None, - caption: str = "", - parse_mode: str = "", - duration: int = 0, - performer: int = "", - title: str = "" - ): - super().__init__(media, caption, parse_mode) - - self.thumb = thumb - self.duration = duration - self.performer = performer - self.title = title diff --git a/pyrogram/client/types/input_media/input_media_document.py b/pyrogram/client/types/input_media/input_media_document.py deleted file mode 100644 index 046b731cd83..00000000000 --- a/pyrogram/client/types/input_media/input_media_document.py +++ /dev/null @@ -1,56 +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 . import InputMedia - - -class InputMediaDocument(InputMedia): - """This object represents a general file to be sent. - - Parameters: - media (``str``): - File to send. - Pass a file_id as string to send a file that exists on the Telegram servers or - pass a file path as string to upload a new file that exists on your local machine. - - thumb (``str``): - Thumbnail of the file sent. - The thumbnail should be in JPEG format and less than 200 KB in size. - A thumbnail's width and height should not exceed 320 pixels. - Thumbnails can't be reused and can be only uploaded as a new file. - - caption (``str``, *optional*): - Caption of the document to be sent, 0-1024 characters - - parse_mode (``str``, *optional*): - Pass "markdown" or "html" if you want Telegram apps to show bold, italic, fixed-width text or inline URLs - in your caption. Defaults to "markdown". - """ - - __slots__ = ["thumb"] - - def __init__( - self, - media: str, - thumb: str = None, - caption: str = "", - parse_mode: str = "" - ): - super().__init__(media, caption, parse_mode) - - self.thumb = thumb diff --git a/pyrogram/client/types/input_media/input_media_photo.py b/pyrogram/client/types/input_media/input_media_photo.py deleted file mode 100644 index 2797bd5f29b..00000000000 --- a/pyrogram/client/types/input_media/input_media_photo.py +++ /dev/null @@ -1,49 +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 . import InputMedia - - -class InputMediaPhoto(InputMedia): - """This object represents a photo to be sent inside an album. - It is intended to be used with :obj:`send_media_group() `. - - Parameters: - media (``str``): - Photo to send. - Pass a file_id as string to send a photo that exists on the Telegram servers or - pass a file path as string to upload a new photo that exists on your local machine. - Sending photo by a URL is currently unsupported. - - caption (``str``, *optional*): - Caption of the photo to be sent, 0-1024 characters - - parse_mode (``str``, *optional*): - Pass "markdown" or "html" if you want Telegram apps to show bold, italic, fixed-width text or inline URLs - in your caption. Defaults to "markdown". - """ - - __slots__ = [] - - def __init__( - self, - media: str, - caption: str = "", - parse_mode: str = "" - ): - super().__init__(media, caption, parse_mode) diff --git a/pyrogram/client/types/input_media/input_media_video.py b/pyrogram/client/types/input_media/input_media_video.py deleted file mode 100644 index 319973de703..00000000000 --- a/pyrogram/client/types/input_media/input_media_video.py +++ /dev/null @@ -1,78 +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 . import InputMedia - - -class InputMediaVideo(InputMedia): - """This object represents a video to be sent inside an album. - It is intended to be used with :obj:`send_media_group() `. - - Parameters: - media (``str``): - Video to send. - Pass a file_id as string to send a video that exists on the Telegram servers or - pass a file path as string to upload a new video that exists on your local machine. - Sending video by a URL is currently unsupported. - - thumb (``str``): - Thumbnail of the video sent. - The thumbnail should be in JPEG format and less than 200 KB in size. - A thumbnail's width and height should not exceed 320 pixels. - Thumbnails can't be reused and can be only uploaded as a new file. - - caption (``str``, *optional*): - Caption of the video to be sent, 0-1024 characters - - parse_mode (``str``, *optional*): - Pass "markdown" or "html" if you want Telegram apps to show bold, italic, fixed-width text or inline URLs - in your caption. Defaults to "markdown". - - width (``int``, *optional*): - Video width. - - height (``int``, *optional*): - Video height. - - duration (``int``, *optional*): - Video duration. - - supports_streaming (``bool``, *optional*): - Pass True, if the uploaded video is suitable for streaming. - """ - - __slots__ = ["thumb", "width", "height", "duration", "supports_streaming"] - - def __init__( - self, - media: str, - thumb: str = None, - caption: str = "", - parse_mode: str = "", - width: int = 0, - height: int = 0, - duration: int = 0, - supports_streaming: bool = True - ): - super().__init__(media, caption, parse_mode) - - self.thumb = thumb - self.width = width - self.height = height - self.duration = duration - self.supports_streaming = supports_streaming diff --git a/pyrogram/client/types/input_media/input_phone_contact.py b/pyrogram/client/types/input_media/input_phone_contact.py deleted file mode 100644 index 02189011c49..00000000000 --- a/pyrogram/client/types/input_media/input_phone_contact.py +++ /dev/null @@ -1,53 +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 pyrogram.api.types import InputPhoneContact as RawInputPhoneContact -from pyrogram.session.internals import MsgId -from ..pyrogram_type import PyrogramType - - -class InputPhoneContact(PyrogramType): - """This object represents a Phone Contact to be added in your Telegram address book. - It is intended to be used with :meth:`add_contacts() ` - - Parameters: - phone (``str``): - Contact's phone number - - first_name (``str``): - Contact's first name - - last_name (``str``, *optional*): - Contact's last name - """ - - __slots__ = [] - - def __init__(self, phone: str, first_name: str, last_name: str = ""): - super().__init__(None) - - def __new__(cls, - phone: str, - first_name: str, - last_name: str = ""): - return RawInputPhoneContact( - client_id=MsgId(), - phone="+" + phone.strip("+"), - first_name=first_name, - last_name=last_name - ) diff --git a/pyrogram/client/types/input_message_content/__init__.py b/pyrogram/client/types/input_message_content/__init__.py deleted file mode 100644 index 5c53fd2e085..00000000000 --- a/pyrogram/client/types/input_message_content/__init__.py +++ /dev/null @@ -1,24 +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 .input_message_content import InputMessageContent -from .input_text_message_content import InputTextMessageContent - -__all__ = [ - "InputMessageContent", "InputTextMessageContent" -] diff --git a/pyrogram/client/types/input_message_content/input_message_content.py b/pyrogram/client/types/input_message_content/input_message_content.py deleted file mode 100644 index f3e238b85b1..00000000000 --- a/pyrogram/client/types/input_message_content/input_message_content.py +++ /dev/null @@ -1,37 +0,0 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 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 ..pyrogram_type import PyrogramType - -"""- :obj:`InputLocationMessageContent` - - :obj:`InputVenueMessageContent` - - :obj:`InputContactMessageContent`""" - - -class InputMessageContent(PyrogramType): - """This object represents the content of a message to be sent as a result of an inline query. - - Pyrogram currently supports the following 4 types: - - - :obj:`InputTextMessageContent` - """ - - __slots__ = [] - - def __init__(self): - super().__init__(None) diff --git a/pyrogram/client/types/input_message_content/input_text_message_content.py b/pyrogram/client/types/input_message_content/input_text_message_content.py deleted file mode 100644 index feedd298e1d..00000000000 --- a/pyrogram/client/types/input_message_content/input_text_message_content.py +++ /dev/null @@ -1,53 +0,0 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 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 pyrogram.api import types -from .input_message_content import InputMessageContent -from ...style import HTML, Markdown - - -class InputTextMessageContent(InputMessageContent): - """This object represents the content of a text message to be sent as the result of an inline query. - - Parameters: - message_text (``str``): - Text of the message to be sent, 1-4096 characters. - - parse_mode (``str``, *optional*): - Pass "markdown" or "html" if you want Telegram apps to show bold, italic, fixed-width text or inline URLs - in your message. Defaults to "markdown". - - disable_web_page_preview (``bool``, *optional*): - Disables link previews for links in this message. - """ - - __slots__ = ["message_text", "parse_mode", "disable_web_page_preview"] - - def __init__(self, message_text: str, parse_mode: str = "", disable_web_page_preview: bool = None): - super().__init__() - - self.message_text = message_text - self.parse_mode = parse_mode - self.disable_web_page_preview = disable_web_page_preview - - def write(self, reply_markup): - return types.InputBotInlineMessageText( - no_webpage=self.disable_web_page_preview or None, - reply_markup=reply_markup.write() if reply_markup else None, - **(HTML() if self.parse_mode.lower() == "html" else Markdown()).parse(self.message_text) - ) diff --git a/pyrogram/client/types/keyboards/__init__.py b/pyrogram/client/types/keyboards/__init__.py deleted file mode 100644 index dae33e108f2..00000000000 --- a/pyrogram/client/types/keyboards/__init__.py +++ /dev/null @@ -1,33 +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 .callback_game import CallbackGame -from .callback_query import CallbackQuery -from .force_reply import ForceReply -from .game_high_score import GameHighScore -from .game_high_scores import GameHighScores -from .inline_keyboard_button import InlineKeyboardButton -from .inline_keyboard_markup import InlineKeyboardMarkup -from .keyboard_button import KeyboardButton -from .reply_keyboard_markup import ReplyKeyboardMarkup -from .reply_keyboard_remove import ReplyKeyboardRemove - -__all__ = [ - "CallbackGame", "CallbackQuery", "ForceReply", "GameHighScore", "GameHighScores", "InlineKeyboardButton", - "InlineKeyboardMarkup", "KeyboardButton", "ReplyKeyboardMarkup", "ReplyKeyboardRemove" -] diff --git a/pyrogram/client/types/keyboards/callback_game.py b/pyrogram/client/types/keyboards/callback_game.py deleted file mode 100644 index fc2d9884a97..00000000000 --- a/pyrogram/client/types/keyboards/callback_game.py +++ /dev/null @@ -1,31 +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 ..pyrogram_type import PyrogramType - - -class CallbackGame(PyrogramType): - """A placeholder, currently holds no information. - - Use BotFather to set up your game. - """ - - __slots__ = [] - - def __init__(self): - super().__init__(None) diff --git a/pyrogram/client/types/keyboards/callback_query.py b/pyrogram/client/types/keyboards/callback_query.py deleted file mode 100644 index 61bbfe405f8..00000000000 --- a/pyrogram/client/types/keyboards/callback_query.py +++ /dev/null @@ -1,165 +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 base64 import b64encode -from struct import pack - -import pyrogram -from pyrogram.api import types -from ..pyrogram_type import PyrogramType -from ..update import Update -from ..user_and_chats import User - - -class CallbackQuery(PyrogramType, Update): - """This object represents an incoming callback query from a callback button in an inline keyboard. - If the button that originated the query was attached to a message sent by the bot, the field message - will be present. If the button was attached to a message sent via the bot (in inline mode), - the field inline_message_id will be present. Exactly one of the fields data or game_short_name will be present. - - Parameters: - id (``str``): - Unique identifier for this query. - - from_user (:obj:`User`): - Sender. - - chat_instance (``str``, *optional*): - Global identifier, uniquely corresponding to the chat to which the message with the callback button was - sent. Useful for high scores in games. - - message (:obj:`Message`, *optional*): - Message with the callback button that originated the query. Note that message content and message date will - not be available if the message is too old. - - inline_message_id (``str``): - Identifier of the message sent via the bot in inline mode, that originated the query. - - data (``bytes``, *optional*): - Data associated with the callback button. Be aware that a bad client can send arbitrary data in this field. - - game_short_name (``str``, *optional*): - Short name of a Game to be returned, serves as the unique identifier for the game. - - """ - - __slots__ = ["id", "from_user", "chat_instance", "message", "inline_message_id", "data", "game_short_name"] - - def __init__( - self, - *, - client: "pyrogram.client.ext.BaseClient", - id: str, - from_user: User, - chat_instance: str, - message: "pyrogram.Message" = None, - inline_message_id: str = None, - data: bytes = None, - game_short_name: str = None - ): - super().__init__(client) - - self.id = id - self.from_user = from_user - self.chat_instance = chat_instance - self.message = message - self.inline_message_id = inline_message_id - self.data = str(data, "utf-8") - self.game_short_name = game_short_name - - @staticmethod - def _parse(client, callback_query, users) -> "CallbackQuery": - message = None - inline_message_id = None - - if isinstance(callback_query, types.UpdateBotCallbackQuery): - peer = callback_query.peer - - if isinstance(peer, types.PeerUser): - peer_id = peer.user_id - elif isinstance(peer, types.PeerChat): - peer_id = -peer.chat_id - else: - peer_id = int("-100" + str(peer.channel_id)) - - message = client.get_messages(peer_id, callback_query.msg_id) - elif isinstance(callback_query, types.UpdateInlineBotCallbackQuery): - inline_message_id = b64encode( - pack( - " -# -# 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 pyrogram.api.types import ReplyKeyboardForceReply -from ..pyrogram_type import PyrogramType - - -class ForceReply(PyrogramType): - """Upon receiving a message with this object, Telegram clients will display a reply interface to the user. - - This acts as if the user has selected the bot's message and tapped "Reply". - This can be extremely useful if you want to create user-friendly step-by-step interfaces without having to - sacrifice privacy mode. - - Parameters: - selective (``bool``, *optional*): - Use this parameter if you want to force reply from specific users only. Targets: - 1) users that are @mentioned in the text of the Message object; - 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message. - """ - - __slots__ = ["selective"] - - def __init__( - self, - selective: bool = None - ): - super().__init__(None) - - self.selective = selective - - @staticmethod - def read(o): - return ForceReply( - selective=o.selective - ) - - def write(self): - return ReplyKeyboardForceReply( - single_use=True, - selective=self.selective or None - ) diff --git a/pyrogram/client/types/keyboards/game_high_score.py b/pyrogram/client/types/keyboards/game_high_score.py deleted file mode 100644 index 07db8e738e4..00000000000 --- a/pyrogram/client/types/keyboards/game_high_score.py +++ /dev/null @@ -1,73 +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 . - -import pyrogram - -from pyrogram.api import types -from pyrogram.client.types.pyrogram_type import PyrogramType -from pyrogram.client.types.user_and_chats import User - - -class GameHighScore(PyrogramType): - """This object represents one row of the high scores table for a game. - - Parameters: - user (:obj:`User`): - User. - - score (``int``): - Score. - - position (``position``, *optional*): - Position in high score table for the game. - """ - - __slots__ = ["user", "score", "position"] - - def __init__( - self, - *, - client: "pyrogram.client.ext.BaseClient", - user: User, - score: int, - position: int = None - ): - super().__init__(client) - - self.user = user - self.score = score - self.position = position - - @staticmethod - def _parse(client, game_high_score: types.HighScore, users: dict) -> "GameHighScore": - users = {i.id: i for i in users} - - return GameHighScore( - user=User._parse(client, users[game_high_score.user_id]), - score=game_high_score.score, - position=game_high_score.pos, - client=client - ) - - @staticmethod - def _parse_action(client, service: types.MessageService, users: dict): - return GameHighScore( - user=User._parse(client, users[service.from_id]), - score=service.action.score, - client=client - ) diff --git a/pyrogram/client/types/keyboards/game_high_scores.py b/pyrogram/client/types/keyboards/game_high_scores.py deleted file mode 100644 index 36b14c0f131..00000000000 --- a/pyrogram/client/types/keyboards/game_high_scores.py +++ /dev/null @@ -1,60 +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 typing import List - -import pyrogram -from pyrogram.api import types -from pyrogram.client.types.pyrogram_type import PyrogramType -from .game_high_score import GameHighScore - - -class GameHighScores(PyrogramType): - """This object represents the high scores table for a game. - - Parameters: - total_count (``int``): - Total number of scores the target game has. - - game_high_scores (List of :obj:`GameHighScore`): - Game scores. - """ - - __slots__ = ["total_count", "game_high_scores"] - - def __init__( - self, - *, - client: "pyrogram.client.ext.BaseClient", - total_count: int, - game_high_scores: List[GameHighScore] - ): - super().__init__(client) - - self.total_count = total_count - self.game_high_scores = game_high_scores - - @staticmethod - def _parse(client, game_high_scores: types.messages.HighScores) -> "GameHighScores": - return GameHighScores( - total_count=len(game_high_scores.scores), - game_high_scores=[ - GameHighScore._parse(client, score, game_high_scores.users) - for score in game_high_scores.scores], - client=client - ) diff --git a/pyrogram/client/types/keyboards/inline_keyboard_button.py b/pyrogram/client/types/keyboards/inline_keyboard_button.py deleted file mode 100644 index ea110f7f094..00000000000 --- a/pyrogram/client/types/keyboards/inline_keyboard_button.py +++ /dev/null @@ -1,132 +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 typing import Union - -from pyrogram.api.types import ( - KeyboardButtonUrl, KeyboardButtonCallback, - KeyboardButtonSwitchInline, KeyboardButtonGame -) -from .callback_game import CallbackGame -from ..pyrogram_type import PyrogramType - - -class InlineKeyboardButton(PyrogramType): - """This object represents one button of an inline keyboard. You must use exactly one of the optional fields. - - Parameters: - text (``str``): - Label text on the button. - - callback_data (``bytes``, *optional*): - Data to be sent in a callback query to the bot when button is pressed, 1-64 bytes. - - url (``str``, *optional*): - HTTP url to be opened when button is pressed. - - switch_inline_query (``str``, *optional*): - If set, pressing the button will prompt the user to select one of their chats, open that chat and insert - the bot's username and the specified inline query in the input field. Can be empty, in which case just - the bot's username will be inserted.Note: This offers an easy way for users to start using your bot in - inline mode when they are currently in a private chat with it. Especially useful when combined with - switch_pm… actions – in this case the user will be automatically returned to the chat they switched from, - skipping the chat selection screen. - - switch_inline_query_current_chat (``str``, *optional*): - If set, pressing the button will insert the bot's username and the specified inline query in the current - chat's input field. Can be empty, in which case only the bot's username will be inserted.This offers a - quick way for the user to open your bot in inline mode in the same chat – good for selecting something - from multiple options. - """ - - # TODO: Add callback_game and pay fields - - __slots__ = [ - "text", "url", "callback_data", "switch_inline_query", "switch_inline_query_current_chat", "callback_game" - ] - - def __init__( - self, - text: str, - callback_data: Union[str, bytes] = None, - url: str = None, - switch_inline_query: str = None, - switch_inline_query_current_chat: str = None, - callback_game: CallbackGame = None - ): - super().__init__(None) - - self.text = str(text) - self.url = url - self.callback_data = bytes(callback_data, "utf-8") if isinstance(callback_data, str) else callback_data - self.switch_inline_query = switch_inline_query - self.switch_inline_query_current_chat = switch_inline_query_current_chat - self.callback_game = callback_game - # self.pay = pay - - @staticmethod - def read(o): - if isinstance(o, KeyboardButtonUrl): - return InlineKeyboardButton( - text=o.text, - url=o.url - ) - - if isinstance(o, KeyboardButtonCallback): - return InlineKeyboardButton( - text=o.text, - callback_data=o.data - ) - - if isinstance(o, KeyboardButtonSwitchInline): - if o.same_peer: - return InlineKeyboardButton( - text=o.text, - switch_inline_query_current_chat=o.query - ) - else: - return InlineKeyboardButton( - text=o.text, - switch_inline_query=o.query - ) - - if isinstance(o, KeyboardButtonGame): - return InlineKeyboardButton( - text=o.text, - callback_game=CallbackGame() - ) - - def write(self): - if self.callback_data is not None: - return KeyboardButtonCallback(text=self.text, data=self.callback_data) - - if self.url is not None: - return KeyboardButtonUrl(text=self.text, url=self.url) - - if self.switch_inline_query is not None: - return KeyboardButtonSwitchInline(text=self.text, query=self.switch_inline_query) - - if self.switch_inline_query_current_chat is not None: - return KeyboardButtonSwitchInline( - text=self.text, - query=self.switch_inline_query_current_chat, - same_peer=True - ) - - if self.callback_game is not None: - return KeyboardButtonGame(text=self.text) diff --git a/pyrogram/client/types/keyboards/inline_keyboard_markup.py b/pyrogram/client/types/keyboards/inline_keyboard_markup.py deleted file mode 100644 index f48f4b8c4cc..00000000000 --- a/pyrogram/client/types/keyboards/inline_keyboard_markup.py +++ /dev/null @@ -1,65 +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 typing import List - -from pyrogram.api.types import ReplyInlineMarkup, KeyboardButtonRow -from . import InlineKeyboardButton -from ..pyrogram_type import PyrogramType - - -class InlineKeyboardMarkup(PyrogramType): - """This object represents an inline keyboard that appears right next to the message it belongs to. - - Parameters: - inline_keyboard (List of List of :obj:`InlineKeyboardButton`): - List of button rows, each represented by a List of InlineKeyboardButton objects. - """ - - __slots__ = ["inline_keyboard"] - - def __init__( - self, - inline_keyboard: List[List[InlineKeyboardButton]] - ): - super().__init__(None) - - self.inline_keyboard = inline_keyboard - - @staticmethod - def read(o): - inline_keyboard = [] - - for i in o.rows: - row = [] - - for j in i.buttons: - row.append(InlineKeyboardButton.read(j)) - - inline_keyboard.append(row) - - return InlineKeyboardMarkup( - inline_keyboard=inline_keyboard - ) - - def write(self): - return ReplyInlineMarkup( - rows=[KeyboardButtonRow( - buttons=[j.write() for j in i] - ) for i in self.inline_keyboard] - ) diff --git a/pyrogram/client/types/keyboards/keyboard_button.py b/pyrogram/client/types/keyboards/keyboard_button.py deleted file mode 100644 index e0393e3de92..00000000000 --- a/pyrogram/client/types/keyboards/keyboard_button.py +++ /dev/null @@ -1,82 +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 pyrogram.api.types import KeyboardButton as RawKeyboardButton -from pyrogram.api.types import KeyboardButtonRequestPhone, KeyboardButtonRequestGeoLocation -from ..pyrogram_type import PyrogramType - - -class KeyboardButton(PyrogramType): - """This object represents one button of the reply keyboard. - For simple text buttons String can be used instead of this object to specify text of the button. - Optional fields are mutually exclusive. - - Parameters: - text (``str``): - Text of the button. If none of the optional fields are used, it will be sent as a message when - the button is pressed. - - request_contact (``bool``, *optional*): - If True, the user's phone number will be sent as a contact when the button is pressed. - Available in private chats only. - - request_location (``bool``, *optional*): - If True, the user's current location will be sent when the button is pressed. - Available in private chats only. - """ - - __slots__ = ["text", "request_contact", "request_location"] - - def __init__( - self, - text: str, - request_contact: bool = None, - request_location: bool = None - ): - super().__init__(None) - - self.text = str(text) - self.request_contact = request_contact - self.request_location = request_location - - @staticmethod - def read(o): - if isinstance(o, RawKeyboardButton): - return o.text - - if isinstance(o, KeyboardButtonRequestPhone): - return KeyboardButton( - text=o.text, - request_contact=True - ) - - if isinstance(o, KeyboardButtonRequestGeoLocation): - return KeyboardButton( - text=o.text, - request_location=True - ) - - def write(self): - # TODO: Enforce optional args mutual exclusiveness - - if self.request_contact: - return KeyboardButtonRequestPhone(text=self.text) - elif self.request_location: - return KeyboardButtonRequestGeoLocation(text=self.text) - else: - return RawKeyboardButton(text=self.text) diff --git a/pyrogram/client/types/keyboards/reply_keyboard_markup.py b/pyrogram/client/types/keyboards/reply_keyboard_markup.py deleted file mode 100644 index 32c73c6b1de..00000000000 --- a/pyrogram/client/types/keyboards/reply_keyboard_markup.py +++ /dev/null @@ -1,99 +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 typing import List, Union - -from pyrogram.api.types import KeyboardButtonRow -from pyrogram.api.types import ReplyKeyboardMarkup as RawReplyKeyboardMarkup -from . import KeyboardButton -from ..pyrogram_type import PyrogramType - - -class ReplyKeyboardMarkup(PyrogramType): - """This object represents a custom keyboard with reply options. - - Parameters: - keyboard (List of List of :obj:`KeyboardButton`): - List of button rows, each represented by a List of KeyboardButton objects. - - resize_keyboard (``bool``, *optional*): - Requests clients to resize the keyboard vertically for optimal fit (e.g., make the keyboard smaller if - there are just two rows of buttons). Defaults to false, in which case the custom keyboard is always of - the same height as the app's standard keyboard. - - one_time_keyboard (``bool``, *optional*): - Requests clients to hide the keyboard as soon as it's been used. The keyboard will still be available, - but clients will automatically display the usual letter-keyboard in the chat – the user can press a - special button in the input field to see the custom keyboard again. Defaults to false. - - selective (``bool``, *optional*): - Use this parameter if you want to show the keyboard to specific users only. Targets: - 1) users that are @mentioned in the text of the Message object; - 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message. - Example: A user requests to change the bot's language, bot replies to the request with a keyboard to - select the new language. Other users in the group don't see the keyboard. - """ - - __slots__ = ["keyboard", "resize_keyboard", "one_time_keyboard", "selective"] - - def __init__( - self, - keyboard: List[List[Union[KeyboardButton, str]]], - resize_keyboard: bool = None, - one_time_keyboard: bool = None, - selective: bool = None - ): - super().__init__(None) - - self.keyboard = keyboard - self.resize_keyboard = resize_keyboard - self.one_time_keyboard = one_time_keyboard - self.selective = selective - - @staticmethod - def read(kb): - keyboard = [] - - for i in kb.rows: - row = [] - - for j in i.buttons: - row.append(KeyboardButton.read(j)) - - keyboard.append(row) - - return ReplyKeyboardMarkup( - keyboard=keyboard, - resize_keyboard=kb.resize, - one_time_keyboard=kb.single_use, - selective=kb.selective - ) - - def write(self): - return RawReplyKeyboardMarkup( - rows=[KeyboardButtonRow( - buttons=[ - KeyboardButton(j).write() - if isinstance(j, str) else j.write() - for j in i - ] - ) for i in self.keyboard], - resize=self.resize_keyboard or None, - single_use=self.one_time_keyboard or None, - selective=self.selective or None - ) diff --git a/pyrogram/client/types/keyboards/reply_keyboard_remove.py b/pyrogram/client/types/keyboards/reply_keyboard_remove.py deleted file mode 100644 index 560c89ee113..00000000000 --- a/pyrogram/client/types/keyboards/reply_keyboard_remove.py +++ /dev/null @@ -1,56 +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 pyrogram.api.types import ReplyKeyboardHide -from ..pyrogram_type import PyrogramType - - -class ReplyKeyboardRemove(PyrogramType): - """Upon receiving a message with this object, Telegram clients will remove the current custom keyboard and display the default letter-keyboard. - By default, custom keyboards are displayed until a new keyboard is sent by a bot. An exception is made for one-time - keyboards that are hidden immediately after the user presses a button (see ReplyKeyboardMarkup). - - Parameters: - selective (``bool``, *optional*): - Use this parameter if you want to remove the keyboard for specific users only. Targets: - 1) users that are @mentioned in the text of the Message object; - 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message. - Example: A user votes in a poll, bot returns confirmation message in reply to the vote and removes the - keyboard for that user, while still showing the keyboard with poll options to users who haven't voted yet. - """ - - __slots__ = ["selective"] - - def __init__( - self, - selective: bool = None - ): - super().__init__(None) - - self.selective = selective - - @staticmethod - def read(o): - return ReplyKeyboardRemove( - selective=o.selective - ) - - def write(self): - return ReplyKeyboardHide( - selective=self.selective or None - ) diff --git a/pyrogram/client/types/messages_and_media/__init__.py b/pyrogram/client/types/messages_and_media/__init__.py deleted file mode 100644 index ae4386d0e66..00000000000 --- a/pyrogram/client/types/messages_and_media/__init__.py +++ /dev/null @@ -1,42 +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 .animation import Animation -from .audio import Audio -from .contact import Contact -from .document import Document -from .game import Game -from .location import Location -from .message import Message -from .message_entity import MessageEntity -from .messages import Messages -from .photo import Photo -from .photo_size import PhotoSize -from .poll import Poll -from .poll_option import PollOption -from .sticker import Sticker -from .user_profile_photos import UserProfilePhotos -from .venue import Venue -from .video import Video -from .video_note import VideoNote -from .voice import Voice - -__all__ = [ - "Animation", "Audio", "Contact", "Document", "Game", "Location", "Message", "MessageEntity", "Messages", "Photo", - "PhotoSize", "Poll", "PollOption", "Sticker", "UserProfilePhotos", "Venue", "Video", "VideoNote", "Voice" -] diff --git a/pyrogram/client/types/messages_and_media/animation.py b/pyrogram/client/types/messages_and_media/animation.py deleted file mode 100644 index 25dda78e49c..00000000000 --- a/pyrogram/client/types/messages_and_media/animation.py +++ /dev/null @@ -1,110 +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 struct import pack - -import pyrogram -from pyrogram.api import types -from .photo_size import PhotoSize -from ..pyrogram_type import PyrogramType -from ...ext.utils import encode - - -class Animation(PyrogramType): - """This object represents an animation file (GIF or H.264/MPEG-4 AVC video without sound). - - Parameters: - file_id (``str``): - Unique identifier for this file. - - width (``int``): - Animation width as defined by sender. - - height (``int``): - Animation height as defined by sender. - - duration (``int``): - Duration of the animation in seconds as defined by sender. - - thumb (:obj:`PhotoSize `, *optional*): - Animation thumbnail. - - file_name (``str``, *optional*): - Animation file name. - - mime_type (``str``, *optional*): - Mime type of a file as defined by sender. - - file_size (``int``, *optional*): - File size. - - date (``int``, *optional*): - Date the animation was sent in Unix time. - """ - - __slots__ = ["file_id", "thumb", "file_name", "mime_type", "file_size", "date", "width", "height", "duration"] - - def __init__( - self, - *, - client: "pyrogram.client.ext.BaseClient", - file_id: str, - width: int, - height: int, - duration: int, - thumb: PhotoSize = None, - file_name: str = None, - mime_type: str = None, - file_size: int = None, - date: int = None - ): - super().__init__(client) - - self.file_id = file_id - self.thumb = thumb - self.file_name = file_name - self.mime_type = mime_type - self.file_size = file_size - self.date = date - self.width = width - self.height = height - self.duration = duration - - @staticmethod - def _parse(client, animation: types.Document, video_attributes: types.DocumentAttributeVideo, - file_name: str) -> "Animation": - return Animation( - file_id=encode( - pack( - " -# -# 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 struct import pack - -import pyrogram -from pyrogram.api import types -from .photo_size import PhotoSize -from ..pyrogram_type import PyrogramType -from ...ext.utils import encode - - -class Audio(PyrogramType): - """This object represents an audio file to be treated as music by the Telegram clients. - - Parameters: - file_id (``str``): - Unique identifier for this file. - - duration (``int``): - Duration of the audio in seconds as defined by sender. - - thumb (:obj:`PhotoSize`, *optional*): - Thumbnail of the music file album cover. - - file_name (``str``, *optional*): - Audio file name. - - mime_type (``str``, *optional*): - MIME type of the file as defined by sender. - - file_size (``int``, *optional*): - File size. - - date (``int``, *optional*): - Date the audio was sent in Unix time. - - performer (``str``, *optional*): - Performer of the audio as defined by sender or by audio tags. - - title (``str``, *optional*): - Title of the audio as defined by sender or by audio tags. - """ - - __slots__ = ["file_id", "thumb", "file_name", "mime_type", "file_size", "date", "duration", "performer", "title"] - - def __init__( - self, - *, - client: "pyrogram.client.ext.BaseClient", - file_id: str, - duration: int, - thumb: PhotoSize = None, - file_name: str = None, - mime_type: str = None, - file_size: int = None, - date: int = None, - performer: str = None, - title: str = None - ): - super().__init__(client) - - self.file_id = file_id - self.thumb = thumb - self.file_name = file_name - self.mime_type = mime_type - self.file_size = file_size - self.date = date - self.duration = duration - self.performer = performer - self.title = title - - @staticmethod - def _parse(client, audio: types.Document, audio_attributes: types.DocumentAttributeAudio, - file_name: str) -> "Audio": - return Audio( - file_id=encode( - pack( - " -# -# 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 struct import pack - -import pyrogram -from pyrogram.api import types -from .photo_size import PhotoSize -from ..pyrogram_type import PyrogramType -from ...ext.utils import encode - - -class Document(PyrogramType): - """This object represents a general file (as opposed to photos, voice messages, audio files, ...). - - Parameters: - file_id (``str``): - Unique file identifier. - - thumb (:obj:`PhotoSize`, *optional*): - Document thumbnail as defined by sender. - - file_name (``str``, *optional*): - Original filename as defined by sender. - - mime_type (``str``, *optional*): - MIME type of the file as defined by sender. - - file_size (``int``, *optional*): - File size. - - date (``int``, *optional*): - Date the document was sent in Unix time. - """ - - __slots__ = ["file_id", "thumb", "file_name", "mime_type", "file_size", "date"] - - def __init__( - self, - *, - client: "pyrogram.client.ext.BaseClient", - file_id: str, - thumb: PhotoSize = None, - file_name: str = None, - mime_type: str = None, - file_size: int = None, - date: int = None - ): - super().__init__(client) - - self.file_id = file_id - self.thumb = thumb - self.file_name = file_name - self.mime_type = mime_type - self.file_size = file_size - self.date = date - - @staticmethod - def _parse(client, document: types.Document, file_name: str) -> "Document": - return Document( - file_id=encode( - pack( - " -# -# 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 . - -import pyrogram -from pyrogram.api import types -from .animation import Animation -from .photo import Photo -from ..pyrogram_type import PyrogramType - - -class Game(PyrogramType): - """This object represents a game. - Use BotFather to create and edit games, their short names will act as unique identifiers. - - Parameters: - id (``int``): - Unique identifier of the game. - - title (``str``): - Title of the game. - - short_name (``str``): - Unique short name of the game. - - description (``str``): - Description of the game. - - photo (:obj:`Photo`): - Photo that will be displayed in the game message in chats. - - animation (:obj:`Animation`, *optional*): - Animation that will be displayed in the game message in chats. - Upload via BotFather. - """ - - __slots__ = ["id", "title", "short_name", "description", "photo", "animation"] - - def __init__( - self, - *, - client: "pyrogram.client.ext.BaseClient", - id: int, - title: str, - short_name: str, - description: str, - photo: Photo, - animation: Animation = None - ): - super().__init__(client) - - self.id = id - self.title = title - self.short_name = short_name - self.description = description - self.photo = photo - self.animation = animation - - @staticmethod - def _parse(client, message: types.Message) -> "Game": - game = message.media.game # type: types.Game - animation = None - - if game.document: - attributes = {type(i): i for i in game.document.attributes} - - file_name = getattr( - attributes.get( - types.DocumentAttributeFilename, None - ), "file_name", None - ) - - animation = Animation._parse( - client, - game.document, - attributes.get(types.DocumentAttributeVideo, None), - file_name - ) - - return Game( - id=game.id, - title=game.title, - short_name=game.short_name, - description=game.description, - photo=Photo._parse(client, game.photo), - animation=animation, - client=client - ) diff --git a/pyrogram/client/types/messages_and_media/location.py b/pyrogram/client/types/messages_and_media/location.py deleted file mode 100644 index 6af918f71fb..00000000000 --- a/pyrogram/client/types/messages_and_media/location.py +++ /dev/null @@ -1,57 +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 . - -import pyrogram - -from pyrogram.api import types -from ..pyrogram_type import PyrogramType - - -class Location(PyrogramType): - """This object represents a point on the map. - - Parameters: - longitude (``float``): - Longitude as defined by sender. - - latitude (``float``): - Latitude as defined by sender. - """ - - __slots__ = ["longitude", "latitude"] - - def __init__( - self, - *, - client: "pyrogram.client.ext.BaseClient", - longitude: float, - latitude: float - ): - super().__init__(client) - - self.longitude = longitude - self.latitude = latitude - - @staticmethod - def _parse(client, geo_point: types.GeoPoint) -> "Location": - if isinstance(geo_point, types.GeoPoint): - return Location( - longitude=geo_point.long, - latitude=geo_point.lat, - client=client - ) diff --git a/pyrogram/client/types/messages_and_media/message.py b/pyrogram/client/types/messages_and_media/message.py deleted file mode 100644 index a4d759c8ee2..00000000000 --- a/pyrogram/client/types/messages_and_media/message.py +++ /dev/null @@ -1,2936 +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 functools import partial -from typing import List, Match, Union - -import pyrogram -from pyrogram.api import types -from pyrogram.client.types.input_media import InputMedia -from pyrogram.errors import MessageIdsEmpty -from .contact import Contact -from .location import Location -from .message_entity import MessageEntity -from ..messages_and_media.photo import Photo -from ..pyrogram_type import PyrogramType -from ..update import Update -from ..user_and_chats.chat import Chat -from ..user_and_chats.user import User - - -class Str(str): - def __init__(self, *args): - super().__init__() - - self._client = None - self._entities = None - - def init(self, client, entities): - self._client = client - self._entities = entities - - return self - - @property - def text(self): - return self - - @property - def markdown(self): - return self._client.markdown.unparse(self, self._entities) - - @property - def html(self): - return self._client.html.unparse(self, self._entities) - - -class Message(PyrogramType, Update): - """This object represents a message. - - Parameters: - message_id (``int``): - Unique message identifier inside this chat. - - date (``int``, *optional*): - Date the message was sent in Unix time. - - chat (:obj:`Chat`, *optional*): - Conversation the message belongs to. - - from_user (:obj:`User`, *optional*): - Sender, empty for messages sent to channels. - - forward_from (:obj:`User`, *optional*): - For forwarded messages, sender of the original message. - - forward_sender_name (``str``, *optional*): - For messages forwarded from users who have hidden their accounts, name of the user. - - forward_from_chat (:obj:`Chat`, *optional*): - For messages forwarded from channels, information about the original channel. - - forward_from_message_id (``int``, *optional*): - For messages forwarded from channels, identifier of the original message in the channel. - - forward_signature (``str``, *optional*): - For messages forwarded from channels, signature of the post author if present. - - forward_date (``int``, *optional*): - For forwarded messages, date the original message was sent in Unix time. - - reply_to_message (:obj:`Message`, *optional*): - For replies, the original message. Note that the Message object in this field will not contain - further reply_to_message fields even if it itself is a reply. - - mentioned (``bool``, *optional*): - The message contains a mention. - - empty (``bool``, *optional*): - The message is empty. - A message can be empty in case it was deleted or you tried to retrieve a message that doesn't exist yet. - - service (``bool``, *optional*): - The message is a service message. - A service message has one and only one of these 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. - - media (``bool`` *optional*): - The message is a media message. - A media message has one and only one of these fields set: audio, document, photo, sticker, video, animation, - voice, video_note, contact, location, venue. - - edit_date (``int``, *optional*): - Date the message was last edited in Unix time. - - media_group_id (``str``, *optional*): - The unique identifier of a media message group this message belongs to. - - author_signature (``str``, *optional*): - Signature of the post author for messages in channels. - - text (``str``, *optional*): - For text messages, the actual UTF-8 text of the message, 0-4096 characters. - If the message contains entities (bold, italic, ...) you can access *text.markdown* or - *text.html* to get the marked up message text. In case there is no entity, the fields - will contain the same text as *text*. - - entities (List of :obj:`MessageEntity`, *optional*): - For text messages, special entities like usernames, URLs, bot commands, etc. that appear in the text. - - caption_entities (List of :obj:`MessageEntity`, *optional*): - For messages with a caption, special entities like usernames, URLs, bot commands, etc. that appear - in the caption. - - audio (:obj:`Audio`, *optional*): - Message is an audio file, information about the file. - - document (:obj:`Document`, *optional*): - Message is a general file, information about the file. - - photo (:obj:`Photo`, *optional*): - Message is a photo, information about the photo. - - sticker (:obj:`Sticker`, *optional*): - Message is a sticker, information about the sticker. - - animation (:obj:`Animation`, *optional*): - Message is an animation, information about the animation. - - game (:obj:`Game`, *optional*): - Message is a game, information about the game. - - video (:obj:`Video`, *optional*): - Message is a video, information about the video. - - voice (:obj:`Voice`, *optional*): - Message is a voice message, information about the file. - - video_note (:obj:`VideoNote`, *optional*): - Message is a video note, information about the video message. - - caption (``str``, *optional*): - Caption for the audio, document, photo, video or voice, 0-1024 characters. - If the message contains caption entities (bold, italic, ...) you can access *caption.markdown* or - *caption.html* to get the marked up caption text. In case there is no caption entity, the fields - will contain the same text as *caption*. - - contact (:obj:`Contact`, *optional*): - Message is a shared contact, information about the contact. - - location (:obj:`Location`, *optional*): - Message is a shared location, information about the location. - - venue (:obj:`Venue`, *optional*): - Message is a venue, information about the venue. - - web_page (``bool``, *optional*): - Message was sent with a webpage preview. - **Note:** Support for web pages is still basic; a simple boolean is set in case the message contains a - web page preview. In future versions this property could turn into a full web page object that contains - more details. - - poll (:obj:`Poll`, *optional*): - Message is a native poll, information about the poll. - - new_chat_members (List of :obj:`User`, *optional*): - New members that were added to the group or supergroup and information about them - (the bot itself may be one of these members). - - left_chat_member (:obj:`User`, *optional*): - A member was removed from the group, information about them (this member may be the bot itself). - - new_chat_title (``str``, *optional*): - A chat title was changed to this value. - - new_chat_photo (:obj:`Photo`, *optional*): - A chat photo was change to this value. - - delete_chat_photo (``bool``, *optional*): - Service message: the chat photo was deleted. - - group_chat_created (``bool``, *optional*): - Service message: the group has been created. - - supergroup_chat_created (``bool``, *optional*): - Service message: the supergroup has been created. - This field can't be received in a message coming through updates, because bot can't be a member of a - supergroup when it is created. It can only be found in reply_to_message if someone replies to a very - first message in a directly created supergroup. - - channel_chat_created (``bool``, *optional*): - Service message: the channel has been created. - This field can't be received in a message coming through updates, because bot can't be a member of a - channel when it is created. It can only be found in reply_to_message if someone replies to a very - first message in a channel. - - migrate_to_chat_id (``int``, *optional*): - The group has been migrated to a supergroup with the specified identifier. - This number may be greater than 32 bits and some programming languages may have difficulty/silent defects - in interpreting it. But it is smaller than 52 bits, so a signed 64 bit integer or double-precision float - type are safe for storing this identifier. - - migrate_from_chat_id (``int``, *optional*): - The supergroup has been migrated from a group with the specified identifier. - This number may be greater than 32 bits and some programming languages may have difficulty/silent defects - in interpreting it. But it is smaller than 52 bits, so a signed 64 bit integer or double-precision float - type are safe for storing this identifier. - - pinned_message (:obj:`Message`, *optional*): - Specified message was pinned. - Note that the Message object in this field will not contain further reply_to_message fields even if it - is itself a reply. - - game_high_score (:obj:`GameHighScore`, *optional*): - The game score for a user. - The reply_to_message field will contain the game Message. - - views (``int``, *optional*): - Channel post views. - - via_bot (:obj:`User`): - The information of the bot that generated the message from an inline query of a user. - - outgoing (``bool``, *optional*): - Whether the message is incoming or outgoing. - Messages received from other chats are incoming (*outgoing* is False). - Messages sent from yourself to other chats are outgoing (*outgoing* is True). - An exception is made for your own personal chat; messages sent there will be incoming. - - matches (List of regex Matches, *optional*): - A list containing all `Match Objects `_ that match - the text of this message. Only applicable when using :obj:`Filters.regex `. - - command (List of ``str``, *optional*): - A list containing the command and its arguments, if any. - E.g.: "/start 1 2 3" would produce ["start", "1", "2", "3"]. - Only applicable when using :obj:`Filters.command `. - - reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*): - Additional interface options. An object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. - """ - - # TODO: Add game missing field. Also invoice, successful_payment, connected_website - - __slots__ = [ - "message_id", "date", "chat", "from_user", "forward_from", "forward_sender_name", "forward_from_chat", - "forward_from_message_id", "forward_signature", "forward_date", "reply_to_message", "mentioned", "empty", - "service", "media", "edit_date", "media_group_id", "author_signature", "text", "entities", "caption_entities", - "audio", "document", "photo", "sticker", "animation", "game", "video", "voice", "video_note", "caption", - "contact", "location", "venue", "web_page", "poll", "new_chat_members", "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_high_score", "views", "via_bot", - "outgoing", "matches", "command", "reply_markup" - ] - - def __init__( - self, - *, - client: "pyrogram.client.ext.BaseClient", - message_id: int, - date: int = None, - chat: Chat = None, - from_user: User = None, - forward_from: User = None, - forward_sender_name: str = None, - forward_from_chat: Chat = None, - forward_from_message_id: int = None, - forward_signature: str = None, - forward_date: int = None, - reply_to_message: "Message" = None, - mentioned: bool = None, - empty: bool = None, - service: bool = None, - media: bool = None, - edit_date: int = None, - media_group_id: str = None, - author_signature: str = None, - text: Str = None, - entities: List["pyrogram.MessageEntity"] = None, - caption_entities: List["pyrogram.MessageEntity"] = None, - audio: "pyrogram.Audio" = None, - document: "pyrogram.Document" = None, - photo: "pyrogram.Photo" = None, - sticker: "pyrogram.Sticker" = None, - animation: "pyrogram.Animation" = None, - game: "pyrogram.Game" = None, - video: "pyrogram.Video" = None, - voice: "pyrogram.Voice" = None, - video_note: "pyrogram.VideoNote" = None, - caption: Str = None, - contact: "pyrogram.Contact" = None, - location: "pyrogram.Location" = None, - venue: "pyrogram.Venue" = None, - web_page: bool = None, - poll: "pyrogram.Poll" = None, - new_chat_members: List[User] = None, - left_chat_member: User = None, - new_chat_title: str = None, - new_chat_photo: "pyrogram.Photo" = None, - delete_chat_photo: bool = None, - group_chat_created: bool = None, - supergroup_chat_created: bool = None, - channel_chat_created: bool = None, - migrate_to_chat_id: int = None, - migrate_from_chat_id: int = None, - pinned_message: "Message" = None, - game_high_score: int = None, - views: int = None, - via_bot: User = None, - outgoing: bool = None, - matches: List[Match] = None, - command: List[str] = None, - reply_markup: Union[ - "pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply" - ] = None - ): - super().__init__(client) - - self.message_id = message_id - self.date = date - self.chat = chat - self.from_user = from_user - self.forward_from = forward_from - self.forward_sender_name = forward_sender_name - self.forward_from_chat = forward_from_chat - self.forward_from_message_id = forward_from_message_id - self.forward_signature = forward_signature - self.forward_date = forward_date - self.reply_to_message = reply_to_message - self.mentioned = mentioned - self.empty = empty - self.service = service - self.media = media - self.edit_date = edit_date - self.media_group_id = media_group_id - self.author_signature = author_signature - self.text = text - self.entities = entities - self.caption_entities = caption_entities - self.audio = audio - self.document = document - self.photo = photo - self.sticker = sticker - self.animation = animation - self.game = game - self.video = video - self.voice = voice - self.video_note = video_note - self.caption = caption - self.contact = contact - self.location = location - self.venue = venue - self.web_page = web_page - self.poll = poll - self.new_chat_members = new_chat_members - self.left_chat_member = left_chat_member - self.new_chat_title = new_chat_title - self.new_chat_photo = new_chat_photo - self.delete_chat_photo = delete_chat_photo - self.group_chat_created = group_chat_created - self.supergroup_chat_created = supergroup_chat_created - self.channel_chat_created = channel_chat_created - self.migrate_to_chat_id = migrate_to_chat_id - self.migrate_from_chat_id = migrate_from_chat_id - self.pinned_message = pinned_message - self.game_high_score = game_high_score - self.views = views - self.via_bot = via_bot - self.outgoing = outgoing - self.matches = matches - self.command = command - self.reply_markup = reply_markup - - @staticmethod - def _parse(client, message: types.Message or types.MessageService or types.MessageEmpty, users: dict, chats: dict, - replies: int = 1): - if isinstance(message, types.MessageEmpty): - return Message(message_id=message.id, empty=True, client=client) - - if isinstance(message, types.MessageService): - action = message.action - - new_chat_members = None - left_chat_member = None - new_chat_title = None - delete_chat_photo = None - migrate_to_chat_id = None - migrate_from_chat_id = None - group_chat_created = None - channel_chat_created = None - new_chat_photo = None - - if isinstance(action, types.MessageActionChatAddUser): - new_chat_members = [User._parse(client, users[i]) for i in action.users] - elif isinstance(action, types.MessageActionChatJoinedByLink): - new_chat_members = [User._parse(client, users[message.from_id])] - elif isinstance(action, types.MessageActionChatDeleteUser): - left_chat_member = User._parse(client, users[action.user_id]) - elif isinstance(action, types.MessageActionChatEditTitle): - new_chat_title = action.title - elif isinstance(action, types.MessageActionChatDeletePhoto): - delete_chat_photo = True - elif isinstance(action, types.MessageActionChatMigrateTo): - migrate_to_chat_id = action.channel_id - elif isinstance(action, types.MessageActionChannelMigrateFrom): - migrate_from_chat_id = action.chat_id - elif isinstance(action, types.MessageActionChatCreate): - group_chat_created = True - elif isinstance(action, types.MessageActionChannelCreate): - channel_chat_created = True - elif isinstance(action, types.MessageActionChatEditPhoto): - new_chat_photo = Photo._parse(client, action.photo) - - parsed_message = Message( - message_id=message.id, - date=message.date, - chat=Chat._parse(client, message, users, chats), - from_user=User._parse(client, users.get(message.from_id, None)), - service=True, - new_chat_members=new_chat_members, - left_chat_member=left_chat_member, - new_chat_title=new_chat_title, - new_chat_photo=new_chat_photo, - delete_chat_photo=delete_chat_photo, - migrate_to_chat_id=int("-100" + str(migrate_to_chat_id)) if migrate_to_chat_id else None, - migrate_from_chat_id=-migrate_from_chat_id if migrate_from_chat_id else None, - group_chat_created=group_chat_created, - channel_chat_created=channel_chat_created, - client=client - # TODO: supergroup_chat_created - ) - - if isinstance(action, types.MessageActionPinMessage): - try: - parsed_message.pinned_message = client.get_messages( - parsed_message.chat.id, - reply_to_message_ids=message.id, - replies=0 - ) - except MessageIdsEmpty: - pass - - if isinstance(action, types.MessageActionGameScore): - parsed_message.game_high_score = pyrogram.GameHighScore._parse_action(client, message, users) - - if message.reply_to_msg_id and replies: - try: - parsed_message.reply_to_message = client.get_messages( - parsed_message.chat.id, - reply_to_message_ids=message.id, - replies=0 - ) - except MessageIdsEmpty: - pass - - return parsed_message - - if isinstance(message, types.Message): - entities = [MessageEntity._parse(client, entity, users) for entity in message.entities] - entities = list(filter(lambda x: x is not None, entities)) - - forward_from = None - forward_sender_name = None - forward_from_chat = None - forward_from_message_id = None - forward_signature = None - forward_date = None - - forward_header = message.fwd_from # type: types.MessageFwdHeader - - if forward_header: - forward_date = forward_header.date - - if forward_header.from_id: - forward_from = User._parse(client, users[forward_header.from_id]) - elif forward_header.from_name: - forward_sender_name = forward_header.from_name - else: - forward_from_chat = Chat._parse_channel_chat(client, chats[forward_header.channel_id]) - forward_from_message_id = forward_header.channel_post - forward_signature = forward_header.post_author - - photo = None - location = None - contact = None - venue = None - game = None - audio = None - voice = None - animation = None - video = None - video_note = None - sticker = None - document = None - web_page = None - poll = None - - media = message.media - - if media: - if isinstance(media, types.MessageMediaPhoto): - photo = Photo._parse(client, media.photo) - elif isinstance(media, types.MessageMediaGeo): - location = Location._parse(client, media.geo) - elif isinstance(media, types.MessageMediaContact): - contact = Contact._parse(client, media) - elif isinstance(media, types.MessageMediaVenue): - venue = pyrogram.Venue._parse(client, media) - elif isinstance(media, types.MessageMediaGame): - game = pyrogram.Game._parse(client, message) - elif isinstance(media, types.MessageMediaDocument): - doc = media.document - - if isinstance(doc, types.Document): - attributes = {type(i): i for i in doc.attributes} - - file_name = getattr( - attributes.get( - types.DocumentAttributeFilename, None - ), "file_name", None - ) - - if types.DocumentAttributeAudio in attributes: - audio_attributes = attributes[types.DocumentAttributeAudio] - - if audio_attributes.voice: - voice = pyrogram.Voice._parse(client, doc, audio_attributes) - else: - audio = pyrogram.Audio._parse(client, doc, audio_attributes, file_name) - elif types.DocumentAttributeAnimated in attributes: - video_attributes = attributes.get(types.DocumentAttributeVideo, None) - - animation = pyrogram.Animation._parse(client, doc, video_attributes, file_name) - elif types.DocumentAttributeVideo in attributes: - video_attributes = attributes[types.DocumentAttributeVideo] - - if video_attributes.round_message: - video_note = pyrogram.VideoNote._parse(client, doc, video_attributes) - else: - video = pyrogram.Video._parse(client, doc, video_attributes, file_name) - elif types.DocumentAttributeSticker in attributes: - sticker = pyrogram.Sticker._parse( - client, doc, - attributes.get(types.DocumentAttributeImageSize, None), - attributes[types.DocumentAttributeSticker], - file_name - ) - else: - document = pyrogram.Document._parse(client, doc, file_name) - elif isinstance(media, types.MessageMediaWebPage): - web_page = True - media = None - elif isinstance(media, types.MessageMediaPoll): - poll = pyrogram.Poll._parse(client, media) - else: - media = None - - reply_markup = message.reply_markup - - if reply_markup: - if isinstance(reply_markup, types.ReplyKeyboardForceReply): - reply_markup = pyrogram.ForceReply.read(reply_markup) - elif isinstance(reply_markup, types.ReplyKeyboardMarkup): - reply_markup = pyrogram.ReplyKeyboardMarkup.read(reply_markup) - elif isinstance(reply_markup, types.ReplyInlineMarkup): - reply_markup = pyrogram.InlineKeyboardMarkup.read(reply_markup) - elif isinstance(reply_markup, types.ReplyKeyboardHide): - reply_markup = pyrogram.ReplyKeyboardRemove.read(reply_markup) - else: - reply_markup = None - - parsed_message = Message( - message_id=message.id, - date=message.date, - chat=Chat._parse(client, message, users, chats), - from_user=User._parse(client, users.get(message.from_id, None)), - text=Str(message.message).init(client, entities) or None if media is None else None, - caption=Str(message.message).init(client, entities) or None if media is not None else None, - entities=entities or None if media is None else None, - caption_entities=entities or None if media is not None else None, - author_signature=message.post_author, - forward_from=forward_from, - forward_sender_name=forward_sender_name, - forward_from_chat=forward_from_chat, - forward_from_message_id=forward_from_message_id, - forward_signature=forward_signature, - forward_date=forward_date, - mentioned=message.mentioned, - media=bool(media) or None, - edit_date=message.edit_date, - media_group_id=message.grouped_id, - photo=photo, - location=location, - contact=contact, - venue=venue, - audio=audio, - voice=voice, - animation=animation, - game=game, - video=video, - video_note=video_note, - sticker=sticker, - document=document, - web_page=web_page, - poll=poll, - views=message.views, - via_bot=User._parse(client, users.get(message.via_bot_id, None)), - outgoing=message.out, - reply_markup=reply_markup, - client=client - ) - - if message.reply_to_msg_id and replies: - try: - parsed_message.reply_to_message = client.get_messages( - parsed_message.chat.id, - reply_to_message_ids=message.id, - replies=replies - 1 - ) - except MessageIdsEmpty: - pass - - return parsed_message - - def reply( - self, - text: str, - quote: bool = None, - parse_mode: str = "", - disable_web_page_preview: bool = None, - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup=None - ) -> "Message": - """Bound method *reply* of :obj:`Message`. - - Use as a shortcut for: - - .. code-block:: python - - client.send_message( - chat_id=message.chat.id, - text="hello", - reply_to_message_id=message.message_id - ) - - Example: - .. code-block:: python - - message.reply("hello", quote=True) - - Parameters: - text (``str``): - Text of the message to be sent. - - quote (``bool``, *optional*): - If ``True``, the message will be sent as a reply to this message. - If *reply_to_message_id* is passed, this parameter will be ignored. - Defaults to ``True`` in group chats and ``False`` in private chats. - - parse_mode (``str``, *optional*): - Pass "markdown" or "html" if you want Telegram apps to show bold, italic, fixed-width text or inline - URLs in your message. Defaults to "markdown". - - disable_web_page_preview (``bool``, *optional*): - Disables link previews for links in this message. - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - reply_to_message_id (``int``, *optional*): - If the message is a reply, ID of the original message. - - reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*): - Additional interface options. An object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. - - Returns: - On success, the sent Message is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - if quote is None: - quote = self.chat.type != "private" - - if reply_to_message_id is None and quote: - reply_to_message_id = self.message_id - - return self._client.send_message( - chat_id=self.chat.id, - text=text, - parse_mode=parse_mode, - disable_web_page_preview=disable_web_page_preview, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup - ) - - def reply_animation( - self, - animation: str, - quote: bool = None, - caption: str = "", - parse_mode: str = "", - duration: int = 0, - width: int = 0, - height: int = 0, - thumb: str = None, - disable_notification: bool = None, - reply_markup: Union[ - "pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply" - ] = None, - reply_to_message_id: int = None, - progress: callable = None, - progress_args: tuple = () - ) -> "Message": - """Bound method *reply_animation* of :obj:`Message`. - - Use as a shortcut for: - - .. code-block:: python - - client.send_animation( - chat_id=message.chat.id, - animation=animation - ) - - Example: - .. code-block:: python - - message.reply_animation(animation) - - Parameters: - animation (``str``): - Animation to send. - Pass a file_id as string to send an animation that exists on the Telegram servers, - pass an HTTP URL as a string for Telegram to get an animation from the Internet, or - pass a file path as string to upload a new animation that exists on your local machine. - - quote (``bool``, *optional*): - If ``True``, the message will be sent as a reply to this message. - If *reply_to_message_id* is passed, this parameter will be ignored. - Defaults to ``True`` in group chats and ``False`` in private chats. - - caption (``str``, *optional*): - Animation caption, 0-1024 characters. - - parse_mode (``str``, *optional*): - Pass "markdown" or "html" if you want Telegram apps to show bold, italic, fixed-width text or inline - URLs in your caption. Defaults to "markdown". - - duration (``int``, *optional*): - Duration of sent animation in seconds. - - width (``int``, *optional*): - Animation width. - - height (``int``, *optional*): - Animation height. - - thumb (``str``, *optional*): - Thumbnail of the animation file sent. - The thumbnail should be in JPEG format and less than 200 KB in size. - A thumbnail's width and height should not exceed 320 pixels. - Thumbnails can't be reused and can be only uploaded as a new file. - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - reply_to_message_id (``int``, *optional*): - If the message is a reply, ID of the original message. - - reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*): - Additional interface options. An object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. - - progress (``callable``, *optional*): - Pass a callback function to view the upload progress. - The function must take *(client, current, total, \*args)* as positional arguments (look at the section - below for a detailed description). - - progress_args (``tuple``, *optional*): - Extra custom arguments for the progress callback function. Useful, for example, if you want to pass - a chat_id and a message_id in order to edit a message with the updated progress. - - Other Parameters: - client (:obj:`Client`): - The Client itself, useful when you want to call other API methods inside the callback function. - - current (``int``): - The amount of bytes uploaded so far. - - total (``int``): - The size of the file. - - *args (``tuple``, *optional*): - Extra custom arguments as defined in the *progress_args* parameter. - You can either keep *\*args* or add every single extra argument in your function signature. - - Returns: - On success, the sent :obj:`Message` is returned. - In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - if quote is None: - quote = self.chat.type != "private" - - if reply_to_message_id is None and quote: - reply_to_message_id = self.message_id - - return self._client.send_animation( - chat_id=self.chat.id, - animation=animation, - caption=caption, - parse_mode=parse_mode, - duration=duration, - width=width, - height=height, - thumb=thumb, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - progress=progress, - progress_args=progress_args - ) - - def reply_audio( - self, - audio: str, - quote: bool = None, - caption: str = "", - parse_mode: str = "", - duration: int = 0, - performer: str = None, - title: str = None, - thumb: str = None, - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union[ - "pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply" - ] = None, - progress: callable = None, - progress_args: tuple = () - ) -> "Message": - """Bound method *reply_audio* of :obj:`Message`. - - Use as a shortcut for: - - .. code-block:: python - - client.send_audio( - chat_id=message.chat.id, - audio=audio - ) - - Example: - .. code-block:: python - - message.reply_audio(audio) - - Parameters: - audio (``str``): - Audio file to send. - Pass a file_id as string to send an audio file that exists on the Telegram servers, - pass an HTTP URL as a string for Telegram to get an audio file from the Internet, or - pass a file path as string to upload a new audio file that exists on your local machine. - - quote (``bool``, *optional*): - If ``True``, the message will be sent as a reply to this message. - If *reply_to_message_id* is passed, this parameter will be ignored. - Defaults to ``True`` in group chats and ``False`` in private chats. - - caption (``str``, *optional*): - Audio caption, 0-1024 characters. - - parse_mode (``str``, *optional*): - Pass "markdown" or "html" if you want Telegram apps to show bold, italic, fixed-width text or inline - URLs in your caption. Defaults to "markdown". - - duration (``int``, *optional*): - Duration of the audio in seconds. - - performer (``str``, *optional*): - Performer. - - title (``str``, *optional*): - Track name. - - thumb (``str``, *optional*): - Thumbnail of the music file album cover. - The thumbnail should be in JPEG format and less than 200 KB in size. - A thumbnail's width and height should not exceed 320 pixels. - Thumbnails can't be reused and can be only uploaded as a new file. - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - reply_to_message_id (``int``, *optional*): - If the message is a reply, ID of the original message. - - reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*): - Additional interface options. An object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. - - progress (``callable``, *optional*): - Pass a callback function to view the upload progress. - The function must take *(client, current, total, \*args)* as positional arguments (look at the section - below for a detailed description). - - progress_args (``tuple``, *optional*): - Extra custom arguments for the progress callback function. Useful, for example, if you want to pass - a chat_id and a message_id in order to edit a message with the updated progress. - - Other Parameters: - client (:obj:`Client`): - The Client itself, useful when you want to call other API methods inside the callback function. - - current (``int``): - The amount of bytes uploaded so far. - - total (``int``): - The size of the file. - - *args (``tuple``, *optional*): - Extra custom arguments as defined in the *progress_args* parameter. - You can either keep *\*args* or add every single extra argument in your function signature. - - Returns: - On success, the sent :obj:`Message` is returned. - In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - if quote is None: - quote = self.chat.type != "private" - - if reply_to_message_id is None and quote: - reply_to_message_id = self.message_id - - return self._client.send_audio( - chat_id=self.chat.id, - audio=audio, - caption=caption, - parse_mode=parse_mode, - duration=duration, - performer=performer, - title=title, - thumb=thumb, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - progress=progress, - progress_args=progress_args - ) - - def reply_cached_media( - self, - file_id: str, - quote: bool = None, - caption: str = "", - parse_mode: str = "", - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union[ - "pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply" - ] = None - ) -> "Message": - """Bound method *reply_cached_media* of :obj:`Message`. - - Use as a shortcut for: - - .. code-block:: python - - client.send_cached_media( - chat_id=message.chat.id, - file_id=file_id - ) - - Example: - .. code-block:: python - - message.reply_cached_media(file_id) - - Parameters: - file_id (``str``): - Media to send. - Pass a file_id as string to send a media that exists on the Telegram servers. - - quote (``bool``, *optional*): - If ``True``, the message will be sent as a reply to this message. - If *reply_to_message_id* is passed, this parameter will be ignored. - Defaults to ``True`` in group chats and ``False`` in private chats. - - caption (``bool``, *optional*): - Media caption, 0-1024 characters. - - parse_mode (``str``, *optional*): - Pass "markdown" or "html" if you want Telegram apps to show bold, italic, fixed-width text or inline - URLs in your caption. Defaults to "markdown". - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - reply_to_message_id (``int``, *optional*): - If the message is a reply, ID of the original message. - - reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*): - Additional interface options. An object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. - - Returns: - On success, the sent :obj:`Message` is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - if quote is None: - quote = self.chat.type != "private" - - if reply_to_message_id is None and quote: - reply_to_message_id = self.message_id - - return self._client.send_cached_media( - chat_id=self.chat.id, - file_id=file_id, - caption=caption, - parse_mode=parse_mode, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup - ) - - def reply_chat_action(self, action: str) -> bool: - """Bound method *reply_chat_action* of :obj:`Message`. - - Use as a shortcut for: - - .. code-block:: python - - client.send_chat_action( - chat_id=message.chat.id, - action="typing" - ) - - Example: - .. code-block:: python - - message.reply_chat_action("typing") - - Parameters: - action (``str``): - Type of action to broadcast. Choose one, depending on what the user is about to receive: *"typing"* for - text messages, *"upload_photo"* for photos, *"record_video"* or *"upload_video"* for videos, - *"record_audio"* or *"upload_audio"* for audio files, *"upload_document"* for general files, - *"find_location"* for location data, *"record_video_note"* or *"upload_video_note"* for video notes, - *"choose_contact"* for contacts, *"playing"* for games or *"cancel"* to cancel any chat action currently - displayed. - - Returns: - ``bool``: On success, True is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - ValueError: In case the provided string is not a valid chat action. - """ - return self._client.send_chat_action( - chat_id=self.chat.id, - action=action - ) - - def reply_contact( - self, - phone_number: str, - first_name: str, - quote: bool = None, - last_name: str = "", - vcard: str = "", - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union[ - "pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply" - ] = None - ) -> "Message": - """Bound method *reply_contact* of :obj:`Message`. - - Use as a shortcut for: - - .. code-block:: python - - client.send_contact( - chat_id=message.chat.id, - phone_number=phone_number, - first_name=first_name - ) - - Example: - .. code-block:: python - - message.reply_contact(phone_number, "Dan") - - Parameters: - phone_number (``str``): - Contact's phone number. - - first_name (``str``): - Contact's first name. - - quote (``bool``, *optional*): - If ``True``, the message will be sent as a reply to this message. - If *reply_to_message_id* is passed, this parameter will be ignored. - Defaults to ``True`` in group chats and ``False`` in private chats. - - last_name (``str``, *optional*): - Contact's last name. - - vcard (``str``, *optional*): - Additional data about the contact in the form of a vCard, 0-2048 bytes - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - reply_to_message_id (``int``, *optional*): - If the message is a reply, ID of the original message. - - reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*): - Additional interface options. An object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. - - Returns: - On success, the sent :obj:`Message` is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - if quote is None: - quote = self.chat.type != "private" - - if reply_to_message_id is None and quote: - reply_to_message_id = self.message_id - - return self._client.send_contact( - chat_id=self.chat.id, - phone_number=phone_number, - first_name=first_name, - last_name=last_name, - vcard=vcard, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup - ) - - def reply_document( - self, - document: str, - quote: bool = None, - thumb: str = None, - caption: str = "", - parse_mode: str = "", - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union[ - "pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply" - ] = None, - progress: callable = None, - progress_args: tuple = () - ) -> "Message": - """Bound method *reply_document* of :obj:`Message`. - - Use as a shortcut for: - - .. code-block:: python - - client.send_document( - chat_id=message.chat.id, - document=document - ) - - Example: - .. code-block:: python - - message.reply_document(document) - - Parameters: - document (``str``): - File to send. - Pass a file_id as string to send a file that exists on the Telegram servers, - pass an HTTP URL as a string for Telegram to get a file from the Internet, or - pass a file path as string to upload a new file that exists on your local machine. - - quote (``bool``, *optional*): - If ``True``, the message will be sent as a reply to this message. - If *reply_to_message_id* is passed, this parameter will be ignored. - Defaults to ``True`` in group chats and ``False`` in private chats. - - thumb (``str``, *optional*): - Thumbnail of the file sent. - The thumbnail should be in JPEG format and less than 200 KB in size. - A thumbnail's width and height should not exceed 320 pixels. - Thumbnails can't be reused and can be only uploaded as a new file. - - caption (``str``, *optional*): - Document caption, 0-1024 characters. - - parse_mode (``str``, *optional*): - Pass "markdown" or "html" if you want Telegram apps to show bold, italic, fixed-width text or inline - URLs in your caption. Defaults to "markdown". - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - reply_to_message_id (``int``, *optional*): - If the message is a reply, ID of the original message. - - reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*): - Additional interface options. An object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. - - progress (``callable``, *optional*): - Pass a callback function to view the upload progress. - The function must take *(client, current, total, \*args)* as positional arguments (look at the section - below for a detailed description). - - progress_args (``tuple``, *optional*): - Extra custom arguments for the progress callback function. Useful, for example, if you want to pass - a chat_id and a message_id in order to edit a message with the updated progress. - - Other Parameters: - client (:obj:`Client`): - The Client itself, useful when you want to call other API methods inside the callback function. - - current (``int``): - The amount of bytes uploaded so far. - - total (``int``): - The size of the file. - - *args (``tuple``, *optional*): - Extra custom arguments as defined in the *progress_args* parameter. - You can either keep *\*args* or add every single extra argument in your function signature. - - Returns: - On success, the sent :obj:`Message` is returned. - In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - if quote is None: - quote = self.chat.type != "private" - - if reply_to_message_id is None and quote: - reply_to_message_id = self.message_id - - return self._client.send_document( - chat_id=self.chat.id, - document=document, - thumb=thumb, - caption=caption, - parse_mode=parse_mode, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - progress=progress, - progress_args=progress_args - ) - - def reply_game( - self, - game_short_name: str, - quote: bool = None, - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union[ - "pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply" - ] = None - ) -> "Message": - """Bound method *reply_game* of :obj:`Message`. - - Use as a shortcut for: - - .. code-block:: python - - client.send_game( - chat_id=message.chat.id, - game_short_name="lumberjack" - ) - - Example: - .. code-block:: python - - message.reply_game("lumberjack") - - Parameters: - game_short_name (``str``): - Short name of the game, serves as the unique identifier for the game. Set up your games via Botfather. - - quote (``bool``, *optional*): - If ``True``, the message will be sent as a reply to this message. - If *reply_to_message_id* is passed, this parameter will be ignored. - Defaults to ``True`` in group chats and ``False`` in private chats. - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - reply_to_message_id (``int``, *optional*): - If the message is a reply, ID of the original message. - - reply_markup (:obj:`InlineKeyboardMarkup`, *optional*): - An object for an inline keyboard. If empty, one ‘Play game_title’ button will be shown automatically. - If not empty, the first button must launch the game. - - Returns: - On success, the sent :obj:`Message` is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - if quote is None: - quote = self.chat.type != "private" - - if reply_to_message_id is None and quote: - reply_to_message_id = self.message_id - - return self._client.send_game( - chat_id=self.chat.id, - game_short_name=game_short_name, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup - ) - - def reply_inline_bot_result( - self, - query_id: int, - result_id: str, - quote: bool = None, - disable_notification: bool = None, - reply_to_message_id: int = None, - hide_via: bool = None - ) -> "Message": - """Bound method *reply_inline_bot_result* of :obj:`Message`. - - Use as a shortcut for: - - .. code-block:: python - - client.send_inline_bot_result( - chat_id=message.chat.id, - query_id=query_id, - result_id=result_id - ) - - Example: - .. code-block:: python - - message.reply_inline_bot_result(query_id, result_id) - - Parameters: - query_id (``int``): - Unique identifier for the answered query. - - result_id (``str``): - Unique identifier for the result that was chosen. - - quote (``bool``, *optional*): - If ``True``, the message will be sent as a reply to this message. - If *reply_to_message_id* is passed, this parameter will be ignored. - Defaults to ``True`` in group chats and ``False`` in private chats. - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - reply_to_message_id (``bool``, *optional*): - If the message is a reply, ID of the original message. - - hide_via (``bool``): - Sends the message with *via @bot* hidden. - - Returns: - On success, the sent Message is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - if quote is None: - quote = self.chat.type != "private" - - if reply_to_message_id is None and quote: - reply_to_message_id = self.message_id - - return self._client.send_inline_bot_result( - chat_id=self.chat.id, - query_id=query_id, - result_id=result_id, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - hide_via=hide_via - ) - - def reply_location( - self, - latitude: float, - longitude: float, - quote: bool = None, - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union[ - "pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply" - ] = None - ) -> "Message": - """Bound method *reply_location* of :obj:`Message`. - - Use as a shortcut for: - - .. code-block:: python - - client.send_location( - chat_id=message.chat.id, - latitude=41.890251, - longitude=12.492373 - ) - - Example: - .. code-block:: python - - message.reply_location(41.890251, 12.492373) - - Parameters: - latitude (``float``): - Latitude of the location. - - longitude (``float``): - Longitude of the location. - - quote (``bool``, *optional*): - If ``True``, the message will be sent as a reply to this message. - If *reply_to_message_id* is passed, this parameter will be ignored. - Defaults to ``True`` in group chats and ``False`` in private chats. - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - reply_to_message_id (``int``, *optional*): - If the message is a reply, ID of the original message - - reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*): - Additional interface options. An object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. - - Returns: - On success, the sent :obj:`Message` is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - if quote is None: - quote = self.chat.type != "private" - - if reply_to_message_id is None and quote: - reply_to_message_id = self.message_id - - return self._client.send_location( - chat_id=self.chat.id, - latitude=latitude, - longitude=longitude, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup - ) - - def reply_media_group( - self, - media: List[Union["pyrogram.InputMediaPhoto", "pyrogram.InputMediaVideo"]], - quote: bool = None, - disable_notification: bool = None, - reply_to_message_id: int = None - ) -> "Message": - """Bound method *reply_media_group* of :obj:`Message`. - - Use as a shortcut for: - - .. code-block:: python - - client.send_media_group( - chat_id=message.chat.id, - media=list_of_media - ) - - Example: - .. code-block:: python - - message.reply_media_group(list_of_media) - - Parameters: - media (``list``): - A list containing either :obj:`InputMediaPhoto ` or - :obj:`InputMediaVideo ` objects - describing photos and videos to be sent, must include 2–10 items. - - quote (``bool``, *optional*): - If ``True``, the message will be sent as a reply to this message. - If *reply_to_message_id* is passed, this parameter will be ignored. - Defaults to ``True`` in group chats and ``False`` in private chats. - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - reply_to_message_id (``int``, *optional*): - If the message is a reply, ID of the original message. - - Returns: - On success, a :obj:`Messages` object is returned containing all the - single messages sent. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - if quote is None: - quote = self.chat.type != "private" - - if reply_to_message_id is None and quote: - reply_to_message_id = self.message_id - - return self._client.send_media_group( - chat_id=self.chat.id, - media=media, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id - ) - - def reply_photo( - self, - photo: str, - quote: bool = None, - caption: str = "", - parse_mode: str = "", - ttl_seconds: int = None, - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union[ - "pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply" - ] = None, - progress: callable = None, - progress_args: tuple = () - ) -> "Message": - """Bound method *reply_photo* of :obj:`Message`. - - Use as a shortcut for: - - .. code-block:: python - - client.send_photo( - chat_id=message.chat.id, - photo=photo - ) - - Example: - .. code-block:: python - - message.reply_photo(photo) - - Parameters: - photo (``str``): - Photo to send. - Pass a file_id as string to send a photo that exists on the Telegram servers, - pass an HTTP URL as a string for Telegram to get a photo from the Internet, or - pass a file path as string to upload a new photo that exists on your local machine. - - quote (``bool``, *optional*): - If ``True``, the message will be sent as a reply to this message. - If *reply_to_message_id* is passed, this parameter will be ignored. - Defaults to ``True`` in group chats and ``False`` in private chats. - - caption (``bool``, *optional*): - Photo caption, 0-1024 characters. - - parse_mode (``str``, *optional*): - Pass "markdown" or "html" if you want Telegram apps to show bold, italic, fixed-width text or inline - URLs in your caption. Defaults to "markdown". - - ttl_seconds (``int``, *optional*): - Self-Destruct Timer. - If you set a timer, the photo will self-destruct in *ttl_seconds* - seconds after it was viewed. - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - reply_to_message_id (``int``, *optional*): - If the message is a reply, ID of the original message. - - reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*): - Additional interface options. An object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. - - progress (``callable``, *optional*): - Pass a callback function to view the upload progress. - The function must take *(client, current, total, \*args)* as positional arguments (look at the section - below for a detailed description). - - progress_args (``tuple``, *optional*): - Extra custom arguments for the progress callback function. Useful, for example, if you want to pass - a chat_id and a message_id in order to edit a message with the updated progress. - - Other Parameters: - client (:obj:`Client`): - The Client itself, useful when you want to call other API methods inside the callback function. - - current (``int``): - The amount of bytes uploaded so far. - - total (``int``): - The size of the file. - - *args (``tuple``, *optional*): - Extra custom arguments as defined in the *progress_args* parameter. - You can either keep *\*args* or add every single extra argument in your function signature. - - Returns: - On success, the sent :obj:`Message` is returned. - In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - if quote is None: - quote = self.chat.type != "private" - - if reply_to_message_id is None and quote: - reply_to_message_id = self.message_id - - return self._client.send_photo( - chat_id=self.chat.id, - photo=photo, - caption=caption, - parse_mode=parse_mode, - ttl_seconds=ttl_seconds, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - progress=progress, - progress_args=progress_args - ) - - def reply_poll( - self, - question: str, - options: List[str], - quote: bool = None, - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union[ - "pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply" - ] = None - ) -> "Message": - """Bound method *reply_poll* of :obj:`Message`. - - Use as a shortcut for: - - .. code-block:: python - - client.send_poll( - chat_id=message.chat.id, - question="Is Pyrogram the best?", - options=["Yes", "Yes"] - ) - - Example: - .. code-block:: python - - message.reply_poll("Is Pyrogram the best?", ["Yes", "Yes"]) - - Parameters: - question (``str``): - The poll question, as string. - - options (List of ``str``): - The poll options, as list of strings (2 to 10 options are allowed). - - quote (``bool``, *optional*): - If ``True``, the message will be sent as a reply to this message. - If *reply_to_message_id* is passed, this parameter will be ignored. - Defaults to ``True`` in group chats and ``False`` in private chats. - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - reply_to_message_id (``int``, *optional*): - If the message is a reply, ID of the original message. - - reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*): - Additional interface options. An object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. - - Returns: - On success, the sent :obj:`Message` is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - if quote is None: - quote = self.chat.type != "private" - - if reply_to_message_id is None and quote: - reply_to_message_id = self.message_id - - return self._client.send_poll( - chat_id=self.chat.id, - question=question, - options=options, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup - ) - - def reply_sticker( - self, - sticker: str, - quote: bool = None, - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union[ - "pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply" - ] = None, - progress: callable = None, - progress_args: tuple = () - ) -> "Message": - """Bound method *reply_sticker* of :obj:`Message`. - - Use as a shortcut for: - - .. code-block:: python - - client.send_sticker( - chat_id=message.chat.id, - sticker=sticker - ) - - Example: - .. code-block:: python - - message.reply_sticker(sticker) - - Parameters: - sticker (``str``): - Sticker to send. - Pass a file_id as string to send a sticker that exists on the Telegram servers, - pass an HTTP URL as a string for Telegram to get a .webp sticker file from the Internet, or - pass a file path as string to upload a new sticker that exists on your local machine. - - quote (``bool``, *optional*): - If ``True``, the message will be sent as a reply to this message. - If *reply_to_message_id* is passed, this parameter will be ignored. - Defaults to ``True`` in group chats and ``False`` in private chats. - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - reply_to_message_id (``int``, *optional*): - If the message is a reply, ID of the original message. - - reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*): - Additional interface options. An object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. - - progress (``callable``, *optional*): - Pass a callback function to view the upload progress. - The function must take *(client, current, total, \*args)* as positional arguments (look at the section - below for a detailed description). - - progress_args (``tuple``, *optional*): - Extra custom arguments for the progress callback function. Useful, for example, if you want to pass - a chat_id and a message_id in order to edit a message with the updated progress. - - Other Parameters: - client (:obj:`Client`): - The Client itself, useful when you want to call other API methods inside the callback function. - - current (``int``): - The amount of bytes uploaded so far. - - total (``int``): - The size of the file. - - *args (``tuple``, *optional*): - Extra custom arguments as defined in the *progress_args* parameter. - You can either keep *\*args* or add every single extra argument in your function signature. - - Returns: - On success, the sent :obj:`Message` is returned. - In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - if quote is None: - quote = self.chat.type != "private" - - if reply_to_message_id is None and quote: - reply_to_message_id = self.message_id - - return self._client.send_sticker( - chat_id=self.chat.id, - sticker=sticker, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - progress=progress, - progress_args=progress_args - ) - - def reply_venue( - self, - latitude: float, - longitude: float, - title: str, - address: str, - quote: bool = None, - foursquare_id: str = "", - foursquare_type: str = "", - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union[ - "pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply" - ] = None - ) -> "Message": - """Bound method *reply_venue* of :obj:`Message`. - - Use as a shortcut for: - - .. code-block:: python - - client.send_venue( - chat_id=message.chat.id, - latitude=41.890251, - longitude=12.492373, - title="Coliseum", - address="Piazza del Colosseo, 1, 00184 Roma RM" - ) - - Example: - .. code-block:: python - - message.reply_venue(41.890251, 12.492373, "Coliseum", "Piazza del Colosseo, 1, 00184 Roma RM") - - Parameters: - latitude (``float``): - Latitude of the venue. - - longitude (``float``): - Longitude of the venue. - - title (``str``): - Name of the venue. - - address (``str``): - Address of the venue. - - quote (``bool``, *optional*): - If ``True``, the message will be sent as a reply to this message. - If *reply_to_message_id* is passed, this parameter will be ignored. - Defaults to ``True`` in group chats and ``False`` in private chats. - - foursquare_id (``str``, *optional*): - Foursquare identifier of the venue. - - foursquare_type (``str``, *optional*): - Foursquare type of the venue, if known. - (For example, "arts_entertainment/default", "arts_entertainment/aquarium" or "food/icecream".) - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - reply_to_message_id (``int``, *optional*): - If the message is a reply, ID of the original message - - reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*): - Additional interface options. An object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. - - Returns: - On success, the sent :obj:`Message` is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - if quote is None: - quote = self.chat.type != "private" - - if reply_to_message_id is None and quote: - reply_to_message_id = self.message_id - - return self._client.send_venue( - chat_id=self.chat.id, - latitude=latitude, - longitude=longitude, - title=title, - address=address, - foursquare_id=foursquare_id, - foursquare_type=foursquare_type, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup - ) - - def reply_video( - self, - video: str, - quote: bool = None, - caption: str = "", - parse_mode: str = "", - duration: int = 0, - width: int = 0, - height: int = 0, - thumb: str = None, - supports_streaming: bool = True, - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union[ - "pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply" - ] = None, - progress: callable = None, - progress_args: tuple = () - ) -> "Message": - """Bound method *reply_video* of :obj:`Message`. - - Use as a shortcut for: - - .. code-block:: python - - client.send_video( - chat_id=message.chat.id, - video=video - ) - - Example: - .. code-block:: python - - message.reply_video(video) - - Parameters: - video (``str``): - Video to send. - Pass a file_id as string to send a video that exists on the Telegram servers, - pass an HTTP URL as a string for Telegram to get a video from the Internet, or - pass a file path as string to upload a new video that exists on your local machine. - - quote (``bool``, *optional*): - If ``True``, the message will be sent as a reply to this message. - If *reply_to_message_id* is passed, this parameter will be ignored. - Defaults to ``True`` in group chats and ``False`` in private chats. - - caption (``str``, *optional*): - Video caption, 0-1024 characters. - - parse_mode (``str``, *optional*): - Pass "markdown" or "html" if you want Telegram apps to show bold, italic, fixed-width text or inline - URLs in your caption. Defaults to "markdown". - - duration (``int``, *optional*): - Duration of sent video in seconds. - - width (``int``, *optional*): - Video width. - - height (``int``, *optional*): - Video height. - - thumb (``str``, *optional*): - Thumbnail of the video sent. - The thumbnail should be in JPEG format and less than 200 KB in size. - A thumbnail's width and height should not exceed 320 pixels. - Thumbnails can't be reused and can be only uploaded as a new file. - - supports_streaming (``bool``, *optional*): - Pass True, if the uploaded video is suitable for streaming. - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - reply_to_message_id (``int``, *optional*): - If the message is a reply, ID of the original message. - - reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*): - Additional interface options. An object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. - - progress (``callable``, *optional*): - Pass a callback function to view the upload progress. - The function must take *(client, current, total, \*args)* as positional arguments (look at the section - below for a detailed description). - - progress_args (``tuple``, *optional*): - Extra custom arguments for the progress callback function. Useful, for example, if you want to pass - a chat_id and a message_id in order to edit a message with the updated progress. - - Other Parameters: - client (:obj:`Client`): - The Client itself, useful when you want to call other API methods inside the callback function. - - current (``int``): - The amount of bytes uploaded so far. - - total (``int``): - The size of the file. - - *args (``tuple``, *optional*): - Extra custom arguments as defined in the *progress_args* parameter. - You can either keep *\*args* or add every single extra argument in your function signature. - - Returns: - On success, the sent :obj:`Message` is returned. - In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - if quote is None: - quote = self.chat.type != "private" - - if reply_to_message_id is None and quote: - reply_to_message_id = self.message_id - - return self._client.send_video( - chat_id=self.chat.id, - video=video, - caption=caption, - parse_mode=parse_mode, - duration=duration, - width=width, - height=height, - thumb=thumb, - supports_streaming=supports_streaming, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - progress=progress, - progress_args=progress_args - ) - - def reply_video_note( - self, - video_note: str, - quote: bool = None, - duration: int = 0, - length: int = 1, - thumb: str = None, - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union[ - "pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply" - ] = None, - progress: callable = None, - progress_args: tuple = () - ) -> "Message": - """Bound method *reply_video_note* of :obj:`Message`. - - Use as a shortcut for: - - .. code-block:: python - - client.send_video_note( - chat_id=message.chat.id, - video_note=video_note - ) - - Example: - .. code-block:: python - - message.reply_video_note(video_note) - - Parameters: - video_note (``str``): - Video note to send. - Pass a file_id as string to send a video note that exists on the Telegram servers, or - pass a file path as string to upload a new video note that exists on your local machine. - Sending video notes by a URL is currently unsupported. - - quote (``bool``, *optional*): - If ``True``, the message will be sent as a reply to this message. - If *reply_to_message_id* is passed, this parameter will be ignored. - Defaults to ``True`` in group chats and ``False`` in private chats. - - duration (``int``, *optional*): - Duration of sent video in seconds. - - length (``int``, *optional*): - Video width and height. - - thumb (``str``, *optional*): - Thumbnail of the video sent. - The thumbnail should be in JPEG format and less than 200 KB in size. - A thumbnail's width and height should not exceed 320 pixels. - Thumbnails can't be reused and can be only uploaded as a new file. - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - reply_to_message_id (``int``, *optional*): - If the message is a reply, ID of the original message - - reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*): - Additional interface options. An object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. - - progress (``callable``, *optional*): - Pass a callback function to view the upload progress. - The function must take *(client, current, total, \*args)* as positional arguments (look at the section - below for a detailed description). - - progress_args (``tuple``, *optional*): - Extra custom arguments for the progress callback function. Useful, for example, if you want to pass - a chat_id and a message_id in order to edit a message with the updated progress. - - Other Parameters: - client (:obj:`Client`): - The Client itself, useful when you want to call other API methods inside the callback function. - - current (``int``): - The amount of bytes uploaded so far. - - total (``int``): - The size of the file. - - *args (``tuple``, *optional*): - Extra custom arguments as defined in the *progress_args* parameter. - You can either keep *\*args* or add every single extra argument in your function signature. - - Returns: - On success, the sent :obj:`Message` is returned. - In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - if quote is None: - quote = self.chat.type != "private" - - if reply_to_message_id is None and quote: - reply_to_message_id = self.message_id - - return self._client.send_video_note( - chat_id=self.chat.id, - video_note=video_note, - duration=duration, - length=length, - thumb=thumb, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - progress=progress, - progress_args=progress_args - ) - - def reply_voice( - self, - voice: str, - quote: bool = None, - caption: str = "", - parse_mode: str = "", - duration: int = 0, - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union[ - "pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply" - ] = None, - progress: callable = None, - progress_args: tuple = () - ) -> "Message": - """Bound method *reply_voice* of :obj:`Message`. - - Use as a shortcut for: - - .. code-block:: python - - client.send_voice( - chat_id=message.chat.id, - voice=voice - ) - - Example: - .. code-block:: python - - message.reply_voice(voice) - - Parameters: - voice (``str``): - Audio file to send. - Pass a file_id as string to send an audio that exists on the Telegram servers, - pass an HTTP URL as a string for Telegram to get an audio from the Internet, or - pass a file path as string to upload a new audio that exists on your local machine. - - quote (``bool``, *optional*): - If ``True``, the message will be sent as a reply to this message. - If *reply_to_message_id* is passed, this parameter will be ignored. - Defaults to ``True`` in group chats and ``False`` in private chats. - - caption (``str``, *optional*): - Voice message caption, 0-1024 characters. - - parse_mode (``str``, *optional*): - Pass "markdown" or "html" if you want Telegram apps to show bold, italic, fixed-width text or inline - URLs in your caption. Defaults to "markdown". - - duration (``int``, *optional*): - Duration of the voice message in seconds. - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - reply_to_message_id (``int``, *optional*): - If the message is a reply, ID of the original message - - reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*): - Additional interface options. An object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. - - progress (``callable``, *optional*): - Pass a callback function to view the upload progress. - The function must take *(client, current, total, \*args)* as positional arguments (look at the section - below for a detailed description). - - progress_args (``tuple``, *optional*): - Extra custom arguments for the progress callback function. Useful, for example, if you want to pass - a chat_id and a message_id in order to edit a message with the updated progress. - - Other Parameters: - client (:obj:`Client`): - The Client itself, useful when you want to call other API methods inside the callback function. - - current (``int``): - The amount of bytes uploaded so far. - - total (``int``): - The size of the file. - - *args (``tuple``, *optional*): - Extra custom arguments as defined in the *progress_args* parameter. - You can either keep *\*args* or add every single extra argument in your function signature. - - Returns: - On success, the sent :obj:`Message` is returned. - In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - if quote is None: - quote = self.chat.type != "private" - - if reply_to_message_id is None and quote: - reply_to_message_id = self.message_id - - return self._client.send_voice( - chat_id=self.chat.id, - voice=voice, - caption=caption, - parse_mode=parse_mode, - duration=duration, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - progress=progress, - progress_args=progress_args - ) - - def edit( - self, - text: str, - parse_mode: str = "", - disable_web_page_preview: bool = None, - reply_markup: Union[ - "pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply" - ] = None - ) -> "Message": - """Bound method *edit* of :obj:`Message` - - Use as a shortcut for: - - .. code-block:: python - - client.edit_message_text( - chat_id=message.chat.id, - message_id=message.message_id, - text="hello" - ) - - Example: - .. code-block:: python - - message.edit("hello") - - Parameters: - text (``str``): - New text of the message. - - parse_mode (``str``, *optional*): - Pass "markdown" or "html" if you want Telegram apps to show bold, italic, fixed-width text or inline - URLs in your message. Defaults to "markdown". - - disable_web_page_preview (``bool``, *optional*): - Disables link previews for links in this message. - - reply_markup (:obj:`InlineKeyboardMarkup`, *optional*): - An InlineKeyboardMarkup object. - - Returns: - On success, the edited :obj:`Message` is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - return self._client.edit_message_text( - chat_id=self.chat.id, - message_id=self.message_id, - text=text, - parse_mode=parse_mode, - disable_web_page_preview=disable_web_page_preview, - reply_markup=reply_markup - ) - - def edit_caption( - self, - caption: str, - parse_mode: str = "", - reply_markup: Union[ - "pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply" - ] = None - ) -> "Message": - """Bound method *edit_caption* of :obj:`Message` - - Use as a shortcut for: - - .. code-block:: python - - client.edit_message_caption( - chat_id=message.chat.id, - message_id=message.message_id, - caption="hello" - ) - - Example: - .. code-block:: python - - message.edit_caption("hello") - - Parameters: - caption (``str``): - New caption of the message. - - parse_mode (``str``, *optional*): - Pass "markdown" or "html" if you want Telegram apps to show bold, italic, fixed-width text or inline - URLs in your message. Defaults to "markdown". - - reply_markup (:obj:`InlineKeyboardMarkup`, *optional*): - An InlineKeyboardMarkup object. - - Returns: - On success, the edited :obj:`Message` is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - return self._client.edit_message_caption( - chat_id=self.chat.id, - message_id=self.message_id, - caption=caption, - parse_mode=parse_mode, - reply_markup=reply_markup - ) - - def edit_media(self, media: InputMedia, reply_markup: "pyrogram.InlineKeyboardMarkup" = None) -> "Message": - """Bound method *edit_media* of :obj:`Message` - - Use as a shortcut for: - - .. code-block:: python - - client.edit_message_media( - chat_id=message.chat.id, - message_id=message.message_id, - media=media - ) - - Example: - .. code-block:: python - - message.edit_media(media) - - Parameters: - media (:obj:`InputMediaAnimation` | :obj:`InputMediaAudio` | :obj:`InputMediaDocument` | :obj:`InputMediaPhoto` | :obj:`InputMediaVideo`) - One of the InputMedia objects describing an animation, audio, document, photo or video. - - reply_markup (:obj:`InlineKeyboardMarkup`, *optional*): - An InlineKeyboardMarkup object. - - Returns: - On success, the edited :obj:`Message` is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - return self._client.edit_message_media( - chat_id=self.chat.id, - message_id=self.message_id, - media=media, - reply_markup=reply_markup - ) - - def edit_reply_markup(self, reply_markup: "pyrogram.InlineKeyboardMarkup" = None) -> "Message": - """Bound method *edit_reply_markup* of :obj:`Message` - - Use as a shortcut for: - - .. code-block:: python - - client.edit_message_reply_markup( - chat_id=message.chat.id, - message_id=message.message_id, - reply_markup=inline_reply_markup - ) - - Example: - .. code-block:: python - - message.edit_reply_markup(inline_reply_markup) - - Parameters: - reply_markup (:obj:`InlineKeyboardMarkup`): - An InlineKeyboardMarkup object. - - Returns: - On success, if edited message is sent by the bot, the edited - :obj:`Message` is returned, otherwise True is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - return self._client.edit_message_reply_markup( - chat_id=self.chat.id, - message_id=self.message_id, - reply_markup=reply_markup - ) - - def forward( - self, - chat_id: int or str, - disable_notification: bool = None, - as_copy: bool = False, - remove_caption: bool = False - ) -> "Message": - """Bound method *forward* of :obj:`Message`. - - Use as a shortcut for: - - .. code-block:: python - - client.forward_messages( - chat_id=chat_id, - from_chat_id=message.chat.id, - message_ids=message.message_id - ) - - Example: - .. code-block:: python - - message.forward(chat_id) - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - disable_notification (``bool``, *optional*): - Sends the message silently. - Users will receive a notification with no sound. - - as_copy (``bool``, *optional*): - Pass True to forward messages without the forward header (i.e.: send a copy of the message content). - Defaults to False. - - remove_caption (``bool``, *optional*): - If set to True and *as_copy* is enabled as well, media captions are not preserved when copying the - message. Has no effect if *as_copy* is not enabled. - Defaults to False. - - Returns: - On success, the forwarded Message is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - if as_copy: - if self.service: - raise ValueError("Unable to copy service messages") - - if self.game and not self._client.is_bot: - raise ValueError("Users cannot send messages with Game media type") - - # TODO: Improve markdown parser. Currently html appears to be more stable, thus we use it here because users - # can"t choose. - - if self.text: - return self._client.send_message( - chat_id, - text=self.text.html, - parse_mode="html", - disable_web_page_preview=not self.web_page, - disable_notification=disable_notification - ) - elif self.media: - caption = self.caption.html if self.caption and not remove_caption else None - - send_media = partial( - self._client.send_cached_media, - chat_id=chat_id, - disable_notification=disable_notification - ) - - if self.photo: - file_id = self.photo.sizes[-1].file_id - elif self.audio: - file_id = self.audio.file_id - elif self.document: - file_id = self.document.file_id - elif self.video: - file_id = self.video.file_id - elif self.animation: - file_id = self.animation.file_id - elif self.voice: - file_id = self.voice.file_id - elif self.sticker: - file_id = self.sticker.file_id - elif self.video_note: - file_id = self.video_note.file_id - elif self.contact: - return self._client.send_contact( - chat_id, - phone_number=self.contact.phone_number, - first_name=self.contact.first_name, - last_name=self.contact.last_name, - vcard=self.contact.vcard, - disable_notification=disable_notification - ) - elif self.location: - return self._client.send_location( - chat_id, - latitude=self.location.latitude, - longitude=self.location.longitude, - disable_notification=disable_notification - ) - elif self.venue: - return self._client.send_venue( - chat_id, - latitude=self.venue.location.latitude, - longitude=self.venue.location.longitude, - title=self.venue.title, - address=self.venue.address, - foursquare_id=self.venue.foursquare_id, - foursquare_type=self.venue.foursquare_type, - disable_notification=disable_notification - ) - elif self.poll: - return self._client.send_poll( - chat_id, - question=self.poll.question, - options=[opt.text for opt in self.poll.options], - disable_notification=disable_notification - ) - elif self.game: - return self._client.send_game( - chat_id, - game_short_name=self.game.short_name, - disable_notification=disable_notification - ) - else: - raise ValueError("Unknown media type") - - if self.sticker or self.video_note: # Sticker and VideoNote should have no caption - return send_media(file_id=file_id) - else: - return send_media(file_id=file_id, caption=caption, parse_mode="html") - else: - raise ValueError("Can't copy this message") - else: - return self._client.forward_messages( - chat_id=chat_id, - from_chat_id=self.chat.id, - message_ids=self.message_id, - disable_notification=disable_notification - ) - - def delete(self, revoke: bool = True): - """Bound method *delete* of :obj:`Message`. - - Use as a shortcut for: - - .. code-block:: python - - client.delete_messages( - chat_id=chat_id, - message_ids=message.message_id - ) - - Example: - .. code-block:: python - - message.delete() - - Parameters: - revoke (``bool``, *optional*): - Deletes messages on both parts. - This is only for private cloud chats and normal groups, messages on - channels and supergroups are always revoked (i.e.: deleted for everyone). - Defaults to True. - - Returns: - True on success, False otherwise. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - return self._client.delete_messages( - chat_id=self.chat.id, - message_ids=self.message_id, - revoke=revoke - ) - - def click(self, x: int or str, y: int = None, quote: bool = None, timeout: int = 10): - """Bound method *click* of :obj:`Message`. - - Use as a shortcut for clicking a button attached to the message instead of: - - - Clicking inline buttons: - - .. code-block:: python - - client.request_callback_answer( - chat_id=message.chat.id, - message_id=message.message_id, - callback_data=message.reply_markup[i][j].callback_data - ) - - - Clicking normal buttons: - - .. code-block:: python - - client.send_message( - chat_id=message.chat.id, - text=message.reply_markup[i][j].text - ) - - Example: - This method can be used in three different ways: - - 1. Pass one integer argument only (e.g.: ``.click(2)``, to click a button at index 2). - Buttons are counted left to right, starting from the top. - - 2. Pass two integer arguments (e.g.: ``.click(1, 0)``, to click a button at position (1, 0)). - The origin (0, 0) is top-left. - - 3. Pass one string argument only (e.g.: ``.click("Settings")``, to click a button by using its label). - Only the first matching button will be pressed. - - Parameters: - x (``int`` | ``str``): - Used as integer index, integer abscissa (in pair with y) or as string label. - - y (``int``, *optional*): - Used as ordinate only (in pair with x). - - quote (``bool``, *optional*): - Useful for normal buttons only, where pressing it will result in a new message sent. - If ``True``, the message will be sent as a reply to this message. - Defaults to ``True`` in group chats and ``False`` in private chats. - - timeout (``int``, *optional*): - Timeout in seconds. - - Returns: - - The result of :meth:`request_callback_answer() ` in case of - inline callback button clicks. - - The result of :meth:`reply() ` in case of normal button clicks. - - A string in case the inline button is a URL, a *switch_inline_query* or a - *switch_inline_query_current_chat* button. - - Raises: - RPCError: In case of a Telegram RPC error. - ValueError: In case the provided index or position is out of range or the button label was not found. - TimeoutError: In case, after clicking an inline button, the bot fails to answer within the timeout. - """ - - if isinstance(self.reply_markup, pyrogram.ReplyKeyboardMarkup): - keyboard = self.reply_markup.keyboard - is_inline = False - elif isinstance(self.reply_markup, pyrogram.InlineKeyboardMarkup): - keyboard = self.reply_markup.inline_keyboard - is_inline = True - else: - raise ValueError("The message doesn't contain any keyboard") - - if isinstance(x, int) and y is None: - try: - button = [ - button - for row in keyboard - for button in row - ][x] - except IndexError: - raise ValueError("The button at index {} doesn't exist".format(x)) - elif isinstance(x, int) and isinstance(y, int): - try: - button = keyboard[y][x] - except IndexError: - raise ValueError("The button at position ({}, {}) doesn't exist".format(x, y)) - elif isinstance(x, str) and y is None: - label = x.encode("utf-16", "surrogatepass").decode("utf-16") - - try: - button = [ - button - for row in keyboard - for button in row - if label == button.text - ][0] - except IndexError: - raise ValueError("The button with label '{}' doesn't exists".format(x)) - else: - raise ValueError("Invalid arguments") - - if is_inline: - if button.callback_data: - return self._client.request_callback_answer( - chat_id=self.chat.id, - message_id=self.message_id, - callback_data=button.callback_data, - timeout=timeout - ) - elif button.url: - return button.url - elif button.switch_inline_query: - return button.switch_inline_query - elif button.switch_inline_query_current_chat: - return button.switch_inline_query_current_chat - else: - raise ValueError("This button is not supported yet") - else: - self.reply(button, quote=quote) - - def download( - self, - file_name: str = "", - block: bool = True, - progress: callable = None, - progress_args: tuple = () - ) -> "Message": - """Bound method *download* of :obj:`Message`. - - Use as a shortcut for: - - .. code-block:: python - - client.download_media(message) - - Example: - .. code-block:: python - - message.download() - - Parameters: - file_name (``str``, *optional*): - A custom *file_name* to be used instead of the one provided by Telegram. - By default, all files are downloaded in the *downloads* folder in your working directory. - You can also specify a path for downloading files in a custom location: paths that end with "/" - are considered directories. All non-existent folders will be created automatically. - - block (``bool``, *optional*): - Blocks the code execution until the file has been downloaded. - Defaults to True. - - progress (``callable``): - Pass a callback function to view the download progress. - The function must take *(client, current, total, \*args)* as positional arguments (look at the section - below for a detailed description). - - progress_args (``tuple``): - Extra custom arguments for the progress callback function. Useful, for example, if you want to pass - a chat_id and a message_id in order to edit a message with the updated progress. - - Returns: - On success, the absolute path of the downloaded file as string is returned, None otherwise. - - Raises: - RPCError: In case of a Telegram RPC error. - ``ValueError``: If the message doesn't contain any downloadable media - """ - return self._client.download_media( - message=self, - file_name=file_name, - block=block, - progress=progress, - progress_args=progress_args, - ) - - def pin(self, disable_notification: bool = None) -> "Message": - """Bound method *pin* of :obj:`Message`. - - Use as a shortcut for: - - .. code-block:: python - - client.pin_chat_message( - chat_id=message.chat.id, - message_id=message_id - ) - - Example: - .. code-block:: python - - message.pin() - - Parameters: - disable_notification (``bool``): - Pass True, if it is not necessary to send a notification to all chat members about the new pinned - message. Notifications are always disabled in channels. - - Returns: - True on success. - - Raises: - :class:`RPCError ` - """ - return self._client.pin_chat_message( - chat_id=self.chat.id, - message_id=self.message_id, - disable_notification=disable_notification - ) diff --git a/pyrogram/client/types/messages_and_media/message_entity.py b/pyrogram/client/types/messages_and_media/message_entity.py deleted file mode 100644 index 6fe4ef9189e..00000000000 --- a/pyrogram/client/types/messages_and_media/message_entity.py +++ /dev/null @@ -1,100 +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 . - -import pyrogram - -from pyrogram.api import types -from ..pyrogram_type import PyrogramType -from ..user_and_chats.user import User - - -class MessageEntity(PyrogramType): - """This object represents one special entity in a text message. - For example, hashtags, usernames, URLs, etc. - - Parameters: - type (``str``): - Type of the entity. - Can be "mention" (@username), "hashtag", "cashtag", "bot_command", "url", "email", "phone_number", "bold" - (bold text), italic (italic text), "code" (monowidth string), "pre" (monowidth block), "text_link" - (for clickable text URLs), "text_mention" (for users without usernames). - - offset (``int``): - Offset in UTF-16 code units to the start of the entity. - - length (``int``): - Length of the entity in UTF-16 code units. - - url (``str``, *optional*): - For "text_link" only, url that will be opened after user taps on the text. - - user (:obj:`User`, *optional*): - For "text_mention" only, the mentioned user. - """ - - __slots__ = ["type", "offset", "length", "url", "user"] - - ENTITIES = { - types.MessageEntityMention.ID: "mention", - types.MessageEntityHashtag.ID: "hashtag", - types.MessageEntityCashtag.ID: "cashtag", - types.MessageEntityBotCommand.ID: "bot_command", - types.MessageEntityUrl.ID: "url", - types.MessageEntityEmail.ID: "email", - types.MessageEntityBold.ID: "bold", - types.MessageEntityItalic.ID: "italic", - types.MessageEntityCode.ID: "code", - types.MessageEntityPre.ID: "pre", - types.MessageEntityTextUrl.ID: "text_link", - types.MessageEntityMentionName.ID: "text_mention", - types.MessageEntityPhone.ID: "phone_number" - } - - def __init__( - self, - *, - client: "pyrogram.client.ext.BaseClient", - type: str, - offset: int, - length: int, - url: str = None, - user: User = None - ): - super().__init__(client) - - self.type = type - self.offset = offset - self.length = length - self.url = url - self.user = user - - @staticmethod - def _parse(client, entity, users: dict) -> "MessageEntity" or None: - type = MessageEntity.ENTITIES.get(entity.ID, None) - - if type is None: - return None - - return MessageEntity( - type=type, - offset=entity.offset, - length=entity.length, - url=getattr(entity, "url", None), - user=User._parse(client, users.get(getattr(entity, "user_id", None), None)), - client=client - ) diff --git a/pyrogram/client/types/messages_and_media/messages.py b/pyrogram/client/types/messages_and_media/messages.py deleted file mode 100644 index 4ce41aecf61..00000000000 --- a/pyrogram/client/types/messages_and_media/messages.py +++ /dev/null @@ -1,170 +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 typing import List, Union - -import pyrogram -from pyrogram.api import types -from .message import Message -from ..pyrogram_type import PyrogramType -from ..update import Update -from ..user_and_chats import Chat - - -class Messages(PyrogramType, Update): - """This object represents a chat's messages. - - Parameters: - total_count (``int``): - Total number of messages the target chat has. - - messages (List of :obj:`Message`): - Requested messages. - """ - - __slots__ = ["total_count", "messages"] - - def __init__( - self, - *, - client: "pyrogram.client.ext.BaseClient", - total_count: int, - messages: List[Message] - ): - super().__init__(client) - - self.total_count = total_count - self.messages = messages - - @staticmethod - def _parse(client, messages: types.messages.Messages, replies: int = 1) -> "Messages": - users = {i.id: i for i in messages.users} - chats = {i.id: i for i in messages.chats} - - total_count = getattr(messages, "count", len(messages.messages)) - - if not messages.messages: - return Messages( - total_count=total_count, - messages=[], - client=client - ) - - parsed_messages = [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 - ).messages - - 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 Messages( - total_count=total_count, - messages=parsed_messages, - client=client - ) - - @staticmethod - def _parse_deleted(client, update) -> "Messages": - messages = update.messages - channel_id = getattr(update, "channel_id", None) - - parsed_messages = [] - - for message in messages: - parsed_messages.append( - Message( - message_id=message, - chat=Chat( - id=int("-100" + str(channel_id)), - type="channel", - client=client - ) if channel_id is not None else None, - client=client - ) - ) - - return Messages( - total_count=len(parsed_messages), - messages=parsed_messages, - client=client - ) - - def forward( - self, - chat_id: Union[int, str], - disable_notification: bool = None, - as_copy: bool = False, - remove_caption: bool = False - ): - """Bound method *forward* of :obj:`Message`. - - Parameters: - chat_id (``int`` | ``str``): - Unique identifier (int) or username (str) of the target chat. - For your personal cloud (Saved Messages) you can simply use "me" or "self". - For a contact that exists in your Telegram address book you can use his phone number (str). - - disable_notification (``bool``, *optional*): - Sends messages silently. - Users will receive a notification with no sound. - - as_copy (``bool``, *optional*): - Pass True to forward messages without the forward header (i.e.: send a copy of the message content). - Defaults to False. - - remove_caption (``bool``, *optional*): - If set to True and *as_copy* is enabled as well, media captions are not preserved when copying the - message. Has no effect if *as_copy* is not enabled. - Defaults to False. - - Returns: - On success, a :class:`Messages` containing forwarded messages is returned. - - Raises: - RPCError: In case of a Telegram RPC error. - """ - forwarded_messages = [] - - for message in self.messages: - forwarded_messages.append( - message.forward( - chat_id=chat_id, - as_copy=as_copy, - disable_notification=disable_notification, - remove_caption=remove_caption - ) - ) - - return Messages( - total_count=len(forwarded_messages), - messages=forwarded_messages, - client=self._client - ) diff --git a/pyrogram/client/types/messages_and_media/photo.py b/pyrogram/client/types/messages_and_media/photo.py deleted file mode 100644 index f7f9f4ec0b3..00000000000 --- a/pyrogram/client/types/messages_and_media/photo.py +++ /dev/null @@ -1,104 +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 base64 import b64encode -from struct import pack -from typing import List - -import pyrogram -from pyrogram.api import types -from .photo_size import PhotoSize -from ..pyrogram_type import PyrogramType -from ...ext.utils import encode - - -class Photo(PyrogramType): - """This object represents a Photo. - - Parameters: - id (``str``): - Unique identifier for this photo. - - date (``int``): - Date the photo was sent in Unix time. - - sizes (List of :obj:`PhotoSize`): - Available sizes of this photo. - """ - - __slots__ = ["id", "date", "sizes"] - - def __init__( - self, - *, - client: "pyrogram.client.ext.BaseClient", - id: str, - date: int, - sizes: List[PhotoSize] - ): - super().__init__(client) - - self.id = id - self.date = date - self.sizes = sizes - - @staticmethod - def _parse(client, photo: types.Photo): - if isinstance(photo, types.Photo): - raw_sizes = photo.sizes - sizes = [] - - for raw_size in raw_sizes: - if isinstance(raw_size, (types.PhotoSize, types.PhotoCachedSize)): - if isinstance(raw_size, types.PhotoSize): - file_size = raw_size.size - elif isinstance(raw_size, types.PhotoCachedSize): - file_size = len(raw_size.bytes) - else: - file_size = 0 - - loc = raw_size.location - - if isinstance(loc, types.FileLocation): - size = PhotoSize( - file_id=encode( - pack( - " Union["PhotoSize", None]: - if not thumbs: - return None - - photo_size = thumbs[-1] - - if not isinstance(photo_size, (types.PhotoSize, types.PhotoCachedSize, types.PhotoStrippedSize)): - return None - - loc = photo_size.location - - if not isinstance(loc, types.FileLocation): - return None - - return PhotoSize( - file_id=encode( - pack( - " -# -# 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 typing import List, Union - -import pyrogram -from pyrogram.api import types -from .poll_option import PollOption -from ..pyrogram_type import PyrogramType -from ..update import Update - - -class Poll(PyrogramType, Update): - """This object represents a Poll. - - Parameters: - id (``str``): - Unique poll identifier. - - question (``str``): - Poll question, 1-255 characters. - - options (List of :obj:`PollOption`): - List of poll options. - - is_closed (``bool``): - True, if the poll is closed. - - total_voters (``int``): - Total count of voters for this poll. - - chosen_option (``int``, *optional*): - Index of your chosen option (0-9), None in case you haven't voted yet. - """ - - __slots__ = ["id", "question", "options", "is_closed", "total_voters", "chosen_option"] - - def __init__( - self, - *, - client: "pyrogram.client.ext.BaseClient", - id: str, - question: str, - options: List[PollOption], - is_closed: bool, - total_voters: int, - chosen_option: int = None - ): - super().__init__(client) - - self.id = id - self.question = question - self.options = options - self.is_closed = is_closed - self.total_voters = total_voters - self.chosen_option = chosen_option - - @staticmethod - def _parse(client, media_poll: Union[types.MessageMediaPoll, types.UpdateMessagePoll]) -> "Poll": - poll = media_poll.poll - results = media_poll.results.results - total_voters = media_poll.results.total_voters - chosen_option = None - options = [] - - for i, answer in enumerate(poll.answers): - voter_count = 0 - - if results: - result = results[i] - voter_count = result.voters - - if result.chosen: - chosen_option = i - - options.append( - PollOption( - text=answer.text, - voter_count=voter_count, - data=answer.option, - client=client - ) - ) - - return Poll( - id=str(poll.id), - question=poll.question, - options=options, - is_closed=poll.closed, - total_voters=total_voters, - chosen_option=chosen_option, - client=client - ) - - @staticmethod - def _parse_update(client, update: types.UpdateMessagePoll): - if update.poll is not None: - return Poll._parse(client, update) - - results = update.results.results - chosen_option = None - options = [] - - for i, result in enumerate(results): - if result.chosen: - chosen_option = i - - options.append( - PollOption( - text="", - voter_count=result.voters, - data=result.option, - client=client - ) - ) - - return Poll( - id=str(update.poll_id), - question="", - options=options, - is_closed=False, - total_voters=update.results.total_voters, - chosen_option=chosen_option, - client=client - ) diff --git a/pyrogram/client/types/messages_and_media/poll_option.py b/pyrogram/client/types/messages_and_media/poll_option.py deleted file mode 100644 index 31eed702f85..00000000000 --- a/pyrogram/client/types/messages_and_media/poll_option.py +++ /dev/null @@ -1,49 +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 . - -import pyrogram -from ..pyrogram_type import PyrogramType - - -class PollOption(PyrogramType): - """This object contains information about one answer option in a poll. - - Parameters: - text (``str``): - Option text, 1-100 characters. - - voter_count (``int``): - Number of users that voted for this option. - Equals to 0 until you vote. - """ - - __slots__ = ["text", "voter_count", "_data"] - - def __init__( - self, - *, - client: "pyrogram.client.ext.BaseClient", - text: str, - voter_count: int, - data: bytes - ): - super().__init__(client) - - self.text = text - self.voter_count = voter_count - self._data = data # Hidden diff --git a/pyrogram/client/types/messages_and_media/sticker.py b/pyrogram/client/types/messages_and_media/sticker.py deleted file mode 100644 index e6b8de35ec4..00000000000 --- a/pyrogram/client/types/messages_and_media/sticker.py +++ /dev/null @@ -1,147 +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 functools import lru_cache -from struct import pack - -import pyrogram -from pyrogram.api import types, functions -from pyrogram.errors import StickersetInvalid -from .photo_size import PhotoSize -from ..pyrogram_type import PyrogramType -from ...ext.utils import encode - - -class Sticker(PyrogramType): - """This object represents a sticker. - - Parameters: - file_id (``str``): - Unique identifier for this file. - - width (``int``): - Sticker width. - - height (``int``): - Sticker height. - - thumb (:obj:`PhotoSize`, *optional*): - Sticker thumbnail in the .webp or .jpg format. - - file_name (``str``, *optional*): - Sticker file name. - - mime_type (``str``, *optional*): - MIME type of the file as defined by sender. - - file_size (``int``, *optional*): - File size. - - date (``int``, *optional*): - Date the sticker was sent in Unix time. - - emoji (``str``, *optional*): - Emoji associated with the sticker. - - set_name (``str``, *optional*): - Name of the sticker set to which the sticker belongs. - """ - - # TODO: Add mask position - - __slots__ = [ - "file_id", "thumb", "file_name", "mime_type", "file_size", "date", "width", "height", "emoji", "set_name" - ] - - def __init__( - self, - *, - client: "pyrogram.client.ext.BaseClient", - file_id: str, - width: int, - height: int, - thumb: PhotoSize = None, - file_name: str = None, - mime_type: str = None, - file_size: int = None, - date: int = None, - emoji: str = None, - set_name: str = None - ): - super().__init__(client) - - self.file_id = file_id - self.thumb = thumb - self.file_name = file_name - self.mime_type = mime_type - self.file_size = file_size - self.date = date - self.width = width - self.height = height - self.emoji = emoji - self.set_name = set_name - # self.mask_position = mask_position - - @staticmethod - @lru_cache(maxsize=256) - def _get_sticker_set_name(send, input_sticker_set_id): - try: - return send( - functions.messages.GetStickerSet( - stickerset=types.InputStickerSetID( - id=input_sticker_set_id[0], - access_hash=input_sticker_set_id[1] - ) - ) - ).set.short_name - except StickersetInvalid: - return None - - @staticmethod - def _parse(client, sticker: types.Document, image_size_attributes: types.DocumentAttributeImageSize, - sticker_attributes: types.DocumentAttributeSticker, file_name: str) -> "Sticker": - sticker_set = sticker_attributes.stickerset - - if isinstance(sticker_set, types.InputStickerSetID): - input_sticker_set_id = (sticker_set.id, sticker_set.access_hash) - set_name = Sticker._get_sticker_set_name(client.send, input_sticker_set_id) - else: - set_name = None - - return Sticker( - file_id=encode( - pack( - " -# -# 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 typing import List - -import pyrogram -from .photo import Photo -from ..pyrogram_type import PyrogramType - - -class UserProfilePhotos(PyrogramType): - """This object represents a user's profile pictures. - - Parameters: - total_count (``int``): - Total number of profile pictures the target user has. - - photos (List of :obj:`Photo`): - Requested profile pictures. - """ - - __slots__ = ["total_count", "photos"] - - def __init__( - self, - *, - client: "pyrogram.client.ext.BaseClient", - total_count: int, - photos: List[Photo] - ): - super().__init__(client) - - self.total_count = total_count - self.photos = photos - - @staticmethod - def _parse(client, photos) -> "UserProfilePhotos": - return UserProfilePhotos( - total_count=getattr(photos, "count", len(photos.photos)), - photos=[Photo._parse(client, photo) for photo in photos.photos], - client=client - ) diff --git a/pyrogram/client/types/messages_and_media/venue.py b/pyrogram/client/types/messages_and_media/venue.py deleted file mode 100644 index 61e3d91235b..00000000000 --- a/pyrogram/client/types/messages_and_media/venue.py +++ /dev/null @@ -1,76 +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 . - -import pyrogram -from pyrogram.api import types -from .location import Location -from ..pyrogram_type import PyrogramType - - -class Venue(PyrogramType): - """This object represents a venue. - - Parameters: - location (:obj:`Location`): - Venue location. - - title (``str``): - Name of the venue. - - address (``str``): - Address of the venue. - - foursquare_id (``str``, *optional*): - Foursquare identifier of the venue. - - foursquare_type (``str``, *optional*): - Foursquare type of the venue. - (For example, "arts_entertainment/default", "arts_entertainment/aquarium" or "food/icecream".) - - """ - - __slots__ = ["location", "title", "address", "foursquare_id", "foursquare_type"] - - def __init__( - self, - *, - client: "pyrogram.client.ext.BaseClient", - location: Location, - title: str, - address: str, - foursquare_id: str = None, - foursquare_type: str = None - ): - super().__init__(client) - - self.location = location - self.title = title - self.address = address - self.foursquare_id = foursquare_id - self.foursquare_type = foursquare_type - - @staticmethod - def _parse(client, venue: types.MessageMediaVenue): - return Venue( - location=Location._parse(client, venue.geo), - title=venue.title, - address=venue.address, - foursquare_id=venue.venue_id or None, - foursquare_type=venue.venue_type, - client=client - ) diff --git a/pyrogram/client/types/messages_and_media/video.py b/pyrogram/client/types/messages_and_media/video.py deleted file mode 100644 index 8c3e9c00198..00000000000 --- a/pyrogram/client/types/messages_and_media/video.py +++ /dev/null @@ -1,119 +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 struct import pack - -import pyrogram -from pyrogram.api import types -from .photo_size import PhotoSize -from ..pyrogram_type import PyrogramType -from ...ext.utils import encode - - -class Video(PyrogramType): - """This object represents a video file. - - Parameters: - file_id (``str``): - Unique identifier for this file. - - width (``int``): - Video width as defined by sender. - - height (``int``): - Video height as defined by sender. - - duration (``int``): - Duration of the video in seconds as defined by sender. - - thumb (:obj:`PhotoSize`, *optional*): - Video thumbnail. - - file_name (``str``, *optional*): - Video file name. - - mime_type (``str``, *optional*): - Mime type of a file as defined by sender. - - supports_streaming (``bool``, *optional*): - True, if the video was uploaded with streaming support. - - file_size (``int``, *optional*): - File size. - - date (``int``, *optional*): - Date the video was sent in Unix time. - """ - - __slots__ = [ - "file_id", "width", "height", "duration", "thumb", "file_name", "mime_type", "supports_streaming", "file_size", - "date" - ] - - def __init__( - self, - *, - client: "pyrogram.client.ext.BaseClient", - file_id: str, - width: int, - height: int, - duration: int, - thumb: PhotoSize = None, - file_name: str = None, - mime_type: str = None, - supports_streaming: bool = None, - file_size: int = None, - date: int = None - ): - super().__init__(client) - - self.file_id = file_id - self.width = width - self.height = height - self.duration = duration - self.thumb = thumb - self.file_name = file_name - self.mime_type = mime_type - self.supports_streaming = supports_streaming - self.file_size = file_size - self.date = date - - @staticmethod - def _parse(client, video: types.Document, video_attributes: types.DocumentAttributeVideo, - file_name: str) -> "Video": - return Video( - file_id=encode( - pack( - " -# -# 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 struct import pack - -import pyrogram -from pyrogram.api import types -from .photo_size import PhotoSize -from ..pyrogram_type import PyrogramType -from ...ext.utils import encode - - -class VideoNote(PyrogramType): - """This object represents a video note. - - Parameters: - file_id (``str``): - Unique identifier for this file. - - length (``int``): - Video width and height as defined by sender. - - duration (``int``): - Duration of the video in seconds as defined by sender. - - thumb (:obj:`PhotoSize`, *optional*): - Video thumbnail. - - mime_type (``str``, *optional*): - MIME type of the file as defined by sender. - - file_size (``int``, *optional*): - File size. - - date (``int``, *optional*): - Date the video note was sent in Unix time. - """ - - __slots__ = ["file_id", "thumb", "mime_type", "file_size", "date", "length", "duration"] - - def __init__( - self, - *, - client: "pyrogram.client.ext.BaseClient", - file_id: str, - length: int, - duration: int, - thumb: PhotoSize = None, - mime_type: str = None, - file_size: int = None, - date: int = None - ): - super().__init__(client) - - self.file_id = file_id - self.thumb = thumb - self.mime_type = mime_type - self.file_size = file_size - self.date = date - self.length = length - self.duration = duration - - @staticmethod - def _parse(client, video_note: types.Document, video_attributes: types.DocumentAttributeVideo) -> "VideoNote": - return VideoNote( - file_id=encode( - pack( - " -# -# 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 struct import pack - -import pyrogram -from pyrogram.api import types -from ..pyrogram_type import PyrogramType -from ...ext.utils import encode - - -class Voice(PyrogramType): - """This object represents a voice note. - - Parameters: - file_id (``str``): - Unique identifier for this file. - - duration (``int``): - Duration of the audio in seconds as defined by sender. - - waveform (``bytes``, *optional*): - Voice waveform. - - mime_type (``str``, *optional*): - MIME type of the file as defined by sender. - - file_size (``int``, *optional*): - File size. - - date (``int``, *optional*): - Date the voice was sent in Unix time. - """ - - __slots__ = ["file_id", "duration", "waveform", "mime_type", "file_size", "date"] - - def __init__( - self, - *, - client: "pyrogram.client.ext.BaseClient", - file_id: str, - duration: int, - waveform: bytes = None, - mime_type: str = None, - file_size: int = None, - date: int = None - ): - super().__init__(client) - - self.file_id = file_id - self.duration = duration - self.waveform = waveform - self.mime_type = mime_type - self.file_size = file_size - self.date = date - - @staticmethod - def _parse(client, voice: types.Document, attributes: types.DocumentAttributeAudio) -> "Voice": - return Voice( - file_id=encode( - pack( - " -# -# 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 json import dumps - -import pyrogram - - -class PyrogramType: - __slots__ = ["_client"] - - def __init__(self, client: "pyrogram.client.ext.BaseClient"): - self._client = client - - def __str__(self): - return dumps(self, indent=4, default=default, ensure_ascii=False) - - 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: PyrogramType): - try: - content = {i: getattr(o, i) for i in o.__slots__} - - return remove_none( - OrderedDict( - [("_", "pyrogram." + o.__class__.__name__)] - + [i for i in content.items() if not i[0].startswith("_")] - ) - ) - except AttributeError: - return repr(o) diff --git a/pyrogram/client/types/update.py b/pyrogram/client/types/update.py deleted file mode 100644 index 48179ac0858..00000000000 --- a/pyrogram/client/types/update.py +++ /dev/null @@ -1,35 +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 . - - -class StopPropagation(StopIteration): - pass - - -class ContinuePropagation(StopIteration): - pass - - -class Update: - __slots__ = [] - - def stop_propagation(self): - raise StopPropagation - - def continue_propagation(self): - raise ContinuePropagation diff --git a/pyrogram/client/types/user_and_chats/__init__.py b/pyrogram/client/types/user_and_chats/__init__.py deleted file mode 100644 index 2059589ab61..00000000000 --- a/pyrogram/client/types/user_and_chats/__init__.py +++ /dev/null @@ -1,33 +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 .chat import Chat -from .chat_member import ChatMember -from .chat_members import ChatMembers -from .chat_permissions import ChatPermissions -from .chat_photo import ChatPhoto -from .chat_preview import ChatPreview -from .dialog import Dialog -from .dialogs import Dialogs -from .user import User -from .user_status import UserStatus - -__all__ = [ - "Chat", "ChatMember", "ChatMembers", "ChatPermissions", "ChatPhoto", "ChatPreview", "Dialog", "Dialogs", "User", - "UserStatus" -] diff --git a/pyrogram/client/types/user_and_chats/chat.py b/pyrogram/client/types/user_and_chats/chat.py deleted file mode 100644 index 0c6210eb352..00000000000 --- a/pyrogram/client/types/user_and_chats/chat.py +++ /dev/null @@ -1,233 +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 typing import Union - -import pyrogram -from pyrogram.api import types -from .chat_permissions import ChatPermissions -from .chat_photo import ChatPhoto -from ..pyrogram_type import PyrogramType - - -class Chat(PyrogramType): - """This object represents a chat. - - Parameters: - id (``int``): - Unique identifier for this chat. - - type (``str``): - Type of chat, can be either "private", "group", "supergroup" or "channel". - - title (``str``, *optional*): - Title, for supergroups, channels and basic group chats. - - username (``str``, *optional*): - Username, for private chats, supergroups and channels if available. - - first_name (``str``, *optional*): - First name of the other party in a private chat. - - last_name (``str``, *optional*): - Last name of the other party in a private chat. - - photo (:obj:`ChatPhoto `, *optional*): - Chat photo. Suitable for downloads only. - - description (``str``, *optional*): - Description, for supergroups and channel chats. - Returned only in :meth:`get_chat() `. - - invite_link (``str``, *optional*): - Chat invite link, for supergroups and channel chats. - Returned only in :meth:`get_chat() `. - - pinned_message (:obj:`Message`, *optional*): - Pinned message, for supergroups and channel chats. - Returned only in :meth:`get_chat() `. - - sticker_set_name (``str``, *optional*): - For supergroups, name of group sticker set. - Returned only in :meth:`get_chat() `. - - can_set_sticker_set (``bool``, *optional*): - True, if the group sticker set can be changed by you. - Returned only in :meth:`get_chat() `. - - members_count (``int``, *optional*): - Chat members count, for groups and channels only. - - restriction_reason (``str``, *optional*): - The reason why this chat might be unavailable to some users. - - permissions (:obj:`ChatPermissions ` *optional*): - Information about the chat default permissions. - """ - - __slots__ = [ - "id", "type", "title", "username", "first_name", "last_name", "photo", "description", "invite_link", - "pinned_message", "sticker_set_name", "can_set_sticker_set", "members_count", "restriction_reason", - "permissions" - ] - - def __init__( - self, - *, - client: "pyrogram.client.ext.BaseClient", - id: int, - type: str, - title: str = None, - username: str = None, - first_name: str = None, - last_name: str = None, - photo: ChatPhoto = None, - description: str = None, - invite_link: str = None, - pinned_message=None, - sticker_set_name: str = None, - can_set_sticker_set: bool = None, - members_count: int = None, - restriction_reason: str = None, - permissions: "pyrogram.ChatPermissions" = None - ): - super().__init__(client) - - self.id = id - self.type = type - self.title = title - self.username = username - self.first_name = first_name - self.last_name = last_name - self.photo = photo - self.description = description - self.invite_link = invite_link - self.pinned_message = pinned_message - self.sticker_set_name = sticker_set_name - self.can_set_sticker_set = can_set_sticker_set - self.members_count = members_count - self.restriction_reason = restriction_reason - self.permissions = permissions - - @staticmethod - def _parse_user_chat(client, user: types.User) -> "Chat": - return Chat( - id=user.id, - type="private", - username=user.username, - first_name=user.first_name, - last_name=user.last_name, - photo=ChatPhoto._parse(client, user.photo), - restriction_reason=user.restriction_reason, - client=client - ) - - @staticmethod - def _parse_chat_chat(client, chat: types.Chat) -> "Chat": - return Chat( - id=-chat.id, - type="group", - title=chat.title, - photo=ChatPhoto._parse(client, getattr(chat, "photo", None)), - permissions=ChatPermissions._parse(getattr(chat, "default_banned_rights", None)), - client=client - ) - - @staticmethod - def _parse_channel_chat(client, channel: types.Channel) -> "Chat": - return Chat( - id=int("-100" + str(channel.id)), - type="supergroup" if channel.megagroup else "channel", - title=channel.title, - username=getattr(channel, "username", None), - photo=ChatPhoto._parse(client, getattr(channel, "photo", None)), - restriction_reason=getattr(channel, "restriction_reason", None), - permissions=ChatPermissions._parse(getattr(channel, "default_banned_rights", None)), - client=client - ) - - @staticmethod - def _parse(client, message: types.Message or types.MessageService, users: dict, chats: dict) -> "Chat": - if isinstance(message.to_id, types.PeerUser): - return Chat._parse_user_chat(client, users[message.to_id.user_id if message.out else message.from_id]) - - if isinstance(message.to_id, types.PeerChat): - return Chat._parse_chat_chat(client, chats[message.to_id.chat_id]) - - return Chat._parse_channel_chat(client, chats[message.to_id.channel_id]) - - @staticmethod - def _parse_dialog(client, peer, users: dict, chats: dict): - if isinstance(peer, types.PeerUser): - return Chat._parse_user_chat(client, users[peer.user_id]) - elif isinstance(peer, types.PeerChat): - return Chat._parse_chat_chat(client, chats[peer.chat_id]) - else: - return Chat._parse_channel_chat(client, chats[peer.channel_id]) - - @staticmethod - def _parse_full(client, chat_full: types.messages.ChatFull or types.UserFull) -> "Chat": - if isinstance(chat_full, types.UserFull): - parsed_chat = Chat._parse_user_chat(client, chat_full.user) - parsed_chat.description = chat_full.about - - if chat_full.pinned_msg_id: - parsed_chat.pinned_message = client.get_messages( - parsed_chat.id, - message_ids=chat_full.pinned_msg_id - ) - else: - full_chat = chat_full.full_chat - chat = None - - for i in chat_full.chats: - if full_chat.id == i.id: - chat = i - - if isinstance(full_chat, types.ChatFull): - parsed_chat = Chat._parse_chat_chat(client, chat) - - if isinstance(full_chat.participants, types.ChatParticipants): - parsed_chat.members_count = len(full_chat.participants.participants) - else: - parsed_chat = Chat._parse_channel_chat(client, chat) - parsed_chat.members_count = full_chat.participants_count - parsed_chat.description = full_chat.about or None - # TODO: Add StickerSet type - parsed_chat.can_set_sticker_set = full_chat.can_set_stickers - parsed_chat.sticker_set_name = getattr(full_chat.stickerset, "short_name", None) - - if full_chat.pinned_msg_id: - parsed_chat.pinned_message = client.get_messages( - parsed_chat.id, - message_ids=full_chat.pinned_msg_id - ) - - if isinstance(full_chat.exported_invite, types.ChatInviteExported): - parsed_chat.invite_link = full_chat.exported_invite.link - - return parsed_chat - - @staticmethod - def _parse_chat(client, chat: Union[types.Chat, types.User, types.Channel]) -> "Chat": - if isinstance(chat, types.Chat): - return Chat._parse_chat_chat(client, chat) - elif isinstance(chat, types.User): - return Chat._parse_user_chat(client, chat) - else: - return Chat._parse_channel_chat(client, chat) diff --git a/pyrogram/client/types/user_and_chats/chat_member.py b/pyrogram/client/types/user_and_chats/chat_member.py deleted file mode 100644 index 9841af83464..00000000000 --- a/pyrogram/client/types/user_and_chats/chat_member.py +++ /dev/null @@ -1,137 +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 . - -import pyrogram - -from pyrogram.api import types -from ..pyrogram_type import PyrogramType - - -class ChatMember(PyrogramType): - """This object contains information about one member of a chat. - - Parameters: - user (:obj:`User`): - Information about the user. - - status (``str``): - The member's status in the chat. - Can be "creator", "administrator", "member", "restricted", "left" or "kicked". - - date (``int``, *optional*): - Date when the user joined, unix time. Not available for creator. - - is_member (``bool``, *optional*): - Restricted only. True, if the user is a member of the chat at the moment of the request. - - invited_by (:obj:`User`, *optional*): - Administrators and self member only. Information about the user who invited this member. - In case the user joined by himself this will be the same as "user". - - promoted_by (:obj:`User`, *optional*): - Administrators only. Information about the user who promoted this member as administrator. - - restricted_by (:obj:`User`, *optional*): - Restricted and kicked only. Information about the user who restricted or kicked this member. - - permissions (:obj:`ChatPermissions` *optional*): - Administrators, restricted and kicked members only. - Information about the member permissions. - """ - - __slots__ = ["user", "status", "date", "is_member", "invited_by", "promoted_by", "restricted_by", "permissions"] - - def __init__( - self, - *, - client: "pyrogram.client.ext.BaseClient", - user: "pyrogram.User", - status: str, - date: int = None, - is_member: bool = None, - invited_by: "pyrogram.User" = None, - promoted_by: "pyrogram.User" = None, - restricted_by: "pyrogram.User" = None, - permissions: "pyrogram.ChatPermissions" = None - ): - super().__init__(client) - - self.user = user - self.status = status - self.date = date - self.is_member = is_member - self.invited_by = invited_by - self.promoted_by = promoted_by - self.restricted_by = restricted_by - self.permissions = permissions - - @staticmethod - def _parse(client, member, users) -> "ChatMember": - user = pyrogram.User._parse(client, users[member.user_id]) - - invited_by = ( - pyrogram.User._parse(client, users[member.inviter_id]) - if getattr(member, "inviter_id", None) else None - ) - - if isinstance(member, (types.ChannelParticipant, types.ChannelParticipantSelf, types.ChatParticipant)): - return ChatMember( - user=user, - status="member", - date=member.date, - invited_by=invited_by, - client=client - ) - - if isinstance(member, (types.ChannelParticipantCreator, types.ChatParticipantCreator)): - return ChatMember( - user=user, - status="creator", - client=client - ) - - if isinstance(member, types.ChatParticipantAdmin): - return ChatMember( - user=user, - status="administrator", - date=member.date, - invited_by=invited_by, - client=client - ) - - if isinstance(member, types.ChannelParticipantAdmin): - return ChatMember( - user=user, - status="administrator", - date=member.date, - invited_by=invited_by, - promoted_by=pyrogram.User._parse(client, users[member.promoted_by]), - permissions=pyrogram.ChatPermissions._parse(member), - client=client - ) - - if isinstance(member, types.ChannelParticipantBanned): - return ChatMember( - user=user, - status="kicked" if member.banned_rights.view_messages else "restricted", - date=member.date, - is_member=not member.left, - restricted_by=pyrogram.User._parse(client, users[member.kicked_by]), - permissions=pyrogram.ChatPermissions._parse(member), - client=client - ) diff --git a/pyrogram/client/types/user_and_chats/chat_members.py b/pyrogram/client/types/user_and_chats/chat_members.py deleted file mode 100644 index d7cf3b9a289..00000000000 --- a/pyrogram/client/types/user_and_chats/chat_members.py +++ /dev/null @@ -1,71 +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 typing import List - -import pyrogram -from pyrogram.api import types -from .chat_member import ChatMember -from ..pyrogram_type import PyrogramType - - -class ChatMembers(PyrogramType): - """This object contains information about the members list of a chat. - - Parameters: - total_count (``int``): - Total number of members the chat has. - - chat_members (List of :obj:`ChatMember `): - Requested chat members. - """ - - __slots__ = ["total_count", "chat_members"] - - def __init__( - self, - *, - client: "pyrogram.client.ext.BaseClient", - total_count: int, - chat_members: List[ChatMember] - ): - super().__init__(client) - - self.total_count = total_count - self.chat_members = chat_members - - @staticmethod - def _parse(client, members): - users = {i.id: i for i in members.users} - chat_members = [] - - if isinstance(members, types.channels.ChannelParticipants): - total_count = members.count - members = members.participants - else: - members = members.full_chat.participants.participants - total_count = len(members) - - for member in members: - chat_members.append(ChatMember._parse(client, member, users)) - - return ChatMembers( - total_count=total_count, - chat_members=chat_members, - client=client - ) diff --git a/pyrogram/client/types/user_and_chats/chat_permissions.py b/pyrogram/client/types/user_and_chats/chat_permissions.py deleted file mode 100644 index ec00272eedd..00000000000 --- a/pyrogram/client/types/user_and_chats/chat_permissions.py +++ /dev/null @@ -1,189 +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 typing import Union - -from pyrogram.api import types -from ..pyrogram_type import PyrogramType - - -class ChatPermissions(PyrogramType): - """This object represents both a chat default permissions and a single member permissions within a chat. - - Some permissions make sense depending on the context: default chat permissions, restricted/kicked member or - administrators in groups or channels. - - Parameters: - until_date (``int``, *optional*): - Applicable to restricted and kicked members only. - Date when user restrictions will be lifted, unix time. - 0 means the restrictions will never be lifted (user restricted forever). - - can_be_edited (``bool``, *optional*): - Applicable to administrators only. - True, if you are allowed to edit administrator privileges of the user. - - can_change_info (``bool``, *optional*): - Applicable to default chat permissions in private groups and administrators in public groups only. - True, if the chat title, photo and other settings can be changed. - - can_post_messages (``bool``, *optional*): - Applicable to channel administrators only. - True, if the administrator can post messages in the channel, channels only. - - can_edit_messages (``bool``, *optional*): - Applicable to channel administrators only. - True, if the administrator can edit messages of other users and can pin messages, channels only. - - can_delete_messages (``bool``, *optional*): - Applicable to administrators only. - True, if the administrator can delete messages of other users. - - can_restrict_members (``bool``, *optional*): - Applicable to administrators only. - True, if the administrator can restrict, ban or unban chat members. - - can_invite_users (``bool``, *optional*): - Applicable to default chat permissions and administrators only. - True, if new users can be invited to the chat. - - can_pin_messages (``bool``, *optional*): - Applicable to default chat permissions in private groups and administrators in public groups only. - True, if messages can be pinned, supergroups only. - - can_promote_members (``bool``, *optional*): - Applicable to administrators only. - True, if the administrator can add new administrators with a subset of his own privileges or demote - administrators that he has promoted, directly or indirectly (promoted by administrators that were appointed - by the user). - - can_send_messages (``bool``, *optional*): - Applicable to default chat permissions and restricted members only. - True, if text messages, contacts, locations and venues can be sent. - - can_send_media_messages (``bool``, *optional*): - Applicable to default chat permissions and restricted members only. - True, if audios, documents, photos, videos, video notes and voice notes can be sent, implies - can_send_messages. - - can_send_other_messages (``bool``, *optional*): - Applicable to default chat permissions and restricted members only. - True, if animations, games, stickers and inline bot results can be sent, implies can_send_media_messages. - - can_add_web_page_previews (``bool``, *optional*): - Applicable to default chat permissions and restricted members only. - True, if web page previews can be attached to text messages, implies can_send_media_messages. - - can_send_polls (``bool``, *optional*): - Applicable to default chat permissions and restricted members only. - True, if polls can be sent, implies can_send_media_messages. - """ - - __slots__ = [ - "until_date", "can_be_edited", "can_change_info", "can_post_messages", "can_edit_messages", - "can_delete_messages", "can_restrict_members", "can_invite_users", "can_pin_messages", "can_promote_members", - "can_send_messages", "can_send_media_messages", "can_send_other_messages", "can_add_web_page_previews", - "can_send_polls" - ] - - def __init__( - self, - *, - until_date: int = None, - - # Admin permissions - can_be_edited: bool = None, - can_change_info: bool = None, - can_post_messages: bool = None, # Channels only - can_edit_messages: bool = None, # Channels only - can_delete_messages: bool = None, - can_restrict_members: bool = None, - can_invite_users: bool = None, - can_pin_messages: bool = None, # Supergroups only - can_promote_members: bool = None, - - # Restricted user permissions - can_send_messages: bool = None, # Text, contacts, locations and venues - can_send_media_messages: bool = None, # Audios, documents, photos, videos, video notes and voice notes - can_send_other_messages: bool = None, # Animations (GIFs), games, stickers, inline bot results - can_add_web_page_previews: bool = None, - can_send_polls: bool = None - ): - super().__init__(None) - - self.until_date = until_date - self.can_be_edited = can_be_edited - - self.can_change_info = can_change_info - self.can_post_messages = can_post_messages - self.can_edit_messages = can_edit_messages - self.can_delete_messages = can_delete_messages - self.can_restrict_members = can_restrict_members - self.can_invite_users = can_invite_users - self.can_pin_messages = can_pin_messages - self.can_promote_members = can_promote_members - - self.can_send_messages = can_send_messages - self.can_send_media_messages = can_send_media_messages - self.can_send_other_messages = can_send_other_messages - self.can_add_web_page_previews = can_add_web_page_previews - self.can_send_polls = can_send_polls - - @staticmethod - def _parse( - entity: Union[ - types.ChannelParticipantAdmin, - types.ChannelParticipantBanned, - types.ChatBannedRights - ] - ) -> "ChatPermissions": - if isinstance(entity, types.ChannelParticipantAdmin): - permissions = entity.admin_rights - - return ChatPermissions( - can_be_edited=entity.can_edit, - can_change_info=permissions.change_info, - can_post_messages=permissions.post_messages, - can_edit_messages=permissions.edit_messages, - can_delete_messages=permissions.delete_messages, - can_restrict_members=permissions.ban_users, - can_invite_users=permissions.invite_users, - can_pin_messages=permissions.pin_messages, - can_promote_members=permissions.add_admins - ) - - if isinstance(entity, (types.ChannelParticipantBanned, types.ChatBannedRights)): - if isinstance(entity, types.ChannelParticipantBanned): - denied_permissions = entity.banned_rights # type: types.ChatBannedRights - else: - denied_permissions = entity - - return ChatPermissions( - until_date=0 if denied_permissions.until_date == (1 << 31) - 1 else denied_permissions.until_date, - can_send_messages=not denied_permissions.send_messages, - can_send_media_messages=not denied_permissions.send_media, - can_send_other_messages=( - not denied_permissions.send_stickers or not denied_permissions.send_gifs or - not denied_permissions.send_games or not denied_permissions.send_inline - ), - can_add_web_page_previews=not denied_permissions.embed_links, - can_send_polls=not denied_permissions.send_polls, - can_change_info=not denied_permissions.change_info, - can_invite_users=not denied_permissions.invite_users, - can_pin_messages=not denied_permissions.pin_messages - ) diff --git a/pyrogram/client/types/user_and_chats/chat_photo.py b/pyrogram/client/types/user_and_chats/chat_photo.py deleted file mode 100644 index af4f2df4714..00000000000 --- a/pyrogram/client/types/user_and_chats/chat_photo.py +++ /dev/null @@ -1,81 +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 struct import pack - -import pyrogram -from pyrogram.api import types -from ..pyrogram_type import PyrogramType -from ...ext.utils import encode - - -class ChatPhoto(PyrogramType): - """This object represents a chat photo. - - Parameters: - small_file_id (``str``): - Unique file identifier of small (160x160) chat photo. This file_id can be used only for photo download. - - big_file_id (``str``): - Unique file identifier of big (640x640) chat photo. This file_id can be used only for photo download. - """ - - __slots__ = ["small_file_id", "big_file_id"] - - def __init__( - self, - *, - client: "pyrogram.client.ext.BaseClient", - small_file_id: str, - big_file_id: str - ): - super().__init__(client) - - self.small_file_id = small_file_id - self.big_file_id = big_file_id - - @staticmethod - def _parse(client, chat_photo: types.UserProfilePhoto or types.ChatPhoto): - if not isinstance(chat_photo, (types.UserProfilePhoto, types.ChatPhoto)): - return None - - if not isinstance(chat_photo.photo_small, types.FileLocation): - return None - - if not isinstance(chat_photo.photo_big, types.FileLocation): - return None - - photo_id = getattr(chat_photo, "photo_id", 0) - loc_small = chat_photo.photo_small - loc_big = chat_photo.photo_big - - return ChatPhoto( - small_file_id=encode( - pack( - " -# -# 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 typing import List - -import pyrogram -from pyrogram.api import types -from .chat_photo import ChatPhoto -from ..pyrogram_type import PyrogramType -from ..user_and_chats.user import User - - -class ChatPreview(PyrogramType): - """This object represents a chat preview. - - Parameters: - title (``str``): - Title of the chat. - - photo (:obj:`ChatPhoto`): - Chat photo. Suitable for downloads only. - - type (``str``): - Type of chat, can be either, "group", "supergroup" or "channel". - - members_count (``int``): - Chat members count. - - members (List of :obj:`User`, *optional*): - Preview of some of the chat members. - """ - - __slots__ = ["title", "photo", "type", "members_count", "members"] - - def __init__( - self, - *, - client: "pyrogram.client.ext.BaseClient", - title: str, - photo: ChatPhoto, - type: str, - members_count: int, - members: List[User] = None - ): - super().__init__(client) - - self.title = title - self.photo = photo - self.type = type - self.members_count = members_count - self.members = members - - @staticmethod - def _parse(client, chat_invite: types.ChatInvite) -> "ChatPreview": - return ChatPreview( - title=chat_invite.title, - photo=ChatPhoto._parse(client, chat_invite.photo), - type=("group" if not chat_invite.channel else - "channel" if chat_invite.broadcast else - "supergroup"), - members_count=chat_invite.participants_count, - members=[User._parse(client, user) for user in chat_invite.participants] or None, - client=client - ) - - # TODO: Maybe just merge this object into Chat itself by adding the "members" field. - # get_chat can be used as well instead of get_chat_preview diff --git a/pyrogram/client/types/user_and_chats/dialog.py b/pyrogram/client/types/user_and_chats/dialog.py deleted file mode 100644 index ec011d813a7..00000000000 --- a/pyrogram/client/types/user_and_chats/dialog.py +++ /dev/null @@ -1,90 +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 . - -import pyrogram - -from pyrogram.api import types -from ..pyrogram_type import PyrogramType -from ..user_and_chats import Chat - - -class Dialog(PyrogramType): - """This object represents a dialog. - - Parameters: - chat (:obj:`Chat `): - Conversation the dialog belongs to. - - top_message (:obj:`Message`): - The last message sent in the dialog at this time. - - unread_messages_count (``int``): - Amount of unread messages in this dialog. - - unread_mentions_count (``int``): - Amount of unread messages containing a mention in this dialog. - - unread_mark (``bool``): - True, if the dialog has the unread mark set. - - is_pinned (``bool``): - True, if the dialog is pinned. - """ - - __slots__ = ["chat", "top_message", "unread_messages_count", "unread_mentions_count", "unread_mark", "is_pinned"] - - def __init__( - self, - *, - client: "pyrogram.client.ext.BaseClient", - chat: Chat, - top_message: "pyrogram.Message", - unread_messages_count: int, - unread_mentions_count: int, - unread_mark: bool, - is_pinned: bool - ): - super().__init__(client) - - self.chat = chat - self.top_message = top_message - self.unread_messages_count = unread_messages_count - self.unread_mentions_count = unread_mentions_count - self.unread_mark = unread_mark - self.is_pinned = is_pinned - - @staticmethod - def _parse(client, dialog, messages, users, chats) -> "Dialog": - chat_id = dialog.peer - - if isinstance(chat_id, types.PeerUser): - chat_id = chat_id.user_id - elif isinstance(chat_id, types.PeerChat): - chat_id = -chat_id.chat_id - else: - chat_id = int("-100" + str(chat_id.channel_id)) - - return Dialog( - chat=Chat._parse_dialog(client, dialog.peer, users, chats), - top_message=messages.get(chat_id), - unread_messages_count=dialog.unread_count, - unread_mentions_count=dialog.unread_mentions_count, - unread_mark=dialog.unread_mark, - is_pinned=dialog.pinned, - client=client - ) diff --git a/pyrogram/client/types/user_and_chats/dialogs.py b/pyrogram/client/types/user_and_chats/dialogs.py deleted file mode 100644 index 97ca462bba1..00000000000 --- a/pyrogram/client/types/user_and_chats/dialogs.py +++ /dev/null @@ -1,79 +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 typing import List - -import pyrogram -from pyrogram.api import types -from .dialog import Dialog -from ..messages_and_media import Message -from ..pyrogram_type import PyrogramType - - -class Dialogs(PyrogramType): - """This object represents a user's dialogs chunk. - - Parameters: - total_count (``int``): - Total number of dialogs the user has. - - dialogs (List of :obj:`Dialog`): - Requested dialogs. - """ - - __slots__ = ["total_count", "dialogs"] - - def __init__( - self, - *, - client: "pyrogram.client.ext.BaseClient", - total_count: int, - dialogs: List[Dialog] - ): - super().__init__(client) - - self.total_count = total_count - self.dialogs = dialogs - - @staticmethod - def _parse(client, dialogs) -> "Dialogs": - users = {i.id: i for i in dialogs.users} - chats = {i.id: i for i in dialogs.chats} - - messages = {} - - for message in dialogs.messages: - to_id = message.to_id - - if isinstance(to_id, types.PeerUser): - if message.out: - chat_id = to_id.user_id - else: - chat_id = message.from_id - elif isinstance(to_id, types.PeerChat): - chat_id = -to_id.chat_id - else: - chat_id = int("-100" + str(to_id.channel_id)) - - messages[chat_id] = Message._parse(client, message, users, chats) - - return Dialogs( - total_count=getattr(dialogs, "count", len(dialogs.dialogs)), - dialogs=[Dialog._parse(client, dialog, messages, users, chats) for dialog in dialogs.dialogs], - client=client - ) diff --git a/pyrogram/client/types/user_and_chats/user.py b/pyrogram/client/types/user_and_chats/user.py deleted file mode 100644 index 976ce5a0a14..00000000000 --- a/pyrogram/client/types/user_and_chats/user.py +++ /dev/null @@ -1,135 +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 . - -import pyrogram -from pyrogram.api import types -from .chat_photo import ChatPhoto -from .user_status import UserStatus -from ..pyrogram_type import PyrogramType - - -class User(PyrogramType): - """This object represents a Telegram user or bot. - - Parameters: - id (``int``): - Unique identifier for this user or bot. - - is_self(``bool``): - True, if this user is you yourself. - - is_contact(``bool``): - True, if this user is in your contacts. - - is_mutual_contact(``bool``): - True, if you both have each other's contact. - - is_deleted(``bool``): - True, if this user is deleted. - - is_bot (``bool``): - True, if this user is a bot. - - first_name (``str``): - User's or bot's first name. - - status (:obj:`UserStatus `, *optional*): - User's Last Seen status. Empty for bots. - - last_name (``str``, *optional*): - User's or bot's last name. - - username (``str``, *optional*): - User's or bot's username. - - language_code (``str``, *optional*): - IETF language tag of the user's language. - - phone_number (``str``, *optional*): - User's phone number. - - photo (:obj:`ChatPhoto `, *optional*): - User's or bot's current profile photo. Suitable for downloads only. - - restriction_reason (``str``, *optional*): - The reason why this bot might be unavailable to some users. - """ - - __slots__ = [ - "id", "is_self", "is_contact", "is_mutual_contact", "is_deleted", "is_bot", "first_name", "last_name", "status", - "username", "language_code", "phone_number", "photo", "restriction_reason" - ] - - def __init__( - self, - *, - client: "pyrogram.client.ext.BaseClient", - id: int, - is_self: bool, - is_contact: bool, - is_mutual_contact: bool, - is_deleted: bool, - is_bot: bool, - first_name: str, - last_name: str = None, - status: UserStatus = None, - username: str = None, - language_code: str = None, - phone_number: str = None, - photo: ChatPhoto = None, - restriction_reason: str = None - ): - super().__init__(client) - - self.id = id - self.is_self = is_self - self.is_contact = is_contact - self.is_mutual_contact = is_mutual_contact - self.is_deleted = is_deleted - self.is_bot = is_bot - self.first_name = first_name - self.last_name = last_name - self.status = status - self.username = username - self.language_code = language_code - self.phone_number = phone_number - self.photo = photo - self.restriction_reason = restriction_reason - - @staticmethod - def _parse(client, user: types.User) -> "User" or None: - if user is None: - return None - - return User( - id=user.id, - is_self=user.is_self, - is_contact=user.contact, - is_mutual_contact=user.mutual_contact, - is_deleted=user.deleted, - is_bot=user.bot, - first_name=user.first_name, - last_name=user.last_name, - status=UserStatus._parse(client, user.status, user.id, user.bot), - username=user.username, - language_code=user.lang_code, - phone_number=user.phone, - photo=ChatPhoto._parse(client, user.photo), - restriction_reason=user.restriction_reason, - client=client - ) diff --git a/pyrogram/client/types/user_and_chats/user_status.py b/pyrogram/client/types/user_and_chats/user_status.py deleted file mode 100644 index 0f9c003ee58..00000000000 --- a/pyrogram/client/types/user_and_chats/user_status.py +++ /dev/null @@ -1,116 +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 . - -import pyrogram - -from pyrogram.api import types -from ..pyrogram_type import PyrogramType -from ..update import Update - - -class UserStatus(PyrogramType, Update): - """This object represents a User status (Last Seen privacy). - - .. note:: - - You won't see exact last seen timestamps for people with whom you don't share your own. Instead, you get - "recently", "within_week", "within_month" or "long_time_ago" fields set. - - Parameters: - user_id (``int``): - User's id. - - online (``bool``, *optional*): - True if the user is online in this very moment, None otherwise. - If True, the "date" field will be also set containing the online expiration date (i.e.: the date when a - user will automatically go offline in case of no action by his client). - - offline (``bool``, *optional*): - True if the user is offline in this moment and has the Last Seen privacy setting public, None otherwise. - If True, the "date" field will be also set containing the last seen date (i.e.: the date when a user - was online the last time). - - date (``int``, *optional*): - Exact date in unix time. Available only in case "online" or "offline" equals to True. - - recently (``bool``, *optional*): - True for users with hidden Last Seen privacy that have been online between 1 second and 2-3 days ago, - None otherwise. - - within_week (``bool``, *optional*): - True for users with hidden Last Seen privacy that have been online between 2-3 and seven days ago, - None otherwise. - - within_month (``bool``, *optional*): - True for users with hidden Last Seen privacy that have been online between 6-7 days and a month ago, - None otherwise. - - long_time_ago (``bool``, *optional*): - True for users with hidden Last Seen privacy that have been online more than a month ago (this is also - always shown to blocked users), None otherwise. - """ - - __slots__ = ["user_id", "online", "offline", "date", "recently", "within_week", "within_month", "long_time_ago"] - - def __init__( - self, - *, - client: "pyrogram.client.ext.BaseClient", - user_id: int, - online: bool = None, - offline: bool = None, - date: int = None, - recently: bool = None, - within_week: bool = None, - within_month: bool = None, - long_time_ago: bool = None - ): - super().__init__(client) - - self.user_id = user_id - self.online = online - self.offline = offline - self.date = date - self.recently = recently - self.within_week = within_week - self.within_month = within_month - self.long_time_ago = long_time_ago - - @staticmethod - def _parse(client, user_status, user_id: int, is_bot: bool = False): - if is_bot: - return None - - status = UserStatus(user_id=user_id, client=client) - - if isinstance(user_status, types.UserStatusOnline): - status.online = True - status.date = user_status.expires - elif isinstance(user_status, types.UserStatusOffline): - status.offline = True - status.date = user_status.was_online - elif isinstance(user_status, types.UserStatusRecently): - status.recently = True - elif isinstance(user_status, types.UserStatusLastWeek): - status.within_week = True - elif isinstance(user_status, types.UserStatusLastMonth): - status.within_month = True - else: - status.long_time_ago = True - - return status diff --git a/pyrogram/connection/__init__.py b/pyrogram/connection/__init__.py index 731e7456da8..4665ce913fe 100644 --- a/pyrogram/connection/__init__.py +++ b/pyrogram/connection/__init__.py @@ -1,19 +1,19 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2019 Dan Tès +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan # -# This file is part of Pyrogram. +# 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 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. +# 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 . +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . from .connection import Connection diff --git a/pyrogram/connection/connection.py b/pyrogram/connection/connection.py index 0c325fae23b..1107673f1a2 100644 --- a/pyrogram/connection/connection.py +++ b/pyrogram/connection/connection.py @@ -1,81 +1,72 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2019 Dan Tès +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan # -# This file is part of Pyrogram. +# 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 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. +# 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 . +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . +import asyncio import logging -import threading -import time +from typing import Optional -from .transport import * +from .transport import TCP, TCPAbridged from ..session.internals import DataCenter log = logging.getLogger(__name__) class Connection: - MAX_RETRIES = 3 + MAX_CONNECTION_ATTEMPTS = 3 - MODES = { - 0: TCPFull, - 1: TCPAbridged, - 2: TCPIntermediate, - 3: TCPAbridgedO, - 4: TCPIntermediateO - } - - def __init__(self, dc_id: int, test_mode: bool, ipv6: bool, proxy: dict, mode: int = 3): + def __init__(self, dc_id: int, test_mode: bool, ipv6: bool, proxy: dict, media: bool = False): self.dc_id = dc_id + self.test_mode = test_mode self.ipv6 = ipv6 self.proxy = proxy - self.address = DataCenter(dc_id, test_mode, ipv6) - self.mode = self.MODES.get(mode, TCPAbridged) + self.media = media - self.lock = threading.Lock() - self.connection = None + self.address = DataCenter(dc_id, test_mode, ipv6, media) + self.protocol: TCP = None - def connect(self): - for i in range(Connection.MAX_RETRIES): - self.connection = self.mode(self.ipv6, self.proxy) + async def connect(self): + for i in range(Connection.MAX_CONNECTION_ATTEMPTS): + self.protocol = TCPAbridged(self.ipv6, self.proxy) try: log.info("Connecting...") - self.connection.connect(self.address) + await self.protocol.connect(self.address) except OSError as e: - log.warning(e) # TODO: Remove - self.connection.close() - time.sleep(1) + log.warning("Unable to connect due to network issues: %s", e) + await self.protocol.close() + await asyncio.sleep(1) else: - log.info("Connected! DC{} - IPv{} - {}".format( - self.dc_id, - "6" if self.ipv6 else "4", - self.mode.__name__ - )) + log.info("Connected! %s DC%s%s - IPv%s", + "Test" if self.test_mode else "Production", + self.dc_id, + " (media)" if self.media else "", + "6" if self.ipv6 else "4") break else: log.warning("Connection failed! Trying again...") - raise TimeoutError + raise ConnectionError - def close(self): - self.connection.close() + async def close(self): + await self.protocol.close() log.info("Disconnected") - def send(self, data: bytes): - with self.lock: - self.connection.sendall(data) + async def send(self, data: bytes): + await self.protocol.send(data) - def recv(self) -> bytes or None: - return self.connection.recvall() + async def recv(self) -> Optional[bytes]: + return await self.protocol.recv() diff --git a/pyrogram/connection/transport/__init__.py b/pyrogram/connection/transport/__init__.py index 80e0d848117..2d08832a702 100644 --- a/pyrogram/connection/transport/__init__.py +++ b/pyrogram/connection/transport/__init__.py @@ -1,19 +1,19 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2019 Dan Tès +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan # -# This file is part of Pyrogram. +# 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 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. +# 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 . +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . from .tcp import * diff --git a/pyrogram/connection/transport/tcp/__init__.py b/pyrogram/connection/transport/tcp/__init__.py index 6ed12ad8abf..3e23a883795 100644 --- a/pyrogram/connection/transport/tcp/__init__.py +++ b/pyrogram/connection/transport/tcp/__init__.py @@ -1,21 +1,22 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2019 Dan Tès +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan # -# This file is part of Pyrogram. +# 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 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. +# 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 . +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . +from .tcp import TCP from .tcp_abridged import TCPAbridged from .tcp_abridged_o import TCPAbridgedO from .tcp_full import TCPFull diff --git a/pyrogram/connection/transport/tcp/tcp.py b/pyrogram/connection/transport/tcp/tcp.py index debe52bd891..82ef033be47 100644 --- a/pyrogram/connection/transport/tcp/tcp.py +++ b/pyrogram/connection/transport/tcp/tcp.py @@ -1,90 +1,122 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2019 Dan Tès +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan # -# This file is part of Pyrogram. +# 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 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. +# 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 . +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . +import asyncio import ipaddress import logging import socket +from concurrent.futures import ThreadPoolExecutor -try: - import socks -except ImportError as e: - e.msg = ( - "PySocks is missing and Pyrogram can't run without. " - "Please install it using \"pip3 install pysocks\"." - ) - - raise e +import socks log = logging.getLogger(__name__) -class TCP(socks.socksocket): +class TCP: + TIMEOUT = 10 + def __init__(self, ipv6: bool, proxy: dict): - if proxy.get("enabled", False): - hostname = proxy.get("hostname", None) - port = proxy.get("port", None) + self.socket = None + + self.reader = None + self.writer = None + + self.lock = asyncio.Lock() + self.loop = asyncio.get_event_loop() + + self.proxy = proxy + + if proxy: + hostname = proxy.get("hostname") try: ip_address = ipaddress.ip_address(hostname) except ValueError: - super().__init__(socket.AF_INET) + self.socket = socks.socksocket(socket.AF_INET) else: if isinstance(ip_address, ipaddress.IPv6Address): - super().__init__(socket.AF_INET6) + self.socket = socks.socksocket(socket.AF_INET6) else: - super().__init__(socket.AF_INET) + self.socket = socks.socksocket(socket.AF_INET) - self.set_proxy( - proxy_type=socks.SOCKS5, + self.socket.set_proxy( + proxy_type=getattr(socks, proxy.get("scheme").upper()), addr=hostname, - port=port, + port=proxy.get("port", None), username=proxy.get("username", None), password=proxy.get("password", None) ) - log.info("Using proxy {}:{}".format(hostname, port)) + self.socket.settimeout(TCP.TIMEOUT) + + log.info("Using proxy %s", hostname) else: - super().__init__( + self.socket = socket.socket( socket.AF_INET6 if ipv6 else socket.AF_INET ) - self.settimeout(10) + self.socket.setblocking(False) - def close(self): + async def connect(self, address: tuple): + if self.proxy: + with ThreadPoolExecutor(1) as executor: + await self.loop.run_in_executor(executor, self.socket.connect, address) + else: + try: + await asyncio.wait_for(asyncio.get_event_loop().sock_connect(self.socket, address), TCP.TIMEOUT) + except asyncio.TimeoutError: # Re-raise as TimeoutError. asyncio.TimeoutError is deprecated in 3.11 + raise TimeoutError("Connection timed out") + + self.reader, self.writer = await asyncio.open_connection(sock=self.socket) + + async def close(self): try: - self.shutdown(socket.SHUT_RDWR) - except OSError: - pass - finally: - super().close() + if self.writer is not None: + self.writer.close() + await asyncio.wait_for(self.writer.wait_closed(), TCP.TIMEOUT) + except Exception as e: + log.info("Close exception: %s %s", type(e).__name__, e) + + async def send(self, data: bytes): + async with self.lock: + try: + if self.writer is not None: + self.writer.write(data) + await self.writer.drain() + except Exception as e: + log.info("Send exception: %s %s", type(e).__name__, e) + raise OSError(e) - def recvall(self, length: int) -> bytes or None: + async def recv(self, length: int = 0): data = b"" while len(data) < length: try: - packet = super().recv(length - len(data)) - except OSError: + chunk = await asyncio.wait_for( + self.reader.read(length - len(data)), + TCP.TIMEOUT + ) + except (OSError, asyncio.TimeoutError): return None else: - if packet: - data += packet + if chunk: + data += chunk else: return None diff --git a/pyrogram/connection/transport/tcp/tcp_abridged.py b/pyrogram/connection/transport/tcp/tcp_abridged.py index 56f8d0259b7..77d44cf41c1 100644 --- a/pyrogram/connection/transport/tcp/tcp_abridged.py +++ b/pyrogram/connection/transport/tcp/tcp_abridged.py @@ -1,22 +1,23 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2019 Dan Tès +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan # -# This file is part of Pyrogram. +# 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 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. +# 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 . +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . import logging +from typing import Optional from .tcp import TCP @@ -27,30 +28,30 @@ class TCPAbridged(TCP): def __init__(self, ipv6: bool, proxy: dict): super().__init__(ipv6, proxy) - def connect(self, address: tuple): - super().connect(address) - super().sendall(b"\xef") + async def connect(self, address: tuple): + await super().connect(address) + await super().send(b"\xef") - def sendall(self, data: bytes, *args): + async def send(self, data: bytes, *args): length = len(data) // 4 - super().sendall( + await super().send( (bytes([length]) if length <= 126 else b"\x7f" + length.to_bytes(3, "little")) + data ) - def recvall(self, length: int = 0) -> bytes or None: - length = super().recvall(1) + async def recv(self, length: int = 0) -> Optional[bytes]: + length = await super().recv(1) if length is None: return None if length == b"\x7f": - length = super().recvall(3) + length = await super().recv(3) if length is None: return None - return super().recvall(int.from_bytes(length, "little") * 4) + return await super().recv(int.from_bytes(length, "little") * 4) diff --git a/pyrogram/connection/transport/tcp/tcp_abridged_o.py b/pyrogram/connection/transport/tcp/tcp_abridged_o.py index 136d22ef1f3..6f57ab11549 100644 --- a/pyrogram/connection/transport/tcp/tcp_abridged_o.py +++ b/pyrogram/connection/transport/tcp/tcp_abridged_o.py @@ -1,26 +1,28 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2019 Dan Tès +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan # -# This file is part of Pyrogram. +# 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 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. +# 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 . +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . import logging import os +from typing import Optional +import pyrogram +from pyrogram.crypto import aes from .tcp import TCP -from ....crypto.aes import AES log = logging.getLogger(__name__) @@ -34,13 +36,13 @@ def __init__(self, ipv6: bool, proxy: dict): self.encrypt = None self.decrypt = None - def connect(self, address: tuple): - super().connect(address) + async def connect(self, address: tuple): + await super().connect(address) while True: nonce = bytearray(os.urandom(64)) - if nonce[0] != b"\xef" and nonce[:4] not in self.RESERVED and nonce[4:4] != b"\x00" * 4: + if bytes([nonce[0]]) != b"\xef" and nonce[:4] not in self.RESERVED and nonce[4:8] != b"\x00" * 4: nonce[56] = nonce[57] = nonce[58] = nonce[59] = 0xef break @@ -49,42 +51,36 @@ def connect(self, address: tuple): self.encrypt = (nonce[8:40], nonce[40:56], bytearray(1)) self.decrypt = (temp[0:32], temp[32:48], bytearray(1)) - nonce[56:64] = AES.ctr256_encrypt(nonce, *self.encrypt)[56:64] + nonce[56:64] = aes.ctr256_encrypt(nonce, *self.encrypt)[56:64] - super().sendall(nonce) + await super().send(nonce) - def sendall(self, data: bytes, *args): + async def send(self, data: bytes, *args): length = len(data) // 4 + data = (bytes([length]) if length <= 126 else b"\x7f" + length.to_bytes(3, "little")) + data + payload = await self.loop.run_in_executor(pyrogram.crypto_executor, aes.ctr256_encrypt, data, *self.encrypt) - super().sendall( - AES.ctr256_encrypt( - (bytes([length]) - if length <= 126 - else b"\x7f" + length.to_bytes(3, "little")) - + data, - *self.encrypt - ) - ) + await super().send(payload) - def recvall(self, length: int = 0) -> bytes or None: - length = super().recvall(1) + async def recv(self, length: int = 0) -> Optional[bytes]: + length = await super().recv(1) if length is None: return None - length = AES.ctr256_decrypt(length, *self.decrypt) + length = aes.ctr256_decrypt(length, *self.decrypt) if length == b"\x7f": - length = super().recvall(3) + length = await super().recv(3) if length is None: return None - length = AES.ctr256_decrypt(length, *self.decrypt) + length = aes.ctr256_decrypt(length, *self.decrypt) - data = super().recvall(int.from_bytes(length, "little") * 4) + data = await super().recv(int.from_bytes(length, "little") * 4) if data is None: return None - return AES.ctr256_decrypt(data, *self.decrypt) + return await self.loop.run_in_executor(pyrogram.crypto_executor, aes.ctr256_decrypt, data, *self.decrypt) diff --git a/pyrogram/connection/transport/tcp/tcp_full.py b/pyrogram/connection/transport/tcp/tcp_full.py index 36f14adbd61..8bd89000c8b 100644 --- a/pyrogram/connection/transport/tcp/tcp_full.py +++ b/pyrogram/connection/transport/tcp/tcp_full.py @@ -1,24 +1,25 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2019 Dan Tès +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan # -# This file is part of Pyrogram. +# 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 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. +# 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 . +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . import logging from binascii import crc32 from struct import pack, unpack +from typing import Optional from .tcp import TCP @@ -31,34 +32,33 @@ def __init__(self, ipv6: bool, proxy: dict): self.seq_no = None - def connect(self, address: tuple): - super().connect(address) + async def connect(self, address: tuple): + await super().connect(address) self.seq_no = 0 - def sendall(self, data: bytes, *args): - # 12 = packet_length (4), seq_no (4), crc32 (4) (at the end) + async def send(self, data: bytes, *args): data = pack(" bytes or None: - length = super().recvall(4) + async def recv(self, length: int = 0) -> Optional[bytes]: + length = await super().recv(4) if length is None: return None - packet = super().recvall(unpack(" +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan # -# This file is part of Pyrogram. +# 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 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. +# 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 . +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . import logging from struct import pack, unpack +from typing import Optional from .tcp import TCP @@ -28,17 +29,17 @@ class TCPIntermediate(TCP): def __init__(self, ipv6: bool, proxy: dict): super().__init__(ipv6, proxy) - def connect(self, address: tuple): - super().connect(address) - super().sendall(b"\xee" * 4) + async def connect(self, address: tuple): + await super().connect(address) + await super().send(b"\xee" * 4) - def sendall(self, data: bytes, *args): - super().sendall(pack(" bytes or None: - length = super().recvall(4) + async def recv(self, length: int = 0) -> Optional[bytes]: + length = await super().recv(4) if length is None: return None - return super().recvall(unpack(" +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan # -# This file is part of Pyrogram. +# 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 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. +# 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 . +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . import logging import os from struct import pack, unpack +from typing import Optional +from pyrogram.crypto import aes from .tcp import TCP -from ....crypto.aes import AES log = logging.getLogger(__name__) @@ -35,13 +36,13 @@ def __init__(self, ipv6: bool, proxy: dict): self.encrypt = None self.decrypt = None - def connect(self, address: tuple): - super().connect(address) + async def connect(self, address: tuple): + await super().connect(address) while True: nonce = bytearray(os.urandom(64)) - if nonce[0] != b"\xef" and nonce[:4] not in self.RESERVED and nonce[4:4] != b"\x00" * 4: + if bytes([nonce[0]]) != b"\xef" and nonce[:4] not in self.RESERVED and nonce[4:8] != b"\x00" * 4: nonce[56] = nonce[57] = nonce[58] = nonce[59] = 0xee break @@ -50,29 +51,29 @@ def connect(self, address: tuple): self.encrypt = (nonce[8:40], nonce[40:56], bytearray(1)) self.decrypt = (temp[0:32], temp[32:48], bytearray(1)) - nonce[56:64] = AES.ctr256_encrypt(nonce, *self.encrypt)[56:64] + nonce[56:64] = aes.ctr256_encrypt(nonce, *self.encrypt)[56:64] - super().sendall(nonce) + await super().send(nonce) - def sendall(self, data: bytes, *args): - super().sendall( - AES.ctr256_encrypt( + async def send(self, data: bytes, *args): + await super().send( + aes.ctr256_encrypt( pack(" bytes or None: - length = super().recvall(4) + async def recv(self, length: int = 0) -> Optional[bytes]: + length = await super().recv(4) if length is None: return None - length = AES.ctr256_decrypt(length, *self.decrypt) + length = aes.ctr256_decrypt(length, *self.decrypt) - data = super().recvall(unpack(" +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan # -# This file is part of Pyrogram. +# 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 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. +# 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 .aes import AES -from .kdf import KDF -from .prime import Prime -from .rsa import RSA +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . diff --git a/pyrogram/crypto/aes.py b/pyrogram/crypto/aes.py index de275bd0f4d..bb937128df9 100644 --- a/pyrogram/crypto/aes.py +++ b/pyrogram/crypto/aes.py @@ -1,20 +1,20 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2019 Dan Tès +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan # -# This file is part of Pyrogram. +# 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 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. +# 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 . +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . import logging @@ -26,109 +26,105 @@ log.info("Using TgCrypto") - class AES: - @classmethod - def ige256_encrypt(cls, data: bytes, key: bytes, iv: bytes) -> bytes: - return tgcrypto.ige256_encrypt(data, key, iv) + def ige256_encrypt(data: bytes, key: bytes, iv: bytes) -> bytes: + return tgcrypto.ige256_encrypt(data, key, iv) - @classmethod - def ige256_decrypt(cls, data: bytes, key: bytes, iv: bytes) -> bytes: - return tgcrypto.ige256_decrypt(data, key, iv) - @staticmethod - def ctr256_encrypt(data: bytes, key: bytes, iv: bytearray, state: bytearray = None) -> bytes: - return tgcrypto.ctr256_encrypt(data, key, iv, state or bytearray(1)) + def ige256_decrypt(data: bytes, key: bytes, iv: bytes) -> bytes: + return tgcrypto.ige256_decrypt(data, key, iv) - @staticmethod - def ctr256_decrypt(data: bytes, key: bytes, iv: bytearray, state: bytearray = None) -> bytes: - return tgcrypto.ctr256_decrypt(data, key, iv, state or bytearray(1)) - @staticmethod - def xor(a: bytes, b: bytes) -> bytes: - return int.to_bytes( - int.from_bytes(a, "big") ^ int.from_bytes(b, "big"), - len(a), - "big", - ) + def ctr256_encrypt(data: bytes, key: bytes, iv: bytearray, state: bytearray = None) -> bytes: + return tgcrypto.ctr256_encrypt(data, key, iv, state or bytearray(1)) + + + def ctr256_decrypt(data: bytes, key: bytes, iv: bytearray, state: bytearray = None) -> bytes: + return tgcrypto.ctr256_decrypt(data, key, iv, state or bytearray(1)) + + + def xor(a: bytes, b: bytes) -> bytes: + return int.to_bytes( + int.from_bytes(a, "big") ^ int.from_bytes(b, "big"), + len(a), + "big", + ) except ImportError: import pyaes log.warning( "TgCrypto is missing! " "Pyrogram will work the same, but at a much slower speed. " - "More info: https://docs.pyrogram.ml/resources/TgCrypto" + "More info: https://docs.pyrogram.org/topics/speedups" ) - class AES: - @classmethod - def ige256_encrypt(cls, data: bytes, key: bytes, iv: bytes) -> bytes: - return cls.ige(data, key, iv, True) + def ige256_encrypt(data: bytes, key: bytes, iv: bytes) -> bytes: + return ige(data, key, iv, True) + + + def ige256_decrypt(data: bytes, key: bytes, iv: bytes) -> bytes: + return ige(data, key, iv, False) + + + def ctr256_encrypt(data: bytes, key: bytes, iv: bytearray, state: bytearray = None) -> bytes: + return ctr(data, key, iv, state or bytearray(1)) + + + def ctr256_decrypt(data: bytes, key: bytes, iv: bytearray, state: bytearray = None) -> bytes: + return ctr(data, key, iv, state or bytearray(1)) - @classmethod - def ige256_decrypt(cls, data: bytes, key: bytes, iv: bytes) -> bytes: - return cls.ige(data, key, iv, False) - @classmethod - def ctr256_encrypt(cls, data: bytes, key: bytes, iv: bytearray, state: bytearray = None) -> bytes: - return cls.ctr(data, key, iv, state or bytearray(1)) + def xor(a: bytes, b: bytes) -> bytes: + return int.to_bytes( + int.from_bytes(a, "big") ^ int.from_bytes(b, "big"), + len(a), + "big", + ) - @classmethod - def ctr256_decrypt(cls, data: bytes, key: bytes, iv: bytearray, state: bytearray = None) -> bytes: - return cls.ctr(data, key, iv, state or bytearray(1)) - @staticmethod - def xor(a: bytes, b: bytes) -> bytes: - return int.to_bytes( - int.from_bytes(a, "big") ^ int.from_bytes(b, "big"), - len(a), - "big", - ) + def ige(data: bytes, key: bytes, iv: bytes, encrypt: bool) -> bytes: + cipher = pyaes.AES(key) - @classmethod - def ige(cls, data: bytes, key: bytes, iv: bytes, encrypt: bool) -> bytes: - cipher = pyaes.AES(key) + iv_1 = iv[:16] + iv_2 = iv[16:] - iv_1 = iv[:16] - iv_2 = iv[16:] + data = [data[i: i + 16] for i in range(0, len(data), 16)] - data = [data[i: i + 16] for i in range(0, len(data), 16)] + if encrypt: + for i, chunk in enumerate(data): + iv_1 = data[i] = xor(cipher.encrypt(xor(chunk, iv_1)), iv_2) + iv_2 = chunk + else: + for i, chunk in enumerate(data): + iv_2 = data[i] = xor(cipher.decrypt(xor(chunk, iv_2)), iv_1) + iv_1 = chunk - if encrypt: - for i, chunk in enumerate(data): - iv_1 = data[i] = cls.xor(cipher.encrypt(cls.xor(chunk, iv_1)), iv_2) - iv_2 = chunk - else: - for i, chunk in enumerate(data): - iv_2 = data[i] = cls.xor(cipher.decrypt(cls.xor(chunk, iv_2)), iv_1) - iv_1 = chunk + return b"".join(data) - return b"".join(data) - @classmethod - def ctr(cls, data: bytes, key: bytes, iv: bytearray, state: bytearray) -> bytes: - cipher = pyaes.AES(key) + def ctr(data: bytes, key: bytes, iv: bytearray, state: bytearray) -> bytes: + cipher = pyaes.AES(key) - out = bytearray(data) - chunk = cipher.encrypt(iv) + out = bytearray(data) + chunk = cipher.encrypt(iv) - for i in range(0, len(data), 16): - for j in range(0, min(len(data) - i, 16)): - out[i + j] ^= chunk[state[0]] + for i in range(0, len(data), 16): + for j in range(0, min(len(data) - i, 16)): + out[i + j] ^= chunk[state[0]] - state[0] += 1 + state[0] += 1 - if state[0] >= 16: - state[0] = 0 + if state[0] >= 16: + state[0] = 0 - if state[0] == 0: - for k in range(15, -1, -1): - try: - iv[k] += 1 - break - except ValueError: - iv[k] = 0 + if state[0] == 0: + for k in range(15, -1, -1): + try: + iv[k] += 1 + break + except ValueError: + iv[k] = 0 - chunk = cipher.encrypt(iv) + chunk = cipher.encrypt(iv) - return out + return out diff --git a/pyrogram/crypto/kdf.py b/pyrogram/crypto/kdf.py deleted file mode 100644 index 56e5233916e..00000000000 --- a/pyrogram/crypto/kdf.py +++ /dev/null @@ -1,33 +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 hashlib import sha256 - - -class KDF: - def __new__(cls, auth_key: bytes, msg_key: bytes, outgoing: bool) -> tuple: - # https://core.telegram.org/mtproto/description#defining-aes-key-and-initialization-vector - x = 0 if outgoing else 8 - - sha256_a = sha256(msg_key + auth_key[x: x + 36]).digest() - sha256_b = sha256(auth_key[x + 40:x + 76] + msg_key).digest() # 76 = 40 + 36 - - aes_key = sha256_a[:8] + sha256_b[8:24] + sha256_a[24:32] - aes_iv = sha256_b[:8] + sha256_a[8:24] + sha256_b[24:32] - - return aes_key, aes_iv diff --git a/pyrogram/crypto/mtproto.py b/pyrogram/crypto/mtproto.py new file mode 100644 index 00000000000..6d1521a47b8 --- /dev/null +++ b/pyrogram/crypto/mtproto.py @@ -0,0 +1,100 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 hashlib import sha256 +from io import BytesIO +from os import urandom + +from pyrogram.errors import SecurityCheckMismatch +from pyrogram.raw.core import Message, Long +from . import aes + + +def kdf(auth_key: bytes, msg_key: bytes, outgoing: bool) -> tuple: + # https://core.telegram.org/mtproto/description#defining-aes-key-and-initialization-vector + x = 0 if outgoing else 8 + + sha256_a = sha256(msg_key + auth_key[x: x + 36]).digest() + sha256_b = sha256(auth_key[x + 40:x + 76] + msg_key).digest() # 76 = 40 + 36 + + aes_key = sha256_a[:8] + sha256_b[8:24] + sha256_a[24:32] + aes_iv = sha256_b[:8] + sha256_a[8:24] + sha256_b[24:32] + + return aes_key, aes_iv + + +def pack(message: Message, salt: int, session_id: bytes, auth_key: bytes, auth_key_id: bytes) -> bytes: + data = Long(salt) + session_id + message.write() + padding = urandom(-(len(data) + 12) % 16 + 12) + + # 88 = 88 + 0 (outgoing message) + msg_key_large = sha256(auth_key[88: 88 + 32] + data + padding).digest() + msg_key = msg_key_large[8:24] + aes_key, aes_iv = kdf(auth_key, msg_key, True) + + return auth_key_id + msg_key + aes.ige256_encrypt(data + padding, aes_key, aes_iv) + + +def unpack( + b: BytesIO, + session_id: bytes, + auth_key: bytes, + auth_key_id: bytes +) -> Message: + SecurityCheckMismatch.check(b.read(8) == auth_key_id, "b.read(8) == auth_key_id") + + msg_key = b.read(16) + aes_key, aes_iv = kdf(auth_key, msg_key, False) + data = BytesIO(aes.ige256_decrypt(b.read(), aes_key, aes_iv)) + data.read(8) # Salt + + # https://core.telegram.org/mtproto/security_guidelines#checking-session-id + SecurityCheckMismatch.check(data.read(8) == session_id, "data.read(8) == session_id") + + try: + message = Message.read(data) + except KeyError as e: + if e.args[0] == 0: + raise ConnectionError(f"Received empty data. Check your internet connection.") + + left = data.read().hex() + + left = [left[i:i + 64] for i in range(0, len(left), 64)] + left = [[left[i:i + 8] for i in range(0, len(left), 8)] for left in left] + left = "\n".join(" ".join(x for x in left) for left in left) + + raise ValueError(f"The server sent an unknown constructor: {hex(e.args[0])}\n{left}") + + # https://core.telegram.org/mtproto/security_guidelines#checking-sha256-hash-value-of-msg-key + # 96 = 88 + 8 (incoming message) + SecurityCheckMismatch.check( + msg_key == sha256(auth_key[96:96 + 32] + data.getvalue()).digest()[8:24], + "msg_key == sha256(auth_key[96:96 + 32] + data.getvalue()).digest()[8:24]" + ) + + # https://core.telegram.org/mtproto/security_guidelines#checking-message-length + data.seek(32) # Get to the payload, skip salt (8) + session_id (8) + msg_id (8) + seq_no (4) + length (4) + payload = data.read() + padding = payload[message.length:] + SecurityCheckMismatch.check(12 <= len(padding) <= 1024, "12 <= len(padding) <= 1024") + SecurityCheckMismatch.check(len(payload) % 4 == 0, "len(payload) % 4 == 0") + + # https://core.telegram.org/mtproto/security_guidelines#checking-msg-id + SecurityCheckMismatch.check(message.msg_id % 2 != 0, "message.msg_id % 2 != 0") + + return message diff --git a/pyrogram/crypto/prime.py b/pyrogram/crypto/prime.py index a1b76e67827..e919e22ce43 100644 --- a/pyrogram/crypto/prime.py +++ b/pyrogram/crypto/prime.py @@ -1,85 +1,82 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2019 Dan Tès +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan # -# This file is part of Pyrogram. +# 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 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. +# 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 . +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . from random import randint +CURRENT_DH_PRIME = int( + "C71CAEB9C6B1C9048E6C522F70F13F73980D40238E3E21C14934D037563D930F" + "48198A0AA7C14058229493D22530F4DBFA336F6E0AC925139543AED44CCE7C37" + "20FD51F69458705AC68CD4FE6B6B13ABDC9746512969328454F18FAF8C595F64" + "2477FE96BB2A941D5BCD1D4AC8CC49880708FA9B378E3C4F3A9060BEE67CF9A4" + "A4A695811051907E162753B56B0F6B410DBA74D8A84B2A14B3144E0EF1284754" + "FD17ED950D5965B4B9DD46582DB1178D169C6BC465B0D6FF9CA3928FEF5B9AE4" + "E418FC15E83EBEA0F87FA9FF5EED70050DED2849F47BF959D956850CE929851F" + "0D8115F635B105EE2E4E15D04B2454BF6F4FADF034B10403119CD8E3B92FCC5B", + 16 +) + + +# Recursive variant +# def gcd(cls, a: int, b: int) -> int: +# return cls.gcd(b, a % b) if b else a + +def gcd(a: int, b: int) -> int: + while b: + a, b = b, a % b + + return a -class Prime: - CURRENT_DH_PRIME = int( - "C71CAEB9C6B1C9048E6C522F70F13F73980D40238E3E21C14934D037563D930F" - "48198A0AA7C14058229493D22530F4DBFA336F6E0AC925139543AED44CCE7C37" - "20FD51F69458705AC68CD4FE6B6B13ABDC9746512969328454F18FAF8C595F64" - "2477FE96BB2A941D5BCD1D4AC8CC49880708FA9B378E3C4F3A9060BEE67CF9A4" - "A4A695811051907E162753B56B0F6B410DBA74D8A84B2A14B3144E0EF1284754" - "FD17ED950D5965B4B9DD46582DB1178D169C6BC465B0D6FF9CA3928FEF5B9AE4" - "E418FC15E83EBEA0F87FA9FF5EED70050DED2849F47BF959D956850CE929851F" - "0D8115F635B105EE2E4E15D04B2454BF6F4FADF034B10403119CD8E3B92FCC5B", - 16 - ) - - # Recursive variant - # @classmethod - # def gcd(cls, a: int, b: int) -> int: - # return cls.gcd(b, a % b) if b else a - - @staticmethod - def gcd(a: int, b: int) -> int: - while b: - a, b = b, a % b - - return a - - @classmethod - def decompose(cls, pq: int) -> int: - # https://comeoncodeon.wordpress.com/2010/09/18/pollard-rho-brent-integer-factorization/ - if pq % 2 == 0: - return 2 - - y, c, m = randint(1, pq - 1), randint(1, pq - 1), randint(1, pq - 1) - g = r = q = 1 - x = ys = 0 - - while g == 1: - x = y - - for i in range(r): - y = (pow(y, 2, pq) + c) % pq - k = 0 +def decompose(pq: int) -> int: + # https://comeoncodeon.wordpress.com/2010/09/18/pollard-rho-brent-integer-factorization/ + if pq % 2 == 0: + return 2 - while k < r and g == 1: - ys = y + y, c, m = randint(1, pq - 1), randint(1, pq - 1), randint(1, pq - 1) + g = r = q = 1 + x = ys = 0 - for i in range(min(m, r - k)): - y = (pow(y, 2, pq) + c) % pq - q = q * (abs(x - y)) % pq + while g == 1: + x = y + + for i in range(r): + y = (pow(y, 2, pq) + c) % pq + + k = 0 + + while k < r and g == 1: + ys = y + + for i in range(min(m, r - k)): + y = (pow(y, 2, pq) + c) % pq + q = q * (abs(x - y)) % pq - g = cls.gcd(q, pq) - k += m + g = gcd(q, pq) + k += m - r *= 2 + r *= 2 - if g == pq: - while True: - ys = (pow(ys, 2, pq) + c) % pq - g = cls.gcd(abs(x - ys), pq) + if g == pq: + while True: + ys = (pow(ys, 2, pq) + c) % pq + g = gcd(abs(x - ys), pq) - if g > 1: - break + if g > 1: + break - return g + return g diff --git a/pyrogram/crypto/rsa.py b/pyrogram/crypto/rsa.py index 26a9092bbd3..25c23229574 100644 --- a/pyrogram/crypto/rsa.py +++ b/pyrogram/crypto/rsa.py @@ -1,213 +1,259 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2019 Dan Tès +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan # -# This file is part of Pyrogram. +# 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 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. +# 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 . +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . from collections import namedtuple PublicKey = namedtuple("PublicKey", ["m", "e"]) +# To get modulus and exponent: +# +# [RSA PUBLIC KEY]: +# grep -v -- - public.key | tr -d \\n | base64 -d | openssl asn1parse -inform DER -i +# +# [PUBLIC KEY]: +# openssl rsa -pubin -in key -text -noout + +server_public_keys = { + # -4344800451088585951 + 0xc3b42b026ce86b21 - (1 << 64): PublicKey( # Telegram servers #1 + # -----BEGIN RSA PUBLIC KEY----- + # MIIBCgKCAQEAwVACPi9w23mF3tBkdZz+zwrzKOaaQdr01vAbU4E1pvkfj4sqDsm6 + # lyDONS789sVoD/xCS9Y0hkkC3gtL1tSfTlgCMOOul9lcixlEKzwKENj1Yz/s7daS + # an9tqw3bfUV/nqgbhGX81v/+7RFAEd+RwFnK7a+XYl9sluzHRyVVaTTveB2GazTw + # Efzk2DWgkBluml8OREmvfraX3bkHZJTKX4EQSjBbbdJ2ZXIsRrYOXfaA+xayEGB+ + # 8hdlLmAjbCVfaigxX0CDqWeR1yFL9kwd9P0NsZRPsmoqVwMbMu7mStFai6aIhc3n + # Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB + # -----END RSA PUBLIC KEY----- + int( + "C150023E2F70DB7985DED064759CFECF0AF328E69A41DAF4D6F01B538135A6F9" + "1F8F8B2A0EC9BA9720CE352EFCF6C5680FFC424BD634864902DE0B4BD6D49F4E" + "580230E3AE97D95C8B19442B3C0A10D8F5633FECEDD6926A7F6DAB0DDB7D457F" + "9EA81B8465FCD6FFFEED114011DF91C059CAEDAF97625F6C96ECC74725556934" + "EF781D866B34F011FCE4D835A090196E9A5F0E4449AF7EB697DDB9076494CA5F" + "81104A305B6DD27665722C46B60E5DF680FB16B210607EF217652E60236C255F" + "6A28315F4083A96791D7214BF64C1DF4FD0DB1944FB26A2A57031B32EEE64AD1" + "5A8BA68885CDE74A5BFC920F6ABF59BA5C75506373E7130F9042DA922179251F", + 16 + ), # Modulus + int("010001", 16) # Exponent + ), + + # 847625836280919973 + 0x10bc35f3509f7b7a5 - (1 << 64): PublicKey( # Telegram servers #2 + # -----BEGIN PUBLIC KEY----- + # MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAruw2yP/BCcsJliRoW5eB + # VBVle9dtjJw+OYED160Wybum9SXtBBLXriwt4rROd9csv0t0OHCaTmRqBcQ0J8fx + # hN6/cpR1GWgOZRUAiQxoMnlt0R93LCX/j1dnVa/gVbCjdSxpbrfY2g2L4frzjJvd + # l84Kd9ORYjDEAyFnEA7dD556OptgLQQ2e2iVNq8NZLYTzLp5YpOdO1doK+ttrltg + # gTCy5SrKeLoCPPbOgGsdxJxyz5KKcZnSLj16yE5HvJQn0CNpRdENvRUXe6tBP78O + # 39oJ8BTHp9oIjd6XWXAsp2CvK45Ol8wFXGF710w9lwCGNbmNxNYhtIkdqfsEcwR5 + # JwIDAQAB + # -----END PUBLIC KEY----- + int( + "AEEC36C8FFC109CB099624685B97815415657BD76D8C9C3E398103D7AD16C9BB" + "A6F525ED0412D7AE2C2DE2B44E77D72CBF4B7438709A4E646A05C43427C7F184" + "DEBF72947519680E651500890C6832796DD11F772C25FF8F576755AFE055B0A3" + "752C696EB7D8DA0D8BE1FAF38C9BDD97CE0A77D3916230C4032167100EDD0F9E" + "7A3A9B602D04367B689536AF0D64B613CCBA7962939D3B57682BEB6DAE5B6081" + "30B2E52ACA78BA023CF6CE806B1DC49C72CF928A7199D22E3D7AC84E47BC9427" + "D0236945D10DBD15177BAB413FBF0EDFDA09F014C7A7DA088DDE9759702CA760" + "AF2B8E4E97CC055C617BD74C3D97008635B98DC4D621B4891DA9FB0473047927", + 16 + ), # Modulus + int("010001", 16) # Exponent + ), -class RSA: - # To get modulus and exponent: - # - # [RSA PUBLIC KEY]: - # grep -v -- - public.key | tr -d \\n | base64 -d | openssl asn1parse -inform DER -i - # - # [PUBLIC KEY]: - # openssl rsa -pubin -in key -text -noout + # 1562291298945373506 + 0x115ae5fa8b5529542 - (1 << 64): PublicKey( # Telegram servers #3 + # -----BEGIN PUBLIC KEY----- + # MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvfLHfYH2r9R70w8prHbl + # Wt/nDkh+XkgpflqQVcnAfSuTtO05lNPspQmL8Y2XjVT4t8cT6xAkdgfmmvnvRPOO + # KPi0OfJXoRVylFzAQG/j83u5K3kRLbae7fLccVhKZhY46lvsueI1hQdLgNV9n1cQ + # 3TDS2pQOCtovG4eDl9wacrXOJTG2990VjgnIKNA0UMoP+KF03qzryqIt3oTvZq03 + # DyWdGK+AZjgBLaDKSnC6qD2cFY81UryRWOab8zKkWAnhw2kFpcqhI0jdV5QaSCEx + # vnsjVaX0Y1N0870931/5Jb9ICe4nweZ9kSDF/gip3kWLG0o8XQpChDfyvsqB9OLV + # /wIDAQAB + # -----END PUBLIC KEY----- + int( + "BDF2C77D81F6AFD47BD30F29AC76E55ADFE70E487E5E48297E5A9055C9C07D2B" + "93B4ED3994D3ECA5098BF18D978D54F8B7C713EB10247607E69AF9EF44F38E28" + "F8B439F257A11572945CC0406FE3F37BB92B79112DB69EEDF2DC71584A661638" + "EA5BECB9E23585074B80D57D9F5710DD30D2DA940E0ADA2F1B878397DC1A72B5" + "CE2531B6F7DD158E09C828D03450CA0FF8A174DEACEBCAA22DDE84EF66AD370F" + "259D18AF806638012DA0CA4A70BAA83D9C158F3552BC9158E69BF332A45809E1" + "C36905A5CAA12348DD57941A482131BE7B2355A5F4635374F3BD3DDF5FF925BF" + "4809EE27C1E67D9120C5FE08A9DE458B1B4A3C5D0A428437F2BECA81F4E2D5FF", + 16 + ), # Modulus + int("010001", 16) # Exponent + ), - server_public_keys = { - # -4344800451088585951 - 0xc3b42b026ce86b21 - (1 << 64): PublicKey( # Telegram servers #1 - # -----BEGIN RSA PUBLIC KEY----- - # MIIBCgKCAQEAwVACPi9w23mF3tBkdZz+zwrzKOaaQdr01vAbU4E1pvkfj4sqDsm6 - # lyDONS789sVoD/xCS9Y0hkkC3gtL1tSfTlgCMOOul9lcixlEKzwKENj1Yz/s7daS - # an9tqw3bfUV/nqgbhGX81v/+7RFAEd+RwFnK7a+XYl9sluzHRyVVaTTveB2GazTw - # Efzk2DWgkBluml8OREmvfraX3bkHZJTKX4EQSjBbbdJ2ZXIsRrYOXfaA+xayEGB+ - # 8hdlLmAjbCVfaigxX0CDqWeR1yFL9kwd9P0NsZRPsmoqVwMbMu7mStFai6aIhc3n - # Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB - # -----END RSA PUBLIC KEY----- - int( - "C150023E2F70DB7985DED064759CFECF0AF328E69A41DAF4D6F01B538135A6F9" - "1F8F8B2A0EC9BA9720CE352EFCF6C5680FFC424BD634864902DE0B4BD6D49F4E" - "580230E3AE97D95C8B19442B3C0A10D8F5633FECEDD6926A7F6DAB0DDB7D457F" - "9EA81B8465FCD6FFFEED114011DF91C059CAEDAF97625F6C96ECC74725556934" - "EF781D866B34F011FCE4D835A090196E9A5F0E4449AF7EB697DDB9076494CA5F" - "81104A305B6DD27665722C46B60E5DF680FB16B210607EF217652E60236C255F" - "6A28315F4083A96791D7214BF64C1DF4FD0DB1944FB26A2A57031B32EEE64AD1" - "5A8BA68885CDE74A5BFC920F6ABF59BA5C75506373E7130F9042DA922179251F", - 16 - ), # Modulus - int("010001", 16) # Exponent - ), + # -5859577972006586033 + 0xaeae98e13cd7f94f - (1 << 64): PublicKey( # Telegram servers #4 + # -----BEGIN PUBLIC KEY----- + # MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs/ditzm+mPND6xkhzwFI + # z6J/968CtkcSE/7Z2qAJiXbmZ3UDJPGrzqTDHkO30R8VeRM/Kz2f4nR05GIFiITl + # 4bEjvpy7xqRDspJcCFIOcyXm8abVDhF+th6knSU0yLtNKuQVP6voMrnt9MV1X92L + # GZQLgdHZbPQz0Z5qIpaKhdyA8DEvWWvSUwwc+yi1/gGaybwlzZwqXYoPOhwMebzK + # Uk0xW14htcJrRrq+PXXQbRzTMynseCoPIoke0dtCodbA3qQxQovE16q9zz4Otv2k + # 4j63cz53J+mhkVWAeWxVGI0lltJmWtEYK6er8VqqWot3nqmWMXogrgRLggv/Nbbo + # oQIDAQAB + # -----END PUBLIC KEY----- + int( + "B3F762B739BE98F343EB1921CF0148CFA27FF7AF02B6471213FED9DAA0098976" + "E667750324F1ABCEA4C31E43B7D11F1579133F2B3D9FE27474E462058884E5E1" + "B123BE9CBBC6A443B2925C08520E7325E6F1A6D50E117EB61EA49D2534C8BB4D" + "2AE4153FABE832B9EDF4C5755FDD8B19940B81D1D96CF433D19E6A22968A85DC" + "80F0312F596BD2530C1CFB28B5FE019AC9BC25CD9C2A5D8A0F3A1C0C79BCCA52" + "4D315B5E21B5C26B46BABE3D75D06D1CD33329EC782A0F22891ED1DB42A1D6C0" + "DEA431428BC4D7AABDCF3E0EB6FDA4E23EB7733E7727E9A1915580796C55188D" + "2596D2665AD1182BA7ABF15AAA5A8B779EA996317A20AE044B820BFF35B6E8A1", + 16 + ), # Modulus + int("010001", 16) # Exponent + ), - # 847625836280919973 - 0x10bc35f3509f7b7a5 - (1 << 64): PublicKey( # Telegram servers #2 - # -----BEGIN PUBLIC KEY----- - # MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAruw2yP/BCcsJliRoW5eB - # VBVle9dtjJw+OYED160Wybum9SXtBBLXriwt4rROd9csv0t0OHCaTmRqBcQ0J8fx - # hN6/cpR1GWgOZRUAiQxoMnlt0R93LCX/j1dnVa/gVbCjdSxpbrfY2g2L4frzjJvd - # l84Kd9ORYjDEAyFnEA7dD556OptgLQQ2e2iVNq8NZLYTzLp5YpOdO1doK+ttrltg - # gTCy5SrKeLoCPPbOgGsdxJxyz5KKcZnSLj16yE5HvJQn0CNpRdENvRUXe6tBP78O - # 39oJ8BTHp9oIjd6XWXAsp2CvK45Ol8wFXGF710w9lwCGNbmNxNYhtIkdqfsEcwR5 - # JwIDAQAB - # -----END PUBLIC KEY----- - int( - "AEEC36C8FFC109CB099624685B97815415657BD76D8C9C3E398103D7AD16C9BB" - "A6F525ED0412D7AE2C2DE2B44E77D72CBF4B7438709A4E646A05C43427C7F184" - "DEBF72947519680E651500890C6832796DD11F772C25FF8F576755AFE055B0A3" - "752C696EB7D8DA0D8BE1FAF38C9BDD97CE0A77D3916230C4032167100EDD0F9E" - "7A3A9B602D04367B689536AF0D64B613CCBA7962939D3B57682BEB6DAE5B6081" - "30B2E52ACA78BA023CF6CE806B1DC49C72CF928A7199D22E3D7AC84E47BC9427" - "D0236945D10DBD15177BAB413FBF0EDFDA09F014C7A7DA088DDE9759702CA760" - "AF2B8E4E97CC055C617BD74C3D97008635B98DC4D621B4891DA9FB0473047927", - 16 - ), # Modulus - int("010001", 16) # Exponent - ), + # 6491968696586960280 + 0x15a181b2235057d98 - (1 << 64): PublicKey( # Telegram servers #5 + # -----BEGIN PUBLIC KEY----- + # MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvmpxVY7ld/8DAjz6F6q0 + # 5shjg8/4p6047bn6/m8yPy1RBsvIyvuDuGnP/RzPEhzXQ9UJ5Ynmh2XJZgHoE9xb + # nfxL5BXHplJhMtADXKM9bWB11PU1Eioc3+AXBB8QiNFBn2XI5UkO5hPhbb9mJpjA + # 9Uhw8EdfqJP8QetVsI/xrCEbwEXe0xvifRLJbY08/Gp66KpQvy7g8w7VB8wlgePe + # xW3pT13Ap6vuC+mQuJPyiHvSxjEKHgqePji9NP3tJUFQjcECqcm0yV7/2d0t/pbC + # m+ZH1sadZspQCEPPrtbkQBlvHb4OLiIWPGHKSMeRFvp3IWcmdJqXahxLCUS1Eh6M + # AQIDAQAB + # -----END PUBLIC KEY----- + int( + "BE6A71558EE577FF03023CFA17AAB4E6C86383CFF8A7AD38EDB9FAFE6F323F2D" + "5106CBC8CAFB83B869CFFD1CCF121CD743D509E589E68765C96601E813DC5B9D" + "FC4BE415C7A6526132D0035CA33D6D6075D4F535122A1CDFE017041F1088D141" + "9F65C8E5490EE613E16DBF662698C0F54870F0475FA893FC41EB55B08FF1AC21" + "1BC045DED31BE27D12C96D8D3CFC6A7AE8AA50BF2EE0F30ED507CC2581E3DEC5" + "6DE94F5DC0A7ABEE0BE990B893F2887BD2C6310A1E0A9E3E38BD34FDED254150" + "8DC102A9C9B4C95EFFD9DD2DFE96C29BE647D6C69D66CA500843CFAED6E44019" + "6F1DBE0E2E22163C61CA48C79116FA77216726749A976A1C4B0944B5121E8C01", + 16 + ), # Modulus + int("010001", 16) # Exponent + ), - # 1562291298945373506 - 0x115ae5fa8b5529542 - (1 << 64): PublicKey( # Telegram servers #3 - # -----BEGIN PUBLIC KEY----- - # MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvfLHfYH2r9R70w8prHbl - # Wt/nDkh+XkgpflqQVcnAfSuTtO05lNPspQmL8Y2XjVT4t8cT6xAkdgfmmvnvRPOO - # KPi0OfJXoRVylFzAQG/j83u5K3kRLbae7fLccVhKZhY46lvsueI1hQdLgNV9n1cQ - # 3TDS2pQOCtovG4eDl9wacrXOJTG2990VjgnIKNA0UMoP+KF03qzryqIt3oTvZq03 - # DyWdGK+AZjgBLaDKSnC6qD2cFY81UryRWOab8zKkWAnhw2kFpcqhI0jdV5QaSCEx - # vnsjVaX0Y1N0870931/5Jb9ICe4nweZ9kSDF/gip3kWLG0o8XQpChDfyvsqB9OLV - # /wIDAQAB - # -----END PUBLIC KEY----- - int( - "BDF2C77D81F6AFD47BD30F29AC76E55ADFE70E487E5E48297E5A9055C9C07D2B" - "93B4ED3994D3ECA5098BF18D978D54F8B7C713EB10247607E69AF9EF44F38E28" - "F8B439F257A11572945CC0406FE3F37BB92B79112DB69EEDF2DC71584A661638" - "EA5BECB9E23585074B80D57D9F5710DD30D2DA940E0ADA2F1B878397DC1A72B5" - "CE2531B6F7DD158E09C828D03450CA0FF8A174DEACEBCAA22DDE84EF66AD370F" - "259D18AF806638012DA0CA4A70BAA83D9C158F3552BC9158E69BF332A45809E1" - "C36905A5CAA12348DD57941A482131BE7B2355A5F4635374F3BD3DDF5FF925BF" - "4809EE27C1E67D9120C5FE08A9DE458B1B4A3C5D0A428437F2BECA81F4E2D5FF", - 16 - ), # Modulus - int("010001", 16) # Exponent - ), + # -7395192255793472640 + 0x995effd323b5db80 - (1 << 64): PublicKey( # CDN DC-121 + # -----BEGIN RSA PUBLIC KEY----- + # MIIBCgKCAQEA4tWHcGJlElkxuxKQJwFjJaulmVHgdxNA3wgI2E8XbNnA88y51Xog + # V5m8BEYuTSP4llXZY4ZSJW5VlFXnmsJT/hmjyeFqqTajyAW6nb9vwZX291QvqD/1 + # ZCFBy7TLvCM0lbNIEhcLMf33ZV8AetLAd+uRLF6QHosys5w0iJ7x+UbGwDxyfeic + # 8EJJnsKaXrUOwRycMRN+V/zDySa0EYl1u1EB1MDX1/jIV1IQEbLvdBH4vsVTVEdW + # KHlzOcFzT9qX/g8XibCPiHLJvqQb8hVibvs9NaANyClcBEt3mOucG1/46Lilkc/K + # d4nlCcohk0jIHNp8symUzNWRPUGmTs3SPwIDAQAB + # -----END RSA PUBLIC KEY----- + int( + "E2D587706265125931BB129027016325ABA59951E0771340DF0808D84F176CD9" + "C0F3CCB9D57A205799BC04462E4D23F89655D9638652256E559455E79AC253FE" + "19A3C9E16AA936A3C805BA9DBF6FC195F6F7542FA83FF5642141CBB4CBBC2334" + "95B34812170B31FDF7655F007AD2C077EB912C5E901E8B32B39C34889EF1F946" + "C6C03C727DE89CF042499EC29A5EB50EC11C9C31137E57FCC3C926B4118975BB" + "5101D4C0D7D7F8C857521011B2EF7411F8BEC55354475628797339C1734FDA97" + "FE0F1789B08F8872C9BEA41BF215626EFB3D35A00DC8295C044B7798EB9C1B5F" + "F8E8B8A591CFCA7789E509CA219348C81CDA7CB32994CCD5913D41A64ECDD23F", + 16 + ), # Modulus + int("010001", 16) # Exponent + ), - # -5859577972006586033 - 0xaeae98e13cd7f94f - (1 << 64): PublicKey( # Telegram servers #4 - # -----BEGIN PUBLIC KEY----- - # MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs/ditzm+mPND6xkhzwFI - # z6J/968CtkcSE/7Z2qAJiXbmZ3UDJPGrzqTDHkO30R8VeRM/Kz2f4nR05GIFiITl - # 4bEjvpy7xqRDspJcCFIOcyXm8abVDhF+th6knSU0yLtNKuQVP6voMrnt9MV1X92L - # GZQLgdHZbPQz0Z5qIpaKhdyA8DEvWWvSUwwc+yi1/gGaybwlzZwqXYoPOhwMebzK - # Uk0xW14htcJrRrq+PXXQbRzTMynseCoPIoke0dtCodbA3qQxQovE16q9zz4Otv2k - # 4j63cz53J+mhkVWAeWxVGI0lltJmWtEYK6er8VqqWot3nqmWMXogrgRLggv/Nbbo - # oQIDAQAB - # -----END PUBLIC KEY----- - int( - "B3F762B739BE98F343EB1921CF0148CFA27FF7AF02B6471213FED9DAA0098976" - "E667750324F1ABCEA4C31E43B7D11F1579133F2B3D9FE27474E462058884E5E1" - "B123BE9CBBC6A443B2925C08520E7325E6F1A6D50E117EB61EA49D2534C8BB4D" - "2AE4153FABE832B9EDF4C5755FDD8B19940B81D1D96CF433D19E6A22968A85DC" - "80F0312F596BD2530C1CFB28B5FE019AC9BC25CD9C2A5D8A0F3A1C0C79BCCA52" - "4D315B5E21B5C26B46BABE3D75D06D1CD33329EC782A0F22891ED1DB42A1D6C0" - "DEA431428BC4D7AABDCF3E0EB6FDA4E23EB7733E7727E9A1915580796C55188D" - "2596D2665AD1182BA7ABF15AAA5A8B779EA996317A20AE044B820BFF35B6E8A1", - 16 - ), # Modulus - int("010001", 16) # Exponent - ), + # 2685959930972952888 + 0x1254672538e935938 - (1 << 64): PublicKey( # CDN DC-140 + # -----BEGIN RSA PUBLIC KEY----- + # MIIBCgKCAQEAzuHVC7sE50Kho/yDVZtWnlmA5Bf/aM8KZY3WzS16w6w1sBqipj8o + # gMGG7ULbGBtYmKEaI7IIJO6WM2m1MaXVnsqS8d7PaGAZiy8rSN3S7S2a8wp4RXZe + # hs0JAXvZeIz45iByCMBfycbJKmSweYkesRUI7hUO8eQhmm/UYUEpJY7VOt0Iemiu + # URSpqlRQ2FlcyHahYUNcvbICb4+/AP7coKBn6cB5FyzM7MCcKxbEKOx3Y3MUnbZq + # q5pN6/eRazkegyrlp4kuJ94KsbRFHFX5Dx8uzjrO9wi8LF7gIgZu5DRMcmjXJKq6 + # rGZ2Z9cnrD8pVu1L2vcInd4K6ximZS2hbwIDAQAB + # -----END RSA PUBLIC KEY----- + int( + "CEE1D50BBB04E742A1A3FC83559B569E5980E417FF68CF0A658DD6CD2D7AC3AC" + "35B01AA2A63F2880C186ED42DB181B5898A11A23B20824EE963369B531A5D59E" + "CA92F1DECF6860198B2F2B48DDD2ED2D9AF30A7845765E86CD09017BD9788CF8" + "E6207208C05FC9C6C92A64B079891EB11508EE150EF1E4219A6FD4614129258E" + "D53ADD087A68AE5114A9AA5450D8595CC876A161435CBDB2026F8FBF00FEDCA0" + "A067E9C079172CCCECC09C2B16C428EC776373149DB66AAB9A4DEBF7916B391E" + "832AE5A7892E27DE0AB1B4451C55F90F1F2ECE3ACEF708BC2C5EE022066EE434" + "4C7268D724AABAAC667667D727AC3F2956ED4BDAF7089DDE0AEB18A6652DA16F", + 16 + ), # Modulus + int("010001", 16) # Exponent + ), - # 6491968696586960280 - 0x15a181b2235057d98 - (1 << 64): PublicKey( # Telegram servers #5 - # -----BEGIN PUBLIC KEY----- - # MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvmpxVY7ld/8DAjz6F6q0 - # 5shjg8/4p6047bn6/m8yPy1RBsvIyvuDuGnP/RzPEhzXQ9UJ5Ynmh2XJZgHoE9xb - # nfxL5BXHplJhMtADXKM9bWB11PU1Eioc3+AXBB8QiNFBn2XI5UkO5hPhbb9mJpjA - # 9Uhw8EdfqJP8QetVsI/xrCEbwEXe0xvifRLJbY08/Gp66KpQvy7g8w7VB8wlgePe - # xW3pT13Ap6vuC+mQuJPyiHvSxjEKHgqePji9NP3tJUFQjcECqcm0yV7/2d0t/pbC - # m+ZH1sadZspQCEPPrtbkQBlvHb4OLiIWPGHKSMeRFvp3IWcmdJqXahxLCUS1Eh6M - # AQIDAQAB - # -----END PUBLIC KEY----- - int( - "BE6A71558EE577FF03023CFA17AAB4E6C86383CFF8A7AD38EDB9FAFE6F323F2D" - "5106CBC8CAFB83B869CFFD1CCF121CD743D509E589E68765C96601E813DC5B9D" - "FC4BE415C7A6526132D0035CA33D6D6075D4F535122A1CDFE017041F1088D141" - "9F65C8E5490EE613E16DBF662698C0F54870F0475FA893FC41EB55B08FF1AC21" - "1BC045DED31BE27D12C96D8D3CFC6A7AE8AA50BF2EE0F30ED507CC2581E3DEC5" - "6DE94F5DC0A7ABEE0BE990B893F2887BD2C6310A1E0A9E3E38BD34FDED254150" - "8DC102A9C9B4C95EFFD9DD2DFE96C29BE647D6C69D66CA500843CFAED6E44019" - "6F1DBE0E2E22163C61CA48C79116FA77216726749A976A1C4B0944B5121E8C01", - 16 - ), # Modulus - int("010001", 16) # Exponent - ), + # -3997872768018684475 + 0xc884b3e62d09e5c5 - (1 << 64): PublicKey( # CDN DC-201 + # -----BEGIN RSA PUBLIC KEY----- + # MIIBCgKCAQEAug6fETVb7NkXYYu5ueZuM0pqw1heuqUrZNYomQN0lS0o7i6mAWwb + # 1/FiscFK+y4LQSSEx+oUzXAhjmll9fmb4e7PbUiXo8MuXO0Rj3e5416DXfTiOYGW + # XlFRV0aQzu8agy1epKwkFDidnmy7g5rJJV0q1+3eR+Jk2OEc/B6lMAOv3fBU6xhE + # ZByN9gqc6fvkNo13PQ8JYZUSGttzLlYy76uFmvFBhRsJU+LNQ2+bsTHwafSffVYl + # Z2boJOblvqbRWe453CzssaSWywGXOQmWvVbEe7F8q1ki/s7S8BxYWrhSLJ6bsu9V + # ZWnIHD9vB34QF8IABPRE93mhCOHBqJxSBQIDAQAB + # -----END RSA PUBLIC KEY----- + int( + "BA0E9F11355BECD917618BB9B9E66E334A6AC3585EBAA52B64D628990374952D" + "28EE2EA6016C1BD7F162B1C14AFB2E0B412484C7EA14CD70218E6965F5F99BE1" + "EECF6D4897A3C32E5CED118F77B9E35E835DF4E23981965E5151574690CEEF1A" + "832D5EA4AC2414389D9E6CBB839AC9255D2AD7EDDE47E264D8E11CFC1EA53003" + "AFDDF054EB1844641C8DF60A9CE9FBE4368D773D0F096195121ADB732E5632EF" + "AB859AF141851B0953E2CD436F9BB131F069F49F7D56256766E824E6E5BEA6D1" + "59EE39DC2CECB1A496CB0197390996BD56C47BB17CAB5922FECED2F01C585AB8" + "522C9E9BB2EF556569C81C3F6F077E1017C20004F444F779A108E1C1A89C5205", + 16 + ), # Modulus + int("010001", 16) # Exponent + ), - # 6427105915145367799 - 0x15931aac70e0d30f7 - (1 << 64): PublicKey( # CDN DC-121 - # -----BEGIN RSA PUBLIC KEY----- - # MIIBCgKCAQEA+Lf3PvgE1yxbJUCMaEAkV0QySTVpnaDjiednB5RbtNWjCeqSVakY - # HbqqGMIIv5WCGdFdrqOfMNcNSstPtSU6R9UmRw6tquOIykpSuUOje9H+4XVIKquj - # yL2ISdK+4ZOMl4hCMkqauw4bP1Sbr03vZRQbU6qEA04V4j879BAyBVhr3WG9+Zi+ - # t5XfGSTgSExPYEl8rZNHYNV5RB+BuroVH2HLTOpT/mJVfikYpgjfWF5ldezV4Wo9 - # LSH0cZGSFIaeJl8d0A8Eiy5B9gtBO8mL+XfQRKOOmr7a4BM4Ro2de5rr2i2od7hY - # Xd3DO9FRSl4y1zA8Am48Rfd95WHF3N/OmQIDAQAB - # -----END RSA PUBLIC KEY----- - int( - "F8B7F73EF804D72C5B25408C6840245744324935699DA0E389E76707945BB4D5" - "A309EA9255A9181DBAAA18C208BF958219D15DAEA39F30D70D4ACB4FB5253A47" - "D526470EADAAE388CA4A52B943A37BD1FEE175482AABA3C8BD8849D2BEE1938C" - "978842324A9ABB0E1B3F549BAF4DEF65141B53AA84034E15E23F3BF410320558" - "6BDD61BDF998BEB795DF1924E0484C4F60497CAD934760D579441F81BABA151F" - "61CB4CEA53FE62557E2918A608DF585E6575ECD5E16A3D2D21F471919214869E" - "265F1DD00F048B2E41F60B413BC98BF977D044A38E9ABEDAE01338468D9D7B9A" - "EBDA2DA877B8585DDDC33BD1514A5E32D7303C026E3C45F77DE561C5DCDFCE99", - 16 - ), # Modulus - int("010001", 16) # Exponent - ), + # -4960899639492471258 + 0xbb27580fd5b01626 - (1 << 64): PublicKey( # CDN DC-203 + # -----BEGIN RSA PUBLIC KEY----- + # MIIBCgKCAQEAv/L6td+mj7Dl81NHfu+Xf1KNtvZPR1tS5xFqkiUson1u7D2ulK05 + # jM8HKvpV1o+1HPPqhaXhasvsX90u3TIHRQ0zuJKJxKAiZo3GK7phHozjAJ9VUFbO + # 7jKAa5BTE9tXgA5ZwJAiQWb3U6ykwRzk3fFRe5WaW7xfVUiepxyWGdr1eecoWCfB + # af1TCXfcS7vcyljNT03pwt2YyS5iXE5IB5wBB5yqSSm4GYtWWR67UjIsXBd77TRp + # foLGpfOdUHxBz4ZSj8D76m1zlpID5J2pF6bH4+ZCz0SUpv3j7bE8WFlvgMfwEPhw + # xMYidRGayq9YlLlYd4D+Yoq0U6jS3MWTRQIDAQAB + # -----END RSA PUBLIC KEY----- + int( + "BFF2FAB5DFA68FB0E5F353477EEF977F528DB6F64F475B52E7116A92252CA27D" + "6EEC3DAE94AD398CCF072AFA55D68FB51CF3EA85A5E16ACBEC5FDD2EDD320745" + "0D33B89289C4A022668DC62BBA611E8CE3009F555056CEEE32806B905313DB57" + "800E59C090224166F753ACA4C11CE4DDF1517B959A5BBC5F55489EA71C9619DA" + "F579E7285827C169FD530977DC4BBBDCCA58CD4F4DE9C2DD98C92E625C4E4807" + "9C01079CAA4929B8198B56591EBB52322C5C177BED34697E82C6A5F39D507C41" + "CF86528FC0FBEA6D73969203E49DA917A6C7E3E642CF4494A6FDE3EDB13C5859" + "6F80C7F010F870C4C62275119ACAAF5894B9587780FE628AB453A8D2DCC59345", + 16 + ), # Modulus + int("010001", 16) # Exponent + ) +} - # 2685959930972952888 - 0x1254672538e935938 - (1 << 64): PublicKey( # CDN DC-140 - # -----BEGIN RSA PUBLIC KEY----- - # MIIBCgKCAQEAzuHVC7sE50Kho/yDVZtWnlmA5Bf/aM8KZY3WzS16w6w1sBqipj8o - # gMGG7ULbGBtYmKEaI7IIJO6WM2m1MaXVnsqS8d7PaGAZiy8rSN3S7S2a8wp4RXZe - # hs0JAXvZeIz45iByCMBfycbJKmSweYkesRUI7hUO8eQhmm/UYUEpJY7VOt0Iemiu - # URSpqlRQ2FlcyHahYUNcvbICb4+/AP7coKBn6cB5FyzM7MCcKxbEKOx3Y3MUnbZq - # q5pN6/eRazkegyrlp4kuJ94KsbRFHFX5Dx8uzjrO9wi8LF7gIgZu5DRMcmjXJKq6 - # rGZ2Z9cnrD8pVu1L2vcInd4K6ximZS2hbwIDAQAB - # -----END RSA PUBLIC KEY----- - int( - "CEE1D50BBB04E742A1A3FC83559B569E5980E417FF68CF0A658DD6CD2D7AC3AC" - "35B01AA2A63F2880C186ED42DB181B5898A11A23B20824EE963369B531A5D59E" - "CA92F1DECF6860198B2F2B48DDD2ED2D9AF30A7845765E86CD09017BD9788CF8" - "E6207208C05FC9C6C92A64B079891EB11508EE150EF1E4219A6FD4614129258E" - "D53ADD087A68AE5114A9AA5450D8595CC876A161435CBDB2026F8FBF00FEDCA0" - "A067E9C079172CCCECC09C2B16C428EC776373149DB66AAB9A4DEBF7916B391E" - "832AE5A7892E27DE0AB1B4451C55F90F1F2ECE3ACEF708BC2C5EE022066EE434" - "4C7268D724AABAAC667667D727AC3F2956ED4BDAF7089DDE0AEB18A6652DA16F", - 16 - ), # Modulus - int("010001", 16) # Exponent - ) - } - @classmethod - def encrypt(cls, data: bytes, fingerprint: int) -> bytes: - return pow( - int.from_bytes(data, "big"), - cls.server_public_keys[fingerprint].e, - cls.server_public_keys[fingerprint].m - ).to_bytes(256, "big") +def encrypt(data: bytes, fingerprint: int) -> bytes: + return pow( + int.from_bytes(data, "big"), + server_public_keys[fingerprint].e, + server_public_keys[fingerprint].m + ).to_bytes(256, "big") diff --git a/pyrogram/dispatcher.py b/pyrogram/dispatcher.py new file mode 100644 index 00000000000..6e503cebb3a --- /dev/null +++ b/pyrogram/dispatcher.py @@ -0,0 +1,259 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import asyncio +import inspect +import logging +from collections import OrderedDict + +import pyrogram +from pyrogram import utils +from pyrogram.handlers import ( + CallbackQueryHandler, MessageHandler, EditedMessageHandler, DeletedMessagesHandler, + UserStatusHandler, RawUpdateHandler, InlineQueryHandler, PollHandler, + ChosenInlineResultHandler, ChatMemberUpdatedHandler, ChatJoinRequestHandler +) +from pyrogram.raw.types import ( + UpdateNewMessage, UpdateNewChannelMessage, UpdateNewScheduledMessage, + UpdateEditMessage, UpdateEditChannelMessage, + UpdateDeleteMessages, UpdateDeleteChannelMessages, + UpdateBotCallbackQuery, UpdateInlineBotCallbackQuery, + UpdateUserStatus, UpdateBotInlineQuery, UpdateMessagePoll, + UpdateBotInlineSend, UpdateChatParticipant, UpdateChannelParticipant, + UpdateBotChatInviteRequester +) + +log = logging.getLogger(__name__) + + +class Dispatcher: + NEW_MESSAGE_UPDATES = (UpdateNewMessage, UpdateNewChannelMessage, UpdateNewScheduledMessage) + EDIT_MESSAGE_UPDATES = (UpdateEditMessage, UpdateEditChannelMessage) + DELETE_MESSAGES_UPDATES = (UpdateDeleteMessages, UpdateDeleteChannelMessages) + CALLBACK_QUERY_UPDATES = (UpdateBotCallbackQuery, UpdateInlineBotCallbackQuery) + CHAT_MEMBER_UPDATES = (UpdateChatParticipant, UpdateChannelParticipant) + USER_STATUS_UPDATES = (UpdateUserStatus,) + BOT_INLINE_QUERY_UPDATES = (UpdateBotInlineQuery,) + POLL_UPDATES = (UpdateMessagePoll,) + CHOSEN_INLINE_RESULT_UPDATES = (UpdateBotInlineSend,) + CHAT_JOIN_REQUEST_UPDATES = (UpdateBotChatInviteRequester,) + + def __init__(self, client: "pyrogram.Client"): + self.client = client + self.loop = asyncio.get_event_loop() + + self.handler_worker_tasks = [] + self.locks_list = [] + + self.updates_queue = asyncio.Queue() + self.groups = OrderedDict() + + async def message_parser(update, users, chats): + return ( + await pyrogram.types.Message._parse(self.client, update.message, users, chats, + isinstance(update, UpdateNewScheduledMessage)), + MessageHandler + ) + + async def edited_message_parser(update, users, chats): + # Edited messages are parsed the same way as new messages, but the handler is different + parsed, _ = await message_parser(update, users, chats) + + return ( + parsed, + EditedMessageHandler + ) + + async def deleted_messages_parser(update, users, chats): + return ( + utils.parse_deleted_messages(self.client, update), + DeletedMessagesHandler + ) + + async def callback_query_parser(update, users, chats): + return ( + await pyrogram.types.CallbackQuery._parse(self.client, update, users), + CallbackQueryHandler + ) + + async def user_status_parser(update, users, chats): + return ( + pyrogram.types.User._parse_user_status(self.client, update), + UserStatusHandler + ) + + async def inline_query_parser(update, users, chats): + return ( + pyrogram.types.InlineQuery._parse(self.client, update, users), + InlineQueryHandler + ) + + async def poll_parser(update, users, chats): + return ( + pyrogram.types.Poll._parse_update(self.client, update), + PollHandler + ) + + async def chosen_inline_result_parser(update, users, chats): + return ( + pyrogram.types.ChosenInlineResult._parse(self.client, update, users), + ChosenInlineResultHandler + ) + + async def chat_member_updated_parser(update, users, chats): + return ( + pyrogram.types.ChatMemberUpdated._parse(self.client, update, users, chats), + ChatMemberUpdatedHandler + ) + + async def chat_join_request_parser(update, users, chats): + return ( + pyrogram.types.ChatJoinRequest._parse(self.client, update, users, chats), + ChatJoinRequestHandler + ) + + self.update_parsers = { + Dispatcher.NEW_MESSAGE_UPDATES: message_parser, + Dispatcher.EDIT_MESSAGE_UPDATES: edited_message_parser, + Dispatcher.DELETE_MESSAGES_UPDATES: deleted_messages_parser, + Dispatcher.CALLBACK_QUERY_UPDATES: callback_query_parser, + Dispatcher.USER_STATUS_UPDATES: user_status_parser, + Dispatcher.BOT_INLINE_QUERY_UPDATES: inline_query_parser, + Dispatcher.POLL_UPDATES: poll_parser, + Dispatcher.CHOSEN_INLINE_RESULT_UPDATES: chosen_inline_result_parser, + Dispatcher.CHAT_MEMBER_UPDATES: chat_member_updated_parser, + Dispatcher.CHAT_JOIN_REQUEST_UPDATES: chat_join_request_parser + } + + self.update_parsers = {key: value for key_tuple, value in self.update_parsers.items() for key in key_tuple} + + async def start(self): + if not self.client.no_updates: + for i in range(self.client.workers): + self.locks_list.append(asyncio.Lock()) + + self.handler_worker_tasks.append( + self.loop.create_task(self.handler_worker(self.locks_list[-1])) + ) + + log.info("Started %s HandlerTasks", self.client.workers) + + async def stop(self): + if not self.client.no_updates: + for i in range(self.client.workers): + self.updates_queue.put_nowait(None) + + for i in self.handler_worker_tasks: + await i + + self.handler_worker_tasks.clear() + self.groups.clear() + + log.info("Stopped %s HandlerTasks", self.client.workers) + + def add_handler(self, handler, group: int): + async def fn(): + for lock in self.locks_list: + await lock.acquire() + + try: + if group not in self.groups: + self.groups[group] = [] + self.groups = OrderedDict(sorted(self.groups.items())) + + self.groups[group].append(handler) + finally: + for lock in self.locks_list: + lock.release() + + self.loop.create_task(fn()) + + def remove_handler(self, handler, group: int): + async def fn(): + for lock in self.locks_list: + await lock.acquire() + + try: + if group not in self.groups: + raise ValueError(f"Group {group} does not exist. Handler was not removed.") + + self.groups[group].remove(handler) + finally: + for lock in self.locks_list: + lock.release() + + self.loop.create_task(fn()) + + async def handler_worker(self, lock): + while True: + packet = await self.updates_queue.get() + + if packet is None: + break + + try: + update, users, chats = packet + parser = self.update_parsers.get(type(update), None) + + parsed_update, handler_type = ( + await parser(update, users, chats) + if parser is not None + else (None, type(None)) + ) + + async with lock: + for group in self.groups.values(): + for handler in group: + args = None + + if isinstance(handler, handler_type): + try: + if await handler.check(self.client, parsed_update): + args = (parsed_update,) + except Exception as e: + log.exception(e) + continue + + elif isinstance(handler, RawUpdateHandler): + args = (update, users, chats) + + if args is None: + continue + + try: + if inspect.iscoroutinefunction(handler.callback): + await handler.callback(self.client, *args) + else: + await self.loop.run_in_executor( + self.client.executor, + handler.callback, + self.client, + *args + ) + except pyrogram.StopPropagation: + raise + except pyrogram.ContinuePropagation: + continue + except Exception as e: + log.exception(e) + + break + except pyrogram.StopPropagation: + pass + except Exception as e: + log.exception(e) diff --git a/pyrogram/emoji.py b/pyrogram/emoji.py new file mode 100644 index 00000000000..d135faf74d1 --- /dev/null +++ b/pyrogram/emoji.py @@ -0,0 +1,4019 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +GRINNING_FACE = "\U0001f600" +GRINNING_FACE_WITH_BIG_EYES = "\U0001f603" +GRINNING_FACE_WITH_SMILING_EYES = "\U0001f604" +BEAMING_FACE_WITH_SMILING_EYES = "\U0001f601" +GRINNING_SQUINTING_FACE = "\U0001f606" +GRINNING_FACE_WITH_SWEAT = "\U0001f605" +ROLLING_ON_THE_FLOOR_LAUGHING = "\U0001f923" +FACE_WITH_TEARS_OF_JOY = "\U0001f602" +SLIGHTLY_SMILING_FACE = "\U0001f642" +UPSIDE_DOWN_FACE = "\U0001f643" +MELTING_FACE = "\U0001fae0" +WINKING_FACE = "\U0001f609" +SMILING_FACE_WITH_SMILING_EYES = "\U0001f60a" +SMILING_FACE_WITH_HALO = "\U0001f607" +SMILING_FACE_WITH_HEARTS = "\U0001f970" +SMILING_FACE_WITH_HEART_EYES = "\U0001f60d" +STAR_STRUCK = "\U0001f929" +FACE_BLOWING_A_KISS = "\U0001f618" +KISSING_FACE = "\U0001f617" +SMILING_FACE = "\u263a\ufe0f" +KISSING_FACE_WITH_CLOSED_EYES = "\U0001f61a" +KISSING_FACE_WITH_SMILING_EYES = "\U0001f619" +SMILING_FACE_WITH_TEAR = "\U0001f972" +FACE_SAVORING_FOOD = "\U0001f60b" +FACE_WITH_TONGUE = "\U0001f61b" +WINKING_FACE_WITH_TONGUE = "\U0001f61c" +ZANY_FACE = "\U0001f92a" +SQUINTING_FACE_WITH_TONGUE = "\U0001f61d" +MONEY_MOUTH_FACE = "\U0001f911" +SMILING_FACE_WITH_OPEN_HANDS = "\U0001f917" +FACE_WITH_HAND_OVER_MOUTH = "\U0001f92d" +FACE_WITH_OPEN_EYES_AND_HAND_OVER_MOUTH = "\U0001fae2" +FACE_WITH_PEEKING_EYE = "\U0001fae3" +SHUSHING_FACE = "\U0001f92b" +THINKING_FACE = "\U0001f914" +SALUTING_FACE = "\U0001fae1" +ZIPPER_MOUTH_FACE = "\U0001f910" +FACE_WITH_RAISED_EYEBROW = "\U0001f928" +NEUTRAL_FACE = "\U0001f610" +EXPRESSIONLESS_FACE = "\U0001f611" +FACE_WITHOUT_MOUTH = "\U0001f636" +DOTTED_LINE_FACE = "\U0001fae5" +FACE_IN_CLOUDS = "\U0001f636\u200d\U0001f32b\ufe0f" +SMIRKING_FACE = "\U0001f60f" +UNAMUSED_FACE = "\U0001f612" +FACE_WITH_ROLLING_EYES = "\U0001f644" +GRIMACING_FACE = "\U0001f62c" +FACE_EXHALING = "\U0001f62e\u200d\U0001f4a8" +LYING_FACE = "\U0001f925" +RELIEVED_FACE = "\U0001f60c" +PENSIVE_FACE = "\U0001f614" +SLEEPY_FACE = "\U0001f62a" +DROOLING_FACE = "\U0001f924" +SLEEPING_FACE = "\U0001f634" +FACE_WITH_MEDICAL_MASK = "\U0001f637" +FACE_WITH_THERMOMETER = "\U0001f912" +FACE_WITH_HEAD_BANDAGE = "\U0001f915" +NAUSEATED_FACE = "\U0001f922" +FACE_VOMITING = "\U0001f92e" +SNEEZING_FACE = "\U0001f927" +HOT_FACE = "\U0001f975" +COLD_FACE = "\U0001f976" +WOOZY_FACE = "\U0001f974" +FACE_WITH_CROSSED_OUT_EYES = "\U0001f635" +FACE_WITH_SPIRAL_EYES = "\U0001f635\u200d\U0001f4ab" +EXPLODING_HEAD = "\U0001f92f" +COWBOY_HAT_FACE = "\U0001f920" +PARTYING_FACE = "\U0001f973" +DISGUISED_FACE = "\U0001f978" +SMILING_FACE_WITH_SUNGLASSES = "\U0001f60e" +NERD_FACE = "\U0001f913" +FACE_WITH_MONOCLE = "\U0001f9d0" +CONFUSED_FACE = "\U0001f615" +FACE_WITH_DIAGONAL_MOUTH = "\U0001fae4" +WORRIED_FACE = "\U0001f61f" +SLIGHTLY_FROWNING_FACE = "\U0001f641" +FROWNING_FACE = "\u2639\ufe0f" +FACE_WITH_OPEN_MOUTH = "\U0001f62e" +HUSHED_FACE = "\U0001f62f" +ASTONISHED_FACE = "\U0001f632" +FLUSHED_FACE = "\U0001f633" +PLEADING_FACE = "\U0001f97a" +FACE_HOLDING_BACK_TEARS = "\U0001f979" +FROWNING_FACE_WITH_OPEN_MOUTH = "\U0001f626" +ANGUISHED_FACE = "\U0001f627" +FEARFUL_FACE = "\U0001f628" +ANXIOUS_FACE_WITH_SWEAT = "\U0001f630" +SAD_BUT_RELIEVED_FACE = "\U0001f625" +CRYING_FACE = "\U0001f622" +LOUDLY_CRYING_FACE = "\U0001f62d" +FACE_SCREAMING_IN_FEAR = "\U0001f631" +CONFOUNDED_FACE = "\U0001f616" +PERSEVERING_FACE = "\U0001f623" +DISAPPOINTED_FACE = "\U0001f61e" +DOWNCAST_FACE_WITH_SWEAT = "\U0001f613" +WEARY_FACE = "\U0001f629" +TIRED_FACE = "\U0001f62b" +YAWNING_FACE = "\U0001f971" +FACE_WITH_STEAM_FROM_NOSE = "\U0001f624" +ENRAGED_FACE = "\U0001f621" +ANGRY_FACE = "\U0001f620" +FACE_WITH_SYMBOLS_ON_MOUTH = "\U0001f92c" +SMILING_FACE_WITH_HORNS = "\U0001f608" +ANGRY_FACE_WITH_HORNS = "\U0001f47f" +SKULL = "\U0001f480" +SKULL_AND_CROSSBONES = "\u2620\ufe0f" +PILE_OF_POO = "\U0001f4a9" +CLOWN_FACE = "\U0001f921" +OGRE = "\U0001f479" +GOBLIN = "\U0001f47a" +GHOST = "\U0001f47b" +ALIEN = "\U0001f47d" +ALIEN_MONSTER = "\U0001f47e" +ROBOT = "\U0001f916" +GRINNING_CAT = "\U0001f63a" +GRINNING_CAT_WITH_SMILING_EYES = "\U0001f638" +CAT_WITH_TEARS_OF_JOY = "\U0001f639" +SMILING_CAT_WITH_HEART_EYES = "\U0001f63b" +CAT_WITH_WRY_SMILE = "\U0001f63c" +KISSING_CAT = "\U0001f63d" +WEARY_CAT = "\U0001f640" +CRYING_CAT = "\U0001f63f" +POUTING_CAT = "\U0001f63e" +SEE_NO_EVIL_MONKEY = "\U0001f648" +HEAR_NO_EVIL_MONKEY = "\U0001f649" +SPEAK_NO_EVIL_MONKEY = "\U0001f64a" +KISS_MARK = "\U0001f48b" +LOVE_LETTER = "\U0001f48c" +HEART_WITH_ARROW = "\U0001f498" +HEART_WITH_RIBBON = "\U0001f49d" +SPARKLING_HEART = "\U0001f496" +GROWING_HEART = "\U0001f497" +BEATING_HEART = "\U0001f493" +REVOLVING_HEARTS = "\U0001f49e" +TWO_HEARTS = "\U0001f495" +HEART_DECORATION = "\U0001f49f" +HEART_EXCLAMATION = "\u2763\ufe0f" +BROKEN_HEART = "\U0001f494" +HEART_ON_FIRE = "\u2764\ufe0f\u200d\U0001f525" +MENDING_HEART = "\u2764\ufe0f\u200d\U0001fa79" +RED_HEART = "\u2764\ufe0f" +ORANGE_HEART = "\U0001f9e1" +YELLOW_HEART = "\U0001f49b" +GREEN_HEART = "\U0001f49a" +BLUE_HEART = "\U0001f499" +PURPLE_HEART = "\U0001f49c" +BROWN_HEART = "\U0001f90e" +BLACK_HEART = "\U0001f5a4" +WHITE_HEART = "\U0001f90d" +HUNDRED_POINTS = "\U0001f4af" +ANGER_SYMBOL = "\U0001f4a2" +COLLISION = "\U0001f4a5" +DIZZY = "\U0001f4ab" +SWEAT_DROPLETS = "\U0001f4a6" +DASHING_AWAY = "\U0001f4a8" +HOLE = "\U0001f573\ufe0f" +BOMB = "\U0001f4a3" +SPEECH_BALLOON = "\U0001f4ac" +EYE_IN_SPEECH_BUBBLE = "\U0001f441\ufe0f\u200d\U0001f5e8\ufe0f" +LEFT_SPEECH_BUBBLE = "\U0001f5e8\ufe0f" +RIGHT_ANGER_BUBBLE = "\U0001f5ef\ufe0f" +THOUGHT_BALLOON = "\U0001f4ad" +ZZZ = "\U0001f4a4" +WAVING_HAND = "\U0001f44b" +WAVING_HAND_LIGHT_SKIN_TONE = "\U0001f44b\U0001f3fb" +WAVING_HAND_MEDIUM_LIGHT_SKIN_TONE = "\U0001f44b\U0001f3fc" +WAVING_HAND_MEDIUM_SKIN_TONE = "\U0001f44b\U0001f3fd" +WAVING_HAND_MEDIUM_DARK_SKIN_TONE = "\U0001f44b\U0001f3fe" +WAVING_HAND_DARK_SKIN_TONE = "\U0001f44b\U0001f3ff" +RAISED_BACK_OF_HAND = "\U0001f91a" +RAISED_BACK_OF_HAND_LIGHT_SKIN_TONE = "\U0001f91a\U0001f3fb" +RAISED_BACK_OF_HAND_MEDIUM_LIGHT_SKIN_TONE = "\U0001f91a\U0001f3fc" +RAISED_BACK_OF_HAND_MEDIUM_SKIN_TONE = "\U0001f91a\U0001f3fd" +RAISED_BACK_OF_HAND_MEDIUM_DARK_SKIN_TONE = "\U0001f91a\U0001f3fe" +RAISED_BACK_OF_HAND_DARK_SKIN_TONE = "\U0001f91a\U0001f3ff" +HAND_WITH_FINGERS_SPLAYED = "\U0001f590\ufe0f" +HAND_WITH_FINGERS_SPLAYED_LIGHT_SKIN_TONE = "\U0001f590\U0001f3fb" +HAND_WITH_FINGERS_SPLAYED_MEDIUM_LIGHT_SKIN_TONE = "\U0001f590\U0001f3fc" +HAND_WITH_FINGERS_SPLAYED_MEDIUM_SKIN_TONE = "\U0001f590\U0001f3fd" +HAND_WITH_FINGERS_SPLAYED_MEDIUM_DARK_SKIN_TONE = "\U0001f590\U0001f3fe" +HAND_WITH_FINGERS_SPLAYED_DARK_SKIN_TONE = "\U0001f590\U0001f3ff" +RAISED_HAND = "\u270b" +RAISED_HAND_LIGHT_SKIN_TONE = "\u270b\U0001f3fb" +RAISED_HAND_MEDIUM_LIGHT_SKIN_TONE = "\u270b\U0001f3fc" +RAISED_HAND_MEDIUM_SKIN_TONE = "\u270b\U0001f3fd" +RAISED_HAND_MEDIUM_DARK_SKIN_TONE = "\u270b\U0001f3fe" +RAISED_HAND_DARK_SKIN_TONE = "\u270b\U0001f3ff" +VULCAN_SALUTE = "\U0001f596" +VULCAN_SALUTE_LIGHT_SKIN_TONE = "\U0001f596\U0001f3fb" +VULCAN_SALUTE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f596\U0001f3fc" +VULCAN_SALUTE_MEDIUM_SKIN_TONE = "\U0001f596\U0001f3fd" +VULCAN_SALUTE_MEDIUM_DARK_SKIN_TONE = "\U0001f596\U0001f3fe" +VULCAN_SALUTE_DARK_SKIN_TONE = "\U0001f596\U0001f3ff" +RIGHTWARDS_HAND = "\U0001faf1" +RIGHTWARDS_HAND_LIGHT_SKIN_TONE = "\U0001faf1\U0001f3fb" +RIGHTWARDS_HAND_MEDIUM_LIGHT_SKIN_TONE = "\U0001faf1\U0001f3fc" +RIGHTWARDS_HAND_MEDIUM_SKIN_TONE = "\U0001faf1\U0001f3fd" +RIGHTWARDS_HAND_MEDIUM_DARK_SKIN_TONE = "\U0001faf1\U0001f3fe" +RIGHTWARDS_HAND_DARK_SKIN_TONE = "\U0001faf1\U0001f3ff" +LEFTWARDS_HAND = "\U0001faf2" +LEFTWARDS_HAND_LIGHT_SKIN_TONE = "\U0001faf2\U0001f3fb" +LEFTWARDS_HAND_MEDIUM_LIGHT_SKIN_TONE = "\U0001faf2\U0001f3fc" +LEFTWARDS_HAND_MEDIUM_SKIN_TONE = "\U0001faf2\U0001f3fd" +LEFTWARDS_HAND_MEDIUM_DARK_SKIN_TONE = "\U0001faf2\U0001f3fe" +LEFTWARDS_HAND_DARK_SKIN_TONE = "\U0001faf2\U0001f3ff" +PALM_DOWN_HAND = "\U0001faf3" +PALM_DOWN_HAND_LIGHT_SKIN_TONE = "\U0001faf3\U0001f3fb" +PALM_DOWN_HAND_MEDIUM_LIGHT_SKIN_TONE = "\U0001faf3\U0001f3fc" +PALM_DOWN_HAND_MEDIUM_SKIN_TONE = "\U0001faf3\U0001f3fd" +PALM_DOWN_HAND_MEDIUM_DARK_SKIN_TONE = "\U0001faf3\U0001f3fe" +PALM_DOWN_HAND_DARK_SKIN_TONE = "\U0001faf3\U0001f3ff" +PALM_UP_HAND = "\U0001faf4" +PALM_UP_HAND_LIGHT_SKIN_TONE = "\U0001faf4\U0001f3fb" +PALM_UP_HAND_MEDIUM_LIGHT_SKIN_TONE = "\U0001faf4\U0001f3fc" +PALM_UP_HAND_MEDIUM_SKIN_TONE = "\U0001faf4\U0001f3fd" +PALM_UP_HAND_MEDIUM_DARK_SKIN_TONE = "\U0001faf4\U0001f3fe" +PALM_UP_HAND_DARK_SKIN_TONE = "\U0001faf4\U0001f3ff" +OK_HAND = "\U0001f44c" +OK_HAND_LIGHT_SKIN_TONE = "\U0001f44c\U0001f3fb" +OK_HAND_MEDIUM_LIGHT_SKIN_TONE = "\U0001f44c\U0001f3fc" +OK_HAND_MEDIUM_SKIN_TONE = "\U0001f44c\U0001f3fd" +OK_HAND_MEDIUM_DARK_SKIN_TONE = "\U0001f44c\U0001f3fe" +OK_HAND_DARK_SKIN_TONE = "\U0001f44c\U0001f3ff" +PINCHED_FINGERS = "\U0001f90c" +PINCHED_FINGERS_LIGHT_SKIN_TONE = "\U0001f90c\U0001f3fb" +PINCHED_FINGERS_MEDIUM_LIGHT_SKIN_TONE = "\U0001f90c\U0001f3fc" +PINCHED_FINGERS_MEDIUM_SKIN_TONE = "\U0001f90c\U0001f3fd" +PINCHED_FINGERS_MEDIUM_DARK_SKIN_TONE = "\U0001f90c\U0001f3fe" +PINCHED_FINGERS_DARK_SKIN_TONE = "\U0001f90c\U0001f3ff" +PINCHING_HAND = "\U0001f90f" +PINCHING_HAND_LIGHT_SKIN_TONE = "\U0001f90f\U0001f3fb" +PINCHING_HAND_MEDIUM_LIGHT_SKIN_TONE = "\U0001f90f\U0001f3fc" +PINCHING_HAND_MEDIUM_SKIN_TONE = "\U0001f90f\U0001f3fd" +PINCHING_HAND_MEDIUM_DARK_SKIN_TONE = "\U0001f90f\U0001f3fe" +PINCHING_HAND_DARK_SKIN_TONE = "\U0001f90f\U0001f3ff" +VICTORY_HAND = "\u270c\ufe0f" +VICTORY_HAND_LIGHT_SKIN_TONE = "\u270c\U0001f3fb" +VICTORY_HAND_MEDIUM_LIGHT_SKIN_TONE = "\u270c\U0001f3fc" +VICTORY_HAND_MEDIUM_SKIN_TONE = "\u270c\U0001f3fd" +VICTORY_HAND_MEDIUM_DARK_SKIN_TONE = "\u270c\U0001f3fe" +VICTORY_HAND_DARK_SKIN_TONE = "\u270c\U0001f3ff" +CROSSED_FINGERS = "\U0001f91e" +CROSSED_FINGERS_LIGHT_SKIN_TONE = "\U0001f91e\U0001f3fb" +CROSSED_FINGERS_MEDIUM_LIGHT_SKIN_TONE = "\U0001f91e\U0001f3fc" +CROSSED_FINGERS_MEDIUM_SKIN_TONE = "\U0001f91e\U0001f3fd" +CROSSED_FINGERS_MEDIUM_DARK_SKIN_TONE = "\U0001f91e\U0001f3fe" +CROSSED_FINGERS_DARK_SKIN_TONE = "\U0001f91e\U0001f3ff" +HAND_WITH_INDEX_FINGER_AND_THUMB_CROSSED = "\U0001faf0" +HAND_WITH_INDEX_FINGER_AND_THUMB_CROSSED_LIGHT_SKIN_TONE = "\U0001faf0\U0001f3fb" +HAND_WITH_INDEX_FINGER_AND_THUMB_CROSSED_MEDIUM_LIGHT_SKIN_TONE = "\U0001faf0\U0001f3fc" +HAND_WITH_INDEX_FINGER_AND_THUMB_CROSSED_MEDIUM_SKIN_TONE = "\U0001faf0\U0001f3fd" +HAND_WITH_INDEX_FINGER_AND_THUMB_CROSSED_MEDIUM_DARK_SKIN_TONE = "\U0001faf0\U0001f3fe" +HAND_WITH_INDEX_FINGER_AND_THUMB_CROSSED_DARK_SKIN_TONE = "\U0001faf0\U0001f3ff" +LOVE_YOU_GESTURE = "\U0001f91f" +LOVE_YOU_GESTURE_LIGHT_SKIN_TONE = "\U0001f91f\U0001f3fb" +LOVE_YOU_GESTURE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f91f\U0001f3fc" +LOVE_YOU_GESTURE_MEDIUM_SKIN_TONE = "\U0001f91f\U0001f3fd" +LOVE_YOU_GESTURE_MEDIUM_DARK_SKIN_TONE = "\U0001f91f\U0001f3fe" +LOVE_YOU_GESTURE_DARK_SKIN_TONE = "\U0001f91f\U0001f3ff" +SIGN_OF_THE_HORNS = "\U0001f918" +SIGN_OF_THE_HORNS_LIGHT_SKIN_TONE = "\U0001f918\U0001f3fb" +SIGN_OF_THE_HORNS_MEDIUM_LIGHT_SKIN_TONE = "\U0001f918\U0001f3fc" +SIGN_OF_THE_HORNS_MEDIUM_SKIN_TONE = "\U0001f918\U0001f3fd" +SIGN_OF_THE_HORNS_MEDIUM_DARK_SKIN_TONE = "\U0001f918\U0001f3fe" +SIGN_OF_THE_HORNS_DARK_SKIN_TONE = "\U0001f918\U0001f3ff" +CALL_ME_HAND = "\U0001f919" +CALL_ME_HAND_LIGHT_SKIN_TONE = "\U0001f919\U0001f3fb" +CALL_ME_HAND_MEDIUM_LIGHT_SKIN_TONE = "\U0001f919\U0001f3fc" +CALL_ME_HAND_MEDIUM_SKIN_TONE = "\U0001f919\U0001f3fd" +CALL_ME_HAND_MEDIUM_DARK_SKIN_TONE = "\U0001f919\U0001f3fe" +CALL_ME_HAND_DARK_SKIN_TONE = "\U0001f919\U0001f3ff" +BACKHAND_INDEX_POINTING_LEFT = "\U0001f448" +BACKHAND_INDEX_POINTING_LEFT_LIGHT_SKIN_TONE = "\U0001f448\U0001f3fb" +BACKHAND_INDEX_POINTING_LEFT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f448\U0001f3fc" +BACKHAND_INDEX_POINTING_LEFT_MEDIUM_SKIN_TONE = "\U0001f448\U0001f3fd" +BACKHAND_INDEX_POINTING_LEFT_MEDIUM_DARK_SKIN_TONE = "\U0001f448\U0001f3fe" +BACKHAND_INDEX_POINTING_LEFT_DARK_SKIN_TONE = "\U0001f448\U0001f3ff" +BACKHAND_INDEX_POINTING_RIGHT = "\U0001f449" +BACKHAND_INDEX_POINTING_RIGHT_LIGHT_SKIN_TONE = "\U0001f449\U0001f3fb" +BACKHAND_INDEX_POINTING_RIGHT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f449\U0001f3fc" +BACKHAND_INDEX_POINTING_RIGHT_MEDIUM_SKIN_TONE = "\U0001f449\U0001f3fd" +BACKHAND_INDEX_POINTING_RIGHT_MEDIUM_DARK_SKIN_TONE = "\U0001f449\U0001f3fe" +BACKHAND_INDEX_POINTING_RIGHT_DARK_SKIN_TONE = "\U0001f449\U0001f3ff" +BACKHAND_INDEX_POINTING_UP = "\U0001f446" +BACKHAND_INDEX_POINTING_UP_LIGHT_SKIN_TONE = "\U0001f446\U0001f3fb" +BACKHAND_INDEX_POINTING_UP_MEDIUM_LIGHT_SKIN_TONE = "\U0001f446\U0001f3fc" +BACKHAND_INDEX_POINTING_UP_MEDIUM_SKIN_TONE = "\U0001f446\U0001f3fd" +BACKHAND_INDEX_POINTING_UP_MEDIUM_DARK_SKIN_TONE = "\U0001f446\U0001f3fe" +BACKHAND_INDEX_POINTING_UP_DARK_SKIN_TONE = "\U0001f446\U0001f3ff" +MIDDLE_FINGER = "\U0001f595" +MIDDLE_FINGER_LIGHT_SKIN_TONE = "\U0001f595\U0001f3fb" +MIDDLE_FINGER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f595\U0001f3fc" +MIDDLE_FINGER_MEDIUM_SKIN_TONE = "\U0001f595\U0001f3fd" +MIDDLE_FINGER_MEDIUM_DARK_SKIN_TONE = "\U0001f595\U0001f3fe" +MIDDLE_FINGER_DARK_SKIN_TONE = "\U0001f595\U0001f3ff" +BACKHAND_INDEX_POINTING_DOWN = "\U0001f447" +BACKHAND_INDEX_POINTING_DOWN_LIGHT_SKIN_TONE = "\U0001f447\U0001f3fb" +BACKHAND_INDEX_POINTING_DOWN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f447\U0001f3fc" +BACKHAND_INDEX_POINTING_DOWN_MEDIUM_SKIN_TONE = "\U0001f447\U0001f3fd" +BACKHAND_INDEX_POINTING_DOWN_MEDIUM_DARK_SKIN_TONE = "\U0001f447\U0001f3fe" +BACKHAND_INDEX_POINTING_DOWN_DARK_SKIN_TONE = "\U0001f447\U0001f3ff" +INDEX_POINTING_UP = "\u261d\ufe0f" +INDEX_POINTING_UP_LIGHT_SKIN_TONE = "\u261d\U0001f3fb" +INDEX_POINTING_UP_MEDIUM_LIGHT_SKIN_TONE = "\u261d\U0001f3fc" +INDEX_POINTING_UP_MEDIUM_SKIN_TONE = "\u261d\U0001f3fd" +INDEX_POINTING_UP_MEDIUM_DARK_SKIN_TONE = "\u261d\U0001f3fe" +INDEX_POINTING_UP_DARK_SKIN_TONE = "\u261d\U0001f3ff" +INDEX_POINTING_AT_THE_VIEWER = "\U0001faf5" +INDEX_POINTING_AT_THE_VIEWER_LIGHT_SKIN_TONE = "\U0001faf5\U0001f3fb" +INDEX_POINTING_AT_THE_VIEWER_MEDIUM_LIGHT_SKIN_TONE = "\U0001faf5\U0001f3fc" +INDEX_POINTING_AT_THE_VIEWER_MEDIUM_SKIN_TONE = "\U0001faf5\U0001f3fd" +INDEX_POINTING_AT_THE_VIEWER_MEDIUM_DARK_SKIN_TONE = "\U0001faf5\U0001f3fe" +INDEX_POINTING_AT_THE_VIEWER_DARK_SKIN_TONE = "\U0001faf5\U0001f3ff" +THUMBS_UP = "\U0001f44d" +THUMBS_UP_LIGHT_SKIN_TONE = "\U0001f44d\U0001f3fb" +THUMBS_UP_MEDIUM_LIGHT_SKIN_TONE = "\U0001f44d\U0001f3fc" +THUMBS_UP_MEDIUM_SKIN_TONE = "\U0001f44d\U0001f3fd" +THUMBS_UP_MEDIUM_DARK_SKIN_TONE = "\U0001f44d\U0001f3fe" +THUMBS_UP_DARK_SKIN_TONE = "\U0001f44d\U0001f3ff" +THUMBS_DOWN = "\U0001f44e" +THUMBS_DOWN_LIGHT_SKIN_TONE = "\U0001f44e\U0001f3fb" +THUMBS_DOWN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f44e\U0001f3fc" +THUMBS_DOWN_MEDIUM_SKIN_TONE = "\U0001f44e\U0001f3fd" +THUMBS_DOWN_MEDIUM_DARK_SKIN_TONE = "\U0001f44e\U0001f3fe" +THUMBS_DOWN_DARK_SKIN_TONE = "\U0001f44e\U0001f3ff" +RAISED_FIST = "\u270a" +RAISED_FIST_LIGHT_SKIN_TONE = "\u270a\U0001f3fb" +RAISED_FIST_MEDIUM_LIGHT_SKIN_TONE = "\u270a\U0001f3fc" +RAISED_FIST_MEDIUM_SKIN_TONE = "\u270a\U0001f3fd" +RAISED_FIST_MEDIUM_DARK_SKIN_TONE = "\u270a\U0001f3fe" +RAISED_FIST_DARK_SKIN_TONE = "\u270a\U0001f3ff" +ONCOMING_FIST = "\U0001f44a" +ONCOMING_FIST_LIGHT_SKIN_TONE = "\U0001f44a\U0001f3fb" +ONCOMING_FIST_MEDIUM_LIGHT_SKIN_TONE = "\U0001f44a\U0001f3fc" +ONCOMING_FIST_MEDIUM_SKIN_TONE = "\U0001f44a\U0001f3fd" +ONCOMING_FIST_MEDIUM_DARK_SKIN_TONE = "\U0001f44a\U0001f3fe" +ONCOMING_FIST_DARK_SKIN_TONE = "\U0001f44a\U0001f3ff" +LEFT_FACING_FIST = "\U0001f91b" +LEFT_FACING_FIST_LIGHT_SKIN_TONE = "\U0001f91b\U0001f3fb" +LEFT_FACING_FIST_MEDIUM_LIGHT_SKIN_TONE = "\U0001f91b\U0001f3fc" +LEFT_FACING_FIST_MEDIUM_SKIN_TONE = "\U0001f91b\U0001f3fd" +LEFT_FACING_FIST_MEDIUM_DARK_SKIN_TONE = "\U0001f91b\U0001f3fe" +LEFT_FACING_FIST_DARK_SKIN_TONE = "\U0001f91b\U0001f3ff" +RIGHT_FACING_FIST = "\U0001f91c" +RIGHT_FACING_FIST_LIGHT_SKIN_TONE = "\U0001f91c\U0001f3fb" +RIGHT_FACING_FIST_MEDIUM_LIGHT_SKIN_TONE = "\U0001f91c\U0001f3fc" +RIGHT_FACING_FIST_MEDIUM_SKIN_TONE = "\U0001f91c\U0001f3fd" +RIGHT_FACING_FIST_MEDIUM_DARK_SKIN_TONE = "\U0001f91c\U0001f3fe" +RIGHT_FACING_FIST_DARK_SKIN_TONE = "\U0001f91c\U0001f3ff" +CLAPPING_HANDS = "\U0001f44f" +CLAPPING_HANDS_LIGHT_SKIN_TONE = "\U0001f44f\U0001f3fb" +CLAPPING_HANDS_MEDIUM_LIGHT_SKIN_TONE = "\U0001f44f\U0001f3fc" +CLAPPING_HANDS_MEDIUM_SKIN_TONE = "\U0001f44f\U0001f3fd" +CLAPPING_HANDS_MEDIUM_DARK_SKIN_TONE = "\U0001f44f\U0001f3fe" +CLAPPING_HANDS_DARK_SKIN_TONE = "\U0001f44f\U0001f3ff" +RAISING_HANDS = "\U0001f64c" +RAISING_HANDS_LIGHT_SKIN_TONE = "\U0001f64c\U0001f3fb" +RAISING_HANDS_MEDIUM_LIGHT_SKIN_TONE = "\U0001f64c\U0001f3fc" +RAISING_HANDS_MEDIUM_SKIN_TONE = "\U0001f64c\U0001f3fd" +RAISING_HANDS_MEDIUM_DARK_SKIN_TONE = "\U0001f64c\U0001f3fe" +RAISING_HANDS_DARK_SKIN_TONE = "\U0001f64c\U0001f3ff" +HEART_HANDS = "\U0001faf6" +HEART_HANDS_LIGHT_SKIN_TONE = "\U0001faf6\U0001f3fb" +HEART_HANDS_MEDIUM_LIGHT_SKIN_TONE = "\U0001faf6\U0001f3fc" +HEART_HANDS_MEDIUM_SKIN_TONE = "\U0001faf6\U0001f3fd" +HEART_HANDS_MEDIUM_DARK_SKIN_TONE = "\U0001faf6\U0001f3fe" +HEART_HANDS_DARK_SKIN_TONE = "\U0001faf6\U0001f3ff" +OPEN_HANDS = "\U0001f450" +OPEN_HANDS_LIGHT_SKIN_TONE = "\U0001f450\U0001f3fb" +OPEN_HANDS_MEDIUM_LIGHT_SKIN_TONE = "\U0001f450\U0001f3fc" +OPEN_HANDS_MEDIUM_SKIN_TONE = "\U0001f450\U0001f3fd" +OPEN_HANDS_MEDIUM_DARK_SKIN_TONE = "\U0001f450\U0001f3fe" +OPEN_HANDS_DARK_SKIN_TONE = "\U0001f450\U0001f3ff" +PALMS_UP_TOGETHER = "\U0001f932" +PALMS_UP_TOGETHER_LIGHT_SKIN_TONE = "\U0001f932\U0001f3fb" +PALMS_UP_TOGETHER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f932\U0001f3fc" +PALMS_UP_TOGETHER_MEDIUM_SKIN_TONE = "\U0001f932\U0001f3fd" +PALMS_UP_TOGETHER_MEDIUM_DARK_SKIN_TONE = "\U0001f932\U0001f3fe" +PALMS_UP_TOGETHER_DARK_SKIN_TONE = "\U0001f932\U0001f3ff" +HANDSHAKE = "\U0001f91d" +HANDSHAKE_LIGHT_SKIN_TONE = "\U0001f91d\U0001f3fb" +HANDSHAKE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f91d\U0001f3fc" +HANDSHAKE_MEDIUM_SKIN_TONE = "\U0001f91d\U0001f3fd" +HANDSHAKE_MEDIUM_DARK_SKIN_TONE = "\U0001f91d\U0001f3fe" +HANDSHAKE_DARK_SKIN_TONE = "\U0001f91d\U0001f3ff" +HANDSHAKE_LIGHT_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = "\U0001faf1\U0001f3fb\u200d\U0001faf2\U0001f3fc" +HANDSHAKE_LIGHT_SKIN_TONE_MEDIUM_SKIN_TONE = "\U0001faf1\U0001f3fb\u200d\U0001faf2\U0001f3fd" +HANDSHAKE_LIGHT_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = "\U0001faf1\U0001f3fb\u200d\U0001faf2\U0001f3fe" +HANDSHAKE_LIGHT_SKIN_TONE_DARK_SKIN_TONE = "\U0001faf1\U0001f3fb\u200d\U0001faf2\U0001f3ff" +HANDSHAKE_MEDIUM_LIGHT_SKIN_TONE_LIGHT_SKIN_TONE = "\U0001faf1\U0001f3fc\u200d\U0001faf2\U0001f3fb" +HANDSHAKE_MEDIUM_LIGHT_SKIN_TONE_MEDIUM_SKIN_TONE = "\U0001faf1\U0001f3fc\u200d\U0001faf2\U0001f3fd" +HANDSHAKE_MEDIUM_LIGHT_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = "\U0001faf1\U0001f3fc\u200d\U0001faf2\U0001f3fe" +HANDSHAKE_MEDIUM_LIGHT_SKIN_TONE_DARK_SKIN_TONE = "\U0001faf1\U0001f3fc\u200d\U0001faf2\U0001f3ff" +HANDSHAKE_MEDIUM_SKIN_TONE_LIGHT_SKIN_TONE = "\U0001faf1\U0001f3fd\u200d\U0001faf2\U0001f3fb" +HANDSHAKE_MEDIUM_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = "\U0001faf1\U0001f3fd\u200d\U0001faf2\U0001f3fc" +HANDSHAKE_MEDIUM_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = "\U0001faf1\U0001f3fd\u200d\U0001faf2\U0001f3fe" +HANDSHAKE_MEDIUM_SKIN_TONE_DARK_SKIN_TONE = "\U0001faf1\U0001f3fd\u200d\U0001faf2\U0001f3ff" +HANDSHAKE_MEDIUM_DARK_SKIN_TONE_LIGHT_SKIN_TONE = "\U0001faf1\U0001f3fe\u200d\U0001faf2\U0001f3fb" +HANDSHAKE_MEDIUM_DARK_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = "\U0001faf1\U0001f3fe\u200d\U0001faf2\U0001f3fc" +HANDSHAKE_MEDIUM_DARK_SKIN_TONE_MEDIUM_SKIN_TONE = "\U0001faf1\U0001f3fe\u200d\U0001faf2\U0001f3fd" +HANDSHAKE_MEDIUM_DARK_SKIN_TONE_DARK_SKIN_TONE = "\U0001faf1\U0001f3fe\u200d\U0001faf2\U0001f3ff" +HANDSHAKE_DARK_SKIN_TONE_LIGHT_SKIN_TONE = "\U0001faf1\U0001f3ff\u200d\U0001faf2\U0001f3fb" +HANDSHAKE_DARK_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = "\U0001faf1\U0001f3ff\u200d\U0001faf2\U0001f3fc" +HANDSHAKE_DARK_SKIN_TONE_MEDIUM_SKIN_TONE = "\U0001faf1\U0001f3ff\u200d\U0001faf2\U0001f3fd" +HANDSHAKE_DARK_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = "\U0001faf1\U0001f3ff\u200d\U0001faf2\U0001f3fe" +FOLDED_HANDS = "\U0001f64f" +FOLDED_HANDS_LIGHT_SKIN_TONE = "\U0001f64f\U0001f3fb" +FOLDED_HANDS_MEDIUM_LIGHT_SKIN_TONE = "\U0001f64f\U0001f3fc" +FOLDED_HANDS_MEDIUM_SKIN_TONE = "\U0001f64f\U0001f3fd" +FOLDED_HANDS_MEDIUM_DARK_SKIN_TONE = "\U0001f64f\U0001f3fe" +FOLDED_HANDS_DARK_SKIN_TONE = "\U0001f64f\U0001f3ff" +WRITING_HAND = "\u270d\ufe0f" +WRITING_HAND_LIGHT_SKIN_TONE = "\u270d\U0001f3fb" +WRITING_HAND_MEDIUM_LIGHT_SKIN_TONE = "\u270d\U0001f3fc" +WRITING_HAND_MEDIUM_SKIN_TONE = "\u270d\U0001f3fd" +WRITING_HAND_MEDIUM_DARK_SKIN_TONE = "\u270d\U0001f3fe" +WRITING_HAND_DARK_SKIN_TONE = "\u270d\U0001f3ff" +NAIL_POLISH = "\U0001f485" +NAIL_POLISH_LIGHT_SKIN_TONE = "\U0001f485\U0001f3fb" +NAIL_POLISH_MEDIUM_LIGHT_SKIN_TONE = "\U0001f485\U0001f3fc" +NAIL_POLISH_MEDIUM_SKIN_TONE = "\U0001f485\U0001f3fd" +NAIL_POLISH_MEDIUM_DARK_SKIN_TONE = "\U0001f485\U0001f3fe" +NAIL_POLISH_DARK_SKIN_TONE = "\U0001f485\U0001f3ff" +SELFIE = "\U0001f933" +SELFIE_LIGHT_SKIN_TONE = "\U0001f933\U0001f3fb" +SELFIE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f933\U0001f3fc" +SELFIE_MEDIUM_SKIN_TONE = "\U0001f933\U0001f3fd" +SELFIE_MEDIUM_DARK_SKIN_TONE = "\U0001f933\U0001f3fe" +SELFIE_DARK_SKIN_TONE = "\U0001f933\U0001f3ff" +FLEXED_BICEPS = "\U0001f4aa" +FLEXED_BICEPS_LIGHT_SKIN_TONE = "\U0001f4aa\U0001f3fb" +FLEXED_BICEPS_MEDIUM_LIGHT_SKIN_TONE = "\U0001f4aa\U0001f3fc" +FLEXED_BICEPS_MEDIUM_SKIN_TONE = "\U0001f4aa\U0001f3fd" +FLEXED_BICEPS_MEDIUM_DARK_SKIN_TONE = "\U0001f4aa\U0001f3fe" +FLEXED_BICEPS_DARK_SKIN_TONE = "\U0001f4aa\U0001f3ff" +MECHANICAL_ARM = "\U0001f9be" +MECHANICAL_LEG = "\U0001f9bf" +LEG = "\U0001f9b5" +LEG_LIGHT_SKIN_TONE = "\U0001f9b5\U0001f3fb" +LEG_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9b5\U0001f3fc" +LEG_MEDIUM_SKIN_TONE = "\U0001f9b5\U0001f3fd" +LEG_MEDIUM_DARK_SKIN_TONE = "\U0001f9b5\U0001f3fe" +LEG_DARK_SKIN_TONE = "\U0001f9b5\U0001f3ff" +FOOT = "\U0001f9b6" +FOOT_LIGHT_SKIN_TONE = "\U0001f9b6\U0001f3fb" +FOOT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9b6\U0001f3fc" +FOOT_MEDIUM_SKIN_TONE = "\U0001f9b6\U0001f3fd" +FOOT_MEDIUM_DARK_SKIN_TONE = "\U0001f9b6\U0001f3fe" +FOOT_DARK_SKIN_TONE = "\U0001f9b6\U0001f3ff" +EAR = "\U0001f442" +EAR_LIGHT_SKIN_TONE = "\U0001f442\U0001f3fb" +EAR_MEDIUM_LIGHT_SKIN_TONE = "\U0001f442\U0001f3fc" +EAR_MEDIUM_SKIN_TONE = "\U0001f442\U0001f3fd" +EAR_MEDIUM_DARK_SKIN_TONE = "\U0001f442\U0001f3fe" +EAR_DARK_SKIN_TONE = "\U0001f442\U0001f3ff" +EAR_WITH_HEARING_AID = "\U0001f9bb" +EAR_WITH_HEARING_AID_LIGHT_SKIN_TONE = "\U0001f9bb\U0001f3fb" +EAR_WITH_HEARING_AID_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9bb\U0001f3fc" +EAR_WITH_HEARING_AID_MEDIUM_SKIN_TONE = "\U0001f9bb\U0001f3fd" +EAR_WITH_HEARING_AID_MEDIUM_DARK_SKIN_TONE = "\U0001f9bb\U0001f3fe" +EAR_WITH_HEARING_AID_DARK_SKIN_TONE = "\U0001f9bb\U0001f3ff" +NOSE = "\U0001f443" +NOSE_LIGHT_SKIN_TONE = "\U0001f443\U0001f3fb" +NOSE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f443\U0001f3fc" +NOSE_MEDIUM_SKIN_TONE = "\U0001f443\U0001f3fd" +NOSE_MEDIUM_DARK_SKIN_TONE = "\U0001f443\U0001f3fe" +NOSE_DARK_SKIN_TONE = "\U0001f443\U0001f3ff" +BRAIN = "\U0001f9e0" +ANATOMICAL_HEART = "\U0001fac0" +LUNGS = "\U0001fac1" +TOOTH = "\U0001f9b7" +BONE = "\U0001f9b4" +EYES = "\U0001f440" +EYE = "\U0001f441\ufe0f" +TONGUE = "\U0001f445" +MOUTH = "\U0001f444" +BITING_LIP = "\U0001fae6" +BABY = "\U0001f476" +BABY_LIGHT_SKIN_TONE = "\U0001f476\U0001f3fb" +BABY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f476\U0001f3fc" +BABY_MEDIUM_SKIN_TONE = "\U0001f476\U0001f3fd" +BABY_MEDIUM_DARK_SKIN_TONE = "\U0001f476\U0001f3fe" +BABY_DARK_SKIN_TONE = "\U0001f476\U0001f3ff" +CHILD = "\U0001f9d2" +CHILD_LIGHT_SKIN_TONE = "\U0001f9d2\U0001f3fb" +CHILD_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d2\U0001f3fc" +CHILD_MEDIUM_SKIN_TONE = "\U0001f9d2\U0001f3fd" +CHILD_MEDIUM_DARK_SKIN_TONE = "\U0001f9d2\U0001f3fe" +CHILD_DARK_SKIN_TONE = "\U0001f9d2\U0001f3ff" +BOY = "\U0001f466" +BOY_LIGHT_SKIN_TONE = "\U0001f466\U0001f3fb" +BOY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f466\U0001f3fc" +BOY_MEDIUM_SKIN_TONE = "\U0001f466\U0001f3fd" +BOY_MEDIUM_DARK_SKIN_TONE = "\U0001f466\U0001f3fe" +BOY_DARK_SKIN_TONE = "\U0001f466\U0001f3ff" +GIRL = "\U0001f467" +GIRL_LIGHT_SKIN_TONE = "\U0001f467\U0001f3fb" +GIRL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f467\U0001f3fc" +GIRL_MEDIUM_SKIN_TONE = "\U0001f467\U0001f3fd" +GIRL_MEDIUM_DARK_SKIN_TONE = "\U0001f467\U0001f3fe" +GIRL_DARK_SKIN_TONE = "\U0001f467\U0001f3ff" +PERSON = "\U0001f9d1" +PERSON_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fb" +PERSON_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fc" +PERSON_MEDIUM_SKIN_TONE = "\U0001f9d1\U0001f3fd" +PERSON_MEDIUM_DARK_SKIN_TONE = "\U0001f9d1\U0001f3fe" +PERSON_DARK_SKIN_TONE = "\U0001f9d1\U0001f3ff" +PERSON_BLOND_HAIR = "\U0001f471" +PERSON_LIGHT_SKIN_TONE_BLOND_HAIR = "\U0001f471\U0001f3fb" +PERSON_MEDIUM_LIGHT_SKIN_TONE_BLOND_HAIR = "\U0001f471\U0001f3fc" +PERSON_MEDIUM_SKIN_TONE_BLOND_HAIR = "\U0001f471\U0001f3fd" +PERSON_MEDIUM_DARK_SKIN_TONE_BLOND_HAIR = "\U0001f471\U0001f3fe" +PERSON_DARK_SKIN_TONE_BLOND_HAIR = "\U0001f471\U0001f3ff" +MAN = "\U0001f468" +MAN_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb" +MAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc" +MAN_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd" +MAN_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe" +MAN_DARK_SKIN_TONE = "\U0001f468\U0001f3ff" +PERSON_BEARD = "\U0001f9d4" +PERSON_LIGHT_SKIN_TONE_BEARD = "\U0001f9d4\U0001f3fb" +PERSON_MEDIUM_LIGHT_SKIN_TONE_BEARD = "\U0001f9d4\U0001f3fc" +PERSON_MEDIUM_SKIN_TONE_BEARD = "\U0001f9d4\U0001f3fd" +PERSON_MEDIUM_DARK_SKIN_TONE_BEARD = "\U0001f9d4\U0001f3fe" +PERSON_DARK_SKIN_TONE_BEARD = "\U0001f9d4\U0001f3ff" +MAN_BEARD = "\U0001f9d4\u200d\u2642\ufe0f" +MAN_LIGHT_SKIN_TONE_BEARD = "\U0001f9d4\U0001f3fb\u200d\u2642\ufe0f" +MAN_MEDIUM_LIGHT_SKIN_TONE_BEARD = "\U0001f9d4\U0001f3fc\u200d\u2642\ufe0f" +MAN_MEDIUM_SKIN_TONE_BEARD = "\U0001f9d4\U0001f3fd\u200d\u2642\ufe0f" +MAN_MEDIUM_DARK_SKIN_TONE_BEARD = "\U0001f9d4\U0001f3fe\u200d\u2642\ufe0f" +MAN_DARK_SKIN_TONE_BEARD = "\U0001f9d4\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_BEARD = "\U0001f9d4\u200d\u2640\ufe0f" +WOMAN_LIGHT_SKIN_TONE_BEARD = "\U0001f9d4\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_MEDIUM_LIGHT_SKIN_TONE_BEARD = "\U0001f9d4\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_MEDIUM_SKIN_TONE_BEARD = "\U0001f9d4\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_MEDIUM_DARK_SKIN_TONE_BEARD = "\U0001f9d4\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_DARK_SKIN_TONE_BEARD = "\U0001f9d4\U0001f3ff\u200d\u2640\ufe0f" +MAN_RED_HAIR = "\U0001f468\u200d\U0001f9b0" +MAN_LIGHT_SKIN_TONE_RED_HAIR = "\U0001f468\U0001f3fb\u200d\U0001f9b0" +MAN_MEDIUM_LIGHT_SKIN_TONE_RED_HAIR = "\U0001f468\U0001f3fc\u200d\U0001f9b0" +MAN_MEDIUM_SKIN_TONE_RED_HAIR = "\U0001f468\U0001f3fd\u200d\U0001f9b0" +MAN_MEDIUM_DARK_SKIN_TONE_RED_HAIR = "\U0001f468\U0001f3fe\u200d\U0001f9b0" +MAN_DARK_SKIN_TONE_RED_HAIR = "\U0001f468\U0001f3ff\u200d\U0001f9b0" +MAN_CURLY_HAIR = "\U0001f468\u200d\U0001f9b1" +MAN_LIGHT_SKIN_TONE_CURLY_HAIR = "\U0001f468\U0001f3fb\u200d\U0001f9b1" +MAN_MEDIUM_LIGHT_SKIN_TONE_CURLY_HAIR = "\U0001f468\U0001f3fc\u200d\U0001f9b1" +MAN_MEDIUM_SKIN_TONE_CURLY_HAIR = "\U0001f468\U0001f3fd\u200d\U0001f9b1" +MAN_MEDIUM_DARK_SKIN_TONE_CURLY_HAIR = "\U0001f468\U0001f3fe\u200d\U0001f9b1" +MAN_DARK_SKIN_TONE_CURLY_HAIR = "\U0001f468\U0001f3ff\u200d\U0001f9b1" +MAN_WHITE_HAIR = "\U0001f468\u200d\U0001f9b3" +MAN_LIGHT_SKIN_TONE_WHITE_HAIR = "\U0001f468\U0001f3fb\u200d\U0001f9b3" +MAN_MEDIUM_LIGHT_SKIN_TONE_WHITE_HAIR = "\U0001f468\U0001f3fc\u200d\U0001f9b3" +MAN_MEDIUM_SKIN_TONE_WHITE_HAIR = "\U0001f468\U0001f3fd\u200d\U0001f9b3" +MAN_MEDIUM_DARK_SKIN_TONE_WHITE_HAIR = "\U0001f468\U0001f3fe\u200d\U0001f9b3" +MAN_DARK_SKIN_TONE_WHITE_HAIR = "\U0001f468\U0001f3ff\u200d\U0001f9b3" +MAN_BALD = "\U0001f468\u200d\U0001f9b2" +MAN_LIGHT_SKIN_TONE_BALD = "\U0001f468\U0001f3fb\u200d\U0001f9b2" +MAN_MEDIUM_LIGHT_SKIN_TONE_BALD = "\U0001f468\U0001f3fc\u200d\U0001f9b2" +MAN_MEDIUM_SKIN_TONE_BALD = "\U0001f468\U0001f3fd\u200d\U0001f9b2" +MAN_MEDIUM_DARK_SKIN_TONE_BALD = "\U0001f468\U0001f3fe\u200d\U0001f9b2" +MAN_DARK_SKIN_TONE_BALD = "\U0001f468\U0001f3ff\u200d\U0001f9b2" +WOMAN = "\U0001f469" +WOMAN_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb" +WOMAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc" +WOMAN_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd" +WOMAN_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe" +WOMAN_DARK_SKIN_TONE = "\U0001f469\U0001f3ff" +WOMAN_RED_HAIR = "\U0001f469\u200d\U0001f9b0" +WOMAN_LIGHT_SKIN_TONE_RED_HAIR = "\U0001f469\U0001f3fb\u200d\U0001f9b0" +WOMAN_MEDIUM_LIGHT_SKIN_TONE_RED_HAIR = "\U0001f469\U0001f3fc\u200d\U0001f9b0" +WOMAN_MEDIUM_SKIN_TONE_RED_HAIR = "\U0001f469\U0001f3fd\u200d\U0001f9b0" +WOMAN_MEDIUM_DARK_SKIN_TONE_RED_HAIR = "\U0001f469\U0001f3fe\u200d\U0001f9b0" +WOMAN_DARK_SKIN_TONE_RED_HAIR = "\U0001f469\U0001f3ff\u200d\U0001f9b0" +PERSON_RED_HAIR = "\U0001f9d1\u200d\U0001f9b0" +PERSON_LIGHT_SKIN_TONE_RED_HAIR = "\U0001f9d1\U0001f3fb\u200d\U0001f9b0" +PERSON_MEDIUM_LIGHT_SKIN_TONE_RED_HAIR = "\U0001f9d1\U0001f3fc\u200d\U0001f9b0" +PERSON_MEDIUM_SKIN_TONE_RED_HAIR = "\U0001f9d1\U0001f3fd\u200d\U0001f9b0" +PERSON_MEDIUM_DARK_SKIN_TONE_RED_HAIR = "\U0001f9d1\U0001f3fe\u200d\U0001f9b0" +PERSON_DARK_SKIN_TONE_RED_HAIR = "\U0001f9d1\U0001f3ff\u200d\U0001f9b0" +WOMAN_CURLY_HAIR = "\U0001f469\u200d\U0001f9b1" +WOMAN_LIGHT_SKIN_TONE_CURLY_HAIR = "\U0001f469\U0001f3fb\u200d\U0001f9b1" +WOMAN_MEDIUM_LIGHT_SKIN_TONE_CURLY_HAIR = "\U0001f469\U0001f3fc\u200d\U0001f9b1" +WOMAN_MEDIUM_SKIN_TONE_CURLY_HAIR = "\U0001f469\U0001f3fd\u200d\U0001f9b1" +WOMAN_MEDIUM_DARK_SKIN_TONE_CURLY_HAIR = "\U0001f469\U0001f3fe\u200d\U0001f9b1" +WOMAN_DARK_SKIN_TONE_CURLY_HAIR = "\U0001f469\U0001f3ff\u200d\U0001f9b1" +PERSON_CURLY_HAIR = "\U0001f9d1\u200d\U0001f9b1" +PERSON_LIGHT_SKIN_TONE_CURLY_HAIR = "\U0001f9d1\U0001f3fb\u200d\U0001f9b1" +PERSON_MEDIUM_LIGHT_SKIN_TONE_CURLY_HAIR = "\U0001f9d1\U0001f3fc\u200d\U0001f9b1" +PERSON_MEDIUM_SKIN_TONE_CURLY_HAIR = "\U0001f9d1\U0001f3fd\u200d\U0001f9b1" +PERSON_MEDIUM_DARK_SKIN_TONE_CURLY_HAIR = "\U0001f9d1\U0001f3fe\u200d\U0001f9b1" +PERSON_DARK_SKIN_TONE_CURLY_HAIR = "\U0001f9d1\U0001f3ff\u200d\U0001f9b1" +WOMAN_WHITE_HAIR = "\U0001f469\u200d\U0001f9b3" +WOMAN_LIGHT_SKIN_TONE_WHITE_HAIR = "\U0001f469\U0001f3fb\u200d\U0001f9b3" +WOMAN_MEDIUM_LIGHT_SKIN_TONE_WHITE_HAIR = "\U0001f469\U0001f3fc\u200d\U0001f9b3" +WOMAN_MEDIUM_SKIN_TONE_WHITE_HAIR = "\U0001f469\U0001f3fd\u200d\U0001f9b3" +WOMAN_MEDIUM_DARK_SKIN_TONE_WHITE_HAIR = "\U0001f469\U0001f3fe\u200d\U0001f9b3" +WOMAN_DARK_SKIN_TONE_WHITE_HAIR = "\U0001f469\U0001f3ff\u200d\U0001f9b3" +PERSON_WHITE_HAIR = "\U0001f9d1\u200d\U0001f9b3" +PERSON_LIGHT_SKIN_TONE_WHITE_HAIR = "\U0001f9d1\U0001f3fb\u200d\U0001f9b3" +PERSON_MEDIUM_LIGHT_SKIN_TONE_WHITE_HAIR = "\U0001f9d1\U0001f3fc\u200d\U0001f9b3" +PERSON_MEDIUM_SKIN_TONE_WHITE_HAIR = "\U0001f9d1\U0001f3fd\u200d\U0001f9b3" +PERSON_MEDIUM_DARK_SKIN_TONE_WHITE_HAIR = "\U0001f9d1\U0001f3fe\u200d\U0001f9b3" +PERSON_DARK_SKIN_TONE_WHITE_HAIR = "\U0001f9d1\U0001f3ff\u200d\U0001f9b3" +WOMAN_BALD = "\U0001f469\u200d\U0001f9b2" +WOMAN_LIGHT_SKIN_TONE_BALD = "\U0001f469\U0001f3fb\u200d\U0001f9b2" +WOMAN_MEDIUM_LIGHT_SKIN_TONE_BALD = "\U0001f469\U0001f3fc\u200d\U0001f9b2" +WOMAN_MEDIUM_SKIN_TONE_BALD = "\U0001f469\U0001f3fd\u200d\U0001f9b2" +WOMAN_MEDIUM_DARK_SKIN_TONE_BALD = "\U0001f469\U0001f3fe\u200d\U0001f9b2" +WOMAN_DARK_SKIN_TONE_BALD = "\U0001f469\U0001f3ff\u200d\U0001f9b2" +PERSON_BALD = "\U0001f9d1\u200d\U0001f9b2" +PERSON_LIGHT_SKIN_TONE_BALD = "\U0001f9d1\U0001f3fb\u200d\U0001f9b2" +PERSON_MEDIUM_LIGHT_SKIN_TONE_BALD = "\U0001f9d1\U0001f3fc\u200d\U0001f9b2" +PERSON_MEDIUM_SKIN_TONE_BALD = "\U0001f9d1\U0001f3fd\u200d\U0001f9b2" +PERSON_MEDIUM_DARK_SKIN_TONE_BALD = "\U0001f9d1\U0001f3fe\u200d\U0001f9b2" +PERSON_DARK_SKIN_TONE_BALD = "\U0001f9d1\U0001f3ff\u200d\U0001f9b2" +WOMAN_BLOND_HAIR = "\U0001f471\u200d\u2640\ufe0f" +WOMAN_LIGHT_SKIN_TONE_BLOND_HAIR = "\U0001f471\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_MEDIUM_LIGHT_SKIN_TONE_BLOND_HAIR = "\U0001f471\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_MEDIUM_SKIN_TONE_BLOND_HAIR = "\U0001f471\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_MEDIUM_DARK_SKIN_TONE_BLOND_HAIR = "\U0001f471\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_DARK_SKIN_TONE_BLOND_HAIR = "\U0001f471\U0001f3ff\u200d\u2640\ufe0f" +MAN_BLOND_HAIR = "\U0001f471\u200d\u2642\ufe0f" +MAN_LIGHT_SKIN_TONE_BLOND_HAIR = "\U0001f471\U0001f3fb\u200d\u2642\ufe0f" +MAN_MEDIUM_LIGHT_SKIN_TONE_BLOND_HAIR = "\U0001f471\U0001f3fc\u200d\u2642\ufe0f" +MAN_MEDIUM_SKIN_TONE_BLOND_HAIR = "\U0001f471\U0001f3fd\u200d\u2642\ufe0f" +MAN_MEDIUM_DARK_SKIN_TONE_BLOND_HAIR = "\U0001f471\U0001f3fe\u200d\u2642\ufe0f" +MAN_DARK_SKIN_TONE_BLOND_HAIR = "\U0001f471\U0001f3ff\u200d\u2642\ufe0f" +OLDER_PERSON = "\U0001f9d3" +OLDER_PERSON_LIGHT_SKIN_TONE = "\U0001f9d3\U0001f3fb" +OLDER_PERSON_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d3\U0001f3fc" +OLDER_PERSON_MEDIUM_SKIN_TONE = "\U0001f9d3\U0001f3fd" +OLDER_PERSON_MEDIUM_DARK_SKIN_TONE = "\U0001f9d3\U0001f3fe" +OLDER_PERSON_DARK_SKIN_TONE = "\U0001f9d3\U0001f3ff" +OLD_MAN = "\U0001f474" +OLD_MAN_LIGHT_SKIN_TONE = "\U0001f474\U0001f3fb" +OLD_MAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f474\U0001f3fc" +OLD_MAN_MEDIUM_SKIN_TONE = "\U0001f474\U0001f3fd" +OLD_MAN_MEDIUM_DARK_SKIN_TONE = "\U0001f474\U0001f3fe" +OLD_MAN_DARK_SKIN_TONE = "\U0001f474\U0001f3ff" +OLD_WOMAN = "\U0001f475" +OLD_WOMAN_LIGHT_SKIN_TONE = "\U0001f475\U0001f3fb" +OLD_WOMAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f475\U0001f3fc" +OLD_WOMAN_MEDIUM_SKIN_TONE = "\U0001f475\U0001f3fd" +OLD_WOMAN_MEDIUM_DARK_SKIN_TONE = "\U0001f475\U0001f3fe" +OLD_WOMAN_DARK_SKIN_TONE = "\U0001f475\U0001f3ff" +PERSON_FROWNING = "\U0001f64d" +PERSON_FROWNING_LIGHT_SKIN_TONE = "\U0001f64d\U0001f3fb" +PERSON_FROWNING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f64d\U0001f3fc" +PERSON_FROWNING_MEDIUM_SKIN_TONE = "\U0001f64d\U0001f3fd" +PERSON_FROWNING_MEDIUM_DARK_SKIN_TONE = "\U0001f64d\U0001f3fe" +PERSON_FROWNING_DARK_SKIN_TONE = "\U0001f64d\U0001f3ff" +MAN_FROWNING = "\U0001f64d\u200d\u2642\ufe0f" +MAN_FROWNING_LIGHT_SKIN_TONE = "\U0001f64d\U0001f3fb\u200d\u2642\ufe0f" +MAN_FROWNING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f64d\U0001f3fc\u200d\u2642\ufe0f" +MAN_FROWNING_MEDIUM_SKIN_TONE = "\U0001f64d\U0001f3fd\u200d\u2642\ufe0f" +MAN_FROWNING_MEDIUM_DARK_SKIN_TONE = "\U0001f64d\U0001f3fe\u200d\u2642\ufe0f" +MAN_FROWNING_DARK_SKIN_TONE = "\U0001f64d\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_FROWNING = "\U0001f64d\u200d\u2640\ufe0f" +WOMAN_FROWNING_LIGHT_SKIN_TONE = "\U0001f64d\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_FROWNING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f64d\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_FROWNING_MEDIUM_SKIN_TONE = "\U0001f64d\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_FROWNING_MEDIUM_DARK_SKIN_TONE = "\U0001f64d\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_FROWNING_DARK_SKIN_TONE = "\U0001f64d\U0001f3ff\u200d\u2640\ufe0f" +PERSON_POUTING = "\U0001f64e" +PERSON_POUTING_LIGHT_SKIN_TONE = "\U0001f64e\U0001f3fb" +PERSON_POUTING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f64e\U0001f3fc" +PERSON_POUTING_MEDIUM_SKIN_TONE = "\U0001f64e\U0001f3fd" +PERSON_POUTING_MEDIUM_DARK_SKIN_TONE = "\U0001f64e\U0001f3fe" +PERSON_POUTING_DARK_SKIN_TONE = "\U0001f64e\U0001f3ff" +MAN_POUTING = "\U0001f64e\u200d\u2642\ufe0f" +MAN_POUTING_LIGHT_SKIN_TONE = "\U0001f64e\U0001f3fb\u200d\u2642\ufe0f" +MAN_POUTING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f64e\U0001f3fc\u200d\u2642\ufe0f" +MAN_POUTING_MEDIUM_SKIN_TONE = "\U0001f64e\U0001f3fd\u200d\u2642\ufe0f" +MAN_POUTING_MEDIUM_DARK_SKIN_TONE = "\U0001f64e\U0001f3fe\u200d\u2642\ufe0f" +MAN_POUTING_DARK_SKIN_TONE = "\U0001f64e\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_POUTING = "\U0001f64e\u200d\u2640\ufe0f" +WOMAN_POUTING_LIGHT_SKIN_TONE = "\U0001f64e\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_POUTING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f64e\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_POUTING_MEDIUM_SKIN_TONE = "\U0001f64e\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_POUTING_MEDIUM_DARK_SKIN_TONE = "\U0001f64e\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_POUTING_DARK_SKIN_TONE = "\U0001f64e\U0001f3ff\u200d\u2640\ufe0f" +PERSON_GESTURING_NO = "\U0001f645" +PERSON_GESTURING_NO_LIGHT_SKIN_TONE = "\U0001f645\U0001f3fb" +PERSON_GESTURING_NO_MEDIUM_LIGHT_SKIN_TONE = "\U0001f645\U0001f3fc" +PERSON_GESTURING_NO_MEDIUM_SKIN_TONE = "\U0001f645\U0001f3fd" +PERSON_GESTURING_NO_MEDIUM_DARK_SKIN_TONE = "\U0001f645\U0001f3fe" +PERSON_GESTURING_NO_DARK_SKIN_TONE = "\U0001f645\U0001f3ff" +MAN_GESTURING_NO = "\U0001f645\u200d\u2642\ufe0f" +MAN_GESTURING_NO_LIGHT_SKIN_TONE = "\U0001f645\U0001f3fb\u200d\u2642\ufe0f" +MAN_GESTURING_NO_MEDIUM_LIGHT_SKIN_TONE = "\U0001f645\U0001f3fc\u200d\u2642\ufe0f" +MAN_GESTURING_NO_MEDIUM_SKIN_TONE = "\U0001f645\U0001f3fd\u200d\u2642\ufe0f" +MAN_GESTURING_NO_MEDIUM_DARK_SKIN_TONE = "\U0001f645\U0001f3fe\u200d\u2642\ufe0f" +MAN_GESTURING_NO_DARK_SKIN_TONE = "\U0001f645\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_GESTURING_NO = "\U0001f645\u200d\u2640\ufe0f" +WOMAN_GESTURING_NO_LIGHT_SKIN_TONE = "\U0001f645\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_GESTURING_NO_MEDIUM_LIGHT_SKIN_TONE = "\U0001f645\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_GESTURING_NO_MEDIUM_SKIN_TONE = "\U0001f645\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_GESTURING_NO_MEDIUM_DARK_SKIN_TONE = "\U0001f645\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_GESTURING_NO_DARK_SKIN_TONE = "\U0001f645\U0001f3ff\u200d\u2640\ufe0f" +PERSON_GESTURING_OK = "\U0001f646" +PERSON_GESTURING_OK_LIGHT_SKIN_TONE = "\U0001f646\U0001f3fb" +PERSON_GESTURING_OK_MEDIUM_LIGHT_SKIN_TONE = "\U0001f646\U0001f3fc" +PERSON_GESTURING_OK_MEDIUM_SKIN_TONE = "\U0001f646\U0001f3fd" +PERSON_GESTURING_OK_MEDIUM_DARK_SKIN_TONE = "\U0001f646\U0001f3fe" +PERSON_GESTURING_OK_DARK_SKIN_TONE = "\U0001f646\U0001f3ff" +MAN_GESTURING_OK = "\U0001f646\u200d\u2642\ufe0f" +MAN_GESTURING_OK_LIGHT_SKIN_TONE = "\U0001f646\U0001f3fb\u200d\u2642\ufe0f" +MAN_GESTURING_OK_MEDIUM_LIGHT_SKIN_TONE = "\U0001f646\U0001f3fc\u200d\u2642\ufe0f" +MAN_GESTURING_OK_MEDIUM_SKIN_TONE = "\U0001f646\U0001f3fd\u200d\u2642\ufe0f" +MAN_GESTURING_OK_MEDIUM_DARK_SKIN_TONE = "\U0001f646\U0001f3fe\u200d\u2642\ufe0f" +MAN_GESTURING_OK_DARK_SKIN_TONE = "\U0001f646\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_GESTURING_OK = "\U0001f646\u200d\u2640\ufe0f" +WOMAN_GESTURING_OK_LIGHT_SKIN_TONE = "\U0001f646\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_GESTURING_OK_MEDIUM_LIGHT_SKIN_TONE = "\U0001f646\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_GESTURING_OK_MEDIUM_SKIN_TONE = "\U0001f646\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_GESTURING_OK_MEDIUM_DARK_SKIN_TONE = "\U0001f646\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_GESTURING_OK_DARK_SKIN_TONE = "\U0001f646\U0001f3ff\u200d\u2640\ufe0f" +PERSON_TIPPING_HAND = "\U0001f481" +PERSON_TIPPING_HAND_LIGHT_SKIN_TONE = "\U0001f481\U0001f3fb" +PERSON_TIPPING_HAND_MEDIUM_LIGHT_SKIN_TONE = "\U0001f481\U0001f3fc" +PERSON_TIPPING_HAND_MEDIUM_SKIN_TONE = "\U0001f481\U0001f3fd" +PERSON_TIPPING_HAND_MEDIUM_DARK_SKIN_TONE = "\U0001f481\U0001f3fe" +PERSON_TIPPING_HAND_DARK_SKIN_TONE = "\U0001f481\U0001f3ff" +MAN_TIPPING_HAND = "\U0001f481\u200d\u2642\ufe0f" +MAN_TIPPING_HAND_LIGHT_SKIN_TONE = "\U0001f481\U0001f3fb\u200d\u2642\ufe0f" +MAN_TIPPING_HAND_MEDIUM_LIGHT_SKIN_TONE = "\U0001f481\U0001f3fc\u200d\u2642\ufe0f" +MAN_TIPPING_HAND_MEDIUM_SKIN_TONE = "\U0001f481\U0001f3fd\u200d\u2642\ufe0f" +MAN_TIPPING_HAND_MEDIUM_DARK_SKIN_TONE = "\U0001f481\U0001f3fe\u200d\u2642\ufe0f" +MAN_TIPPING_HAND_DARK_SKIN_TONE = "\U0001f481\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_TIPPING_HAND = "\U0001f481\u200d\u2640\ufe0f" +WOMAN_TIPPING_HAND_LIGHT_SKIN_TONE = "\U0001f481\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_TIPPING_HAND_MEDIUM_LIGHT_SKIN_TONE = "\U0001f481\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_TIPPING_HAND_MEDIUM_SKIN_TONE = "\U0001f481\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_TIPPING_HAND_MEDIUM_DARK_SKIN_TONE = "\U0001f481\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_TIPPING_HAND_DARK_SKIN_TONE = "\U0001f481\U0001f3ff\u200d\u2640\ufe0f" +PERSON_RAISING_HAND = "\U0001f64b" +PERSON_RAISING_HAND_LIGHT_SKIN_TONE = "\U0001f64b\U0001f3fb" +PERSON_RAISING_HAND_MEDIUM_LIGHT_SKIN_TONE = "\U0001f64b\U0001f3fc" +PERSON_RAISING_HAND_MEDIUM_SKIN_TONE = "\U0001f64b\U0001f3fd" +PERSON_RAISING_HAND_MEDIUM_DARK_SKIN_TONE = "\U0001f64b\U0001f3fe" +PERSON_RAISING_HAND_DARK_SKIN_TONE = "\U0001f64b\U0001f3ff" +MAN_RAISING_HAND = "\U0001f64b\u200d\u2642\ufe0f" +MAN_RAISING_HAND_LIGHT_SKIN_TONE = "\U0001f64b\U0001f3fb\u200d\u2642\ufe0f" +MAN_RAISING_HAND_MEDIUM_LIGHT_SKIN_TONE = "\U0001f64b\U0001f3fc\u200d\u2642\ufe0f" +MAN_RAISING_HAND_MEDIUM_SKIN_TONE = "\U0001f64b\U0001f3fd\u200d\u2642\ufe0f" +MAN_RAISING_HAND_MEDIUM_DARK_SKIN_TONE = "\U0001f64b\U0001f3fe\u200d\u2642\ufe0f" +MAN_RAISING_HAND_DARK_SKIN_TONE = "\U0001f64b\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_RAISING_HAND = "\U0001f64b\u200d\u2640\ufe0f" +WOMAN_RAISING_HAND_LIGHT_SKIN_TONE = "\U0001f64b\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_RAISING_HAND_MEDIUM_LIGHT_SKIN_TONE = "\U0001f64b\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_RAISING_HAND_MEDIUM_SKIN_TONE = "\U0001f64b\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_RAISING_HAND_MEDIUM_DARK_SKIN_TONE = "\U0001f64b\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_RAISING_HAND_DARK_SKIN_TONE = "\U0001f64b\U0001f3ff\u200d\u2640\ufe0f" +DEAF_PERSON = "\U0001f9cf" +DEAF_PERSON_LIGHT_SKIN_TONE = "\U0001f9cf\U0001f3fb" +DEAF_PERSON_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9cf\U0001f3fc" +DEAF_PERSON_MEDIUM_SKIN_TONE = "\U0001f9cf\U0001f3fd" +DEAF_PERSON_MEDIUM_DARK_SKIN_TONE = "\U0001f9cf\U0001f3fe" +DEAF_PERSON_DARK_SKIN_TONE = "\U0001f9cf\U0001f3ff" +DEAF_MAN = "\U0001f9cf\u200d\u2642\ufe0f" +DEAF_MAN_LIGHT_SKIN_TONE = "\U0001f9cf\U0001f3fb\u200d\u2642\ufe0f" +DEAF_MAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9cf\U0001f3fc\u200d\u2642\ufe0f" +DEAF_MAN_MEDIUM_SKIN_TONE = "\U0001f9cf\U0001f3fd\u200d\u2642\ufe0f" +DEAF_MAN_MEDIUM_DARK_SKIN_TONE = "\U0001f9cf\U0001f3fe\u200d\u2642\ufe0f" +DEAF_MAN_DARK_SKIN_TONE = "\U0001f9cf\U0001f3ff\u200d\u2642\ufe0f" +DEAF_WOMAN = "\U0001f9cf\u200d\u2640\ufe0f" +DEAF_WOMAN_LIGHT_SKIN_TONE = "\U0001f9cf\U0001f3fb\u200d\u2640\ufe0f" +DEAF_WOMAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9cf\U0001f3fc\u200d\u2640\ufe0f" +DEAF_WOMAN_MEDIUM_SKIN_TONE = "\U0001f9cf\U0001f3fd\u200d\u2640\ufe0f" +DEAF_WOMAN_MEDIUM_DARK_SKIN_TONE = "\U0001f9cf\U0001f3fe\u200d\u2640\ufe0f" +DEAF_WOMAN_DARK_SKIN_TONE = "\U0001f9cf\U0001f3ff\u200d\u2640\ufe0f" +PERSON_BOWING = "\U0001f647" +PERSON_BOWING_LIGHT_SKIN_TONE = "\U0001f647\U0001f3fb" +PERSON_BOWING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f647\U0001f3fc" +PERSON_BOWING_MEDIUM_SKIN_TONE = "\U0001f647\U0001f3fd" +PERSON_BOWING_MEDIUM_DARK_SKIN_TONE = "\U0001f647\U0001f3fe" +PERSON_BOWING_DARK_SKIN_TONE = "\U0001f647\U0001f3ff" +MAN_BOWING = "\U0001f647\u200d\u2642\ufe0f" +MAN_BOWING_LIGHT_SKIN_TONE = "\U0001f647\U0001f3fb\u200d\u2642\ufe0f" +MAN_BOWING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f647\U0001f3fc\u200d\u2642\ufe0f" +MAN_BOWING_MEDIUM_SKIN_TONE = "\U0001f647\U0001f3fd\u200d\u2642\ufe0f" +MAN_BOWING_MEDIUM_DARK_SKIN_TONE = "\U0001f647\U0001f3fe\u200d\u2642\ufe0f" +MAN_BOWING_DARK_SKIN_TONE = "\U0001f647\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_BOWING = "\U0001f647\u200d\u2640\ufe0f" +WOMAN_BOWING_LIGHT_SKIN_TONE = "\U0001f647\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_BOWING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f647\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_BOWING_MEDIUM_SKIN_TONE = "\U0001f647\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_BOWING_MEDIUM_DARK_SKIN_TONE = "\U0001f647\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_BOWING_DARK_SKIN_TONE = "\U0001f647\U0001f3ff\u200d\u2640\ufe0f" +PERSON_FACEPALMING = "\U0001f926" +PERSON_FACEPALMING_LIGHT_SKIN_TONE = "\U0001f926\U0001f3fb" +PERSON_FACEPALMING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f926\U0001f3fc" +PERSON_FACEPALMING_MEDIUM_SKIN_TONE = "\U0001f926\U0001f3fd" +PERSON_FACEPALMING_MEDIUM_DARK_SKIN_TONE = "\U0001f926\U0001f3fe" +PERSON_FACEPALMING_DARK_SKIN_TONE = "\U0001f926\U0001f3ff" +MAN_FACEPALMING = "\U0001f926\u200d\u2642\ufe0f" +MAN_FACEPALMING_LIGHT_SKIN_TONE = "\U0001f926\U0001f3fb\u200d\u2642\ufe0f" +MAN_FACEPALMING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f926\U0001f3fc\u200d\u2642\ufe0f" +MAN_FACEPALMING_MEDIUM_SKIN_TONE = "\U0001f926\U0001f3fd\u200d\u2642\ufe0f" +MAN_FACEPALMING_MEDIUM_DARK_SKIN_TONE = "\U0001f926\U0001f3fe\u200d\u2642\ufe0f" +MAN_FACEPALMING_DARK_SKIN_TONE = "\U0001f926\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_FACEPALMING = "\U0001f926\u200d\u2640\ufe0f" +WOMAN_FACEPALMING_LIGHT_SKIN_TONE = "\U0001f926\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_FACEPALMING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f926\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_FACEPALMING_MEDIUM_SKIN_TONE = "\U0001f926\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_FACEPALMING_MEDIUM_DARK_SKIN_TONE = "\U0001f926\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_FACEPALMING_DARK_SKIN_TONE = "\U0001f926\U0001f3ff\u200d\u2640\ufe0f" +PERSON_SHRUGGING = "\U0001f937" +PERSON_SHRUGGING_LIGHT_SKIN_TONE = "\U0001f937\U0001f3fb" +PERSON_SHRUGGING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f937\U0001f3fc" +PERSON_SHRUGGING_MEDIUM_SKIN_TONE = "\U0001f937\U0001f3fd" +PERSON_SHRUGGING_MEDIUM_DARK_SKIN_TONE = "\U0001f937\U0001f3fe" +PERSON_SHRUGGING_DARK_SKIN_TONE = "\U0001f937\U0001f3ff" +MAN_SHRUGGING = "\U0001f937\u200d\u2642\ufe0f" +MAN_SHRUGGING_LIGHT_SKIN_TONE = "\U0001f937\U0001f3fb\u200d\u2642\ufe0f" +MAN_SHRUGGING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f937\U0001f3fc\u200d\u2642\ufe0f" +MAN_SHRUGGING_MEDIUM_SKIN_TONE = "\U0001f937\U0001f3fd\u200d\u2642\ufe0f" +MAN_SHRUGGING_MEDIUM_DARK_SKIN_TONE = "\U0001f937\U0001f3fe\u200d\u2642\ufe0f" +MAN_SHRUGGING_DARK_SKIN_TONE = "\U0001f937\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_SHRUGGING = "\U0001f937\u200d\u2640\ufe0f" +WOMAN_SHRUGGING_LIGHT_SKIN_TONE = "\U0001f937\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_SHRUGGING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f937\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_SHRUGGING_MEDIUM_SKIN_TONE = "\U0001f937\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_SHRUGGING_MEDIUM_DARK_SKIN_TONE = "\U0001f937\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_SHRUGGING_DARK_SKIN_TONE = "\U0001f937\U0001f3ff\u200d\u2640\ufe0f" +HEALTH_WORKER = "\U0001f9d1\u200d\u2695\ufe0f" +HEALTH_WORKER_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fb\u200d\u2695\ufe0f" +HEALTH_WORKER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fc\u200d\u2695\ufe0f" +HEALTH_WORKER_MEDIUM_SKIN_TONE = "\U0001f9d1\U0001f3fd\u200d\u2695\ufe0f" +HEALTH_WORKER_MEDIUM_DARK_SKIN_TONE = "\U0001f9d1\U0001f3fe\u200d\u2695\ufe0f" +HEALTH_WORKER_DARK_SKIN_TONE = "\U0001f9d1\U0001f3ff\u200d\u2695\ufe0f" +MAN_HEALTH_WORKER = "\U0001f468\u200d\u2695\ufe0f" +MAN_HEALTH_WORKER_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\u2695\ufe0f" +MAN_HEALTH_WORKER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\u2695\ufe0f" +MAN_HEALTH_WORKER_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\u2695\ufe0f" +MAN_HEALTH_WORKER_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\u2695\ufe0f" +MAN_HEALTH_WORKER_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\u2695\ufe0f" +WOMAN_HEALTH_WORKER = "\U0001f469\u200d\u2695\ufe0f" +WOMAN_HEALTH_WORKER_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\u2695\ufe0f" +WOMAN_HEALTH_WORKER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\u2695\ufe0f" +WOMAN_HEALTH_WORKER_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\u2695\ufe0f" +WOMAN_HEALTH_WORKER_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\u2695\ufe0f" +WOMAN_HEALTH_WORKER_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\u2695\ufe0f" +STUDENT = "\U0001f9d1\u200d\U0001f393" +STUDENT_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fb\u200d\U0001f393" +STUDENT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fc\u200d\U0001f393" +STUDENT_MEDIUM_SKIN_TONE = "\U0001f9d1\U0001f3fd\u200d\U0001f393" +STUDENT_MEDIUM_DARK_SKIN_TONE = "\U0001f9d1\U0001f3fe\u200d\U0001f393" +STUDENT_DARK_SKIN_TONE = "\U0001f9d1\U0001f3ff\u200d\U0001f393" +MAN_STUDENT = "\U0001f468\u200d\U0001f393" +MAN_STUDENT_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f393" +MAN_STUDENT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f393" +MAN_STUDENT_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f393" +MAN_STUDENT_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f393" +MAN_STUDENT_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f393" +WOMAN_STUDENT = "\U0001f469\u200d\U0001f393" +WOMAN_STUDENT_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f393" +WOMAN_STUDENT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f393" +WOMAN_STUDENT_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f393" +WOMAN_STUDENT_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f393" +WOMAN_STUDENT_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f393" +TEACHER = "\U0001f9d1\u200d\U0001f3eb" +TEACHER_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fb\u200d\U0001f3eb" +TEACHER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fc\u200d\U0001f3eb" +TEACHER_MEDIUM_SKIN_TONE = "\U0001f9d1\U0001f3fd\u200d\U0001f3eb" +TEACHER_MEDIUM_DARK_SKIN_TONE = "\U0001f9d1\U0001f3fe\u200d\U0001f3eb" +TEACHER_DARK_SKIN_TONE = "\U0001f9d1\U0001f3ff\u200d\U0001f3eb" +MAN_TEACHER = "\U0001f468\u200d\U0001f3eb" +MAN_TEACHER_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f3eb" +MAN_TEACHER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f3eb" +MAN_TEACHER_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f3eb" +MAN_TEACHER_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f3eb" +MAN_TEACHER_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f3eb" +WOMAN_TEACHER = "\U0001f469\u200d\U0001f3eb" +WOMAN_TEACHER_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f3eb" +WOMAN_TEACHER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f3eb" +WOMAN_TEACHER_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f3eb" +WOMAN_TEACHER_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f3eb" +WOMAN_TEACHER_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f3eb" +JUDGE = "\U0001f9d1\u200d\u2696\ufe0f" +JUDGE_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fb\u200d\u2696\ufe0f" +JUDGE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fc\u200d\u2696\ufe0f" +JUDGE_MEDIUM_SKIN_TONE = "\U0001f9d1\U0001f3fd\u200d\u2696\ufe0f" +JUDGE_MEDIUM_DARK_SKIN_TONE = "\U0001f9d1\U0001f3fe\u200d\u2696\ufe0f" +JUDGE_DARK_SKIN_TONE = "\U0001f9d1\U0001f3ff\u200d\u2696\ufe0f" +MAN_JUDGE = "\U0001f468\u200d\u2696\ufe0f" +MAN_JUDGE_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\u2696\ufe0f" +MAN_JUDGE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\u2696\ufe0f" +MAN_JUDGE_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\u2696\ufe0f" +MAN_JUDGE_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\u2696\ufe0f" +MAN_JUDGE_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\u2696\ufe0f" +WOMAN_JUDGE = "\U0001f469\u200d\u2696\ufe0f" +WOMAN_JUDGE_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\u2696\ufe0f" +WOMAN_JUDGE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\u2696\ufe0f" +WOMAN_JUDGE_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\u2696\ufe0f" +WOMAN_JUDGE_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\u2696\ufe0f" +WOMAN_JUDGE_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\u2696\ufe0f" +FARMER = "\U0001f9d1\u200d\U0001f33e" +FARMER_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fb\u200d\U0001f33e" +FARMER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fc\u200d\U0001f33e" +FARMER_MEDIUM_SKIN_TONE = "\U0001f9d1\U0001f3fd\u200d\U0001f33e" +FARMER_MEDIUM_DARK_SKIN_TONE = "\U0001f9d1\U0001f3fe\u200d\U0001f33e" +FARMER_DARK_SKIN_TONE = "\U0001f9d1\U0001f3ff\u200d\U0001f33e" +MAN_FARMER = "\U0001f468\u200d\U0001f33e" +MAN_FARMER_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f33e" +MAN_FARMER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f33e" +MAN_FARMER_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f33e" +MAN_FARMER_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f33e" +MAN_FARMER_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f33e" +WOMAN_FARMER = "\U0001f469\u200d\U0001f33e" +WOMAN_FARMER_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f33e" +WOMAN_FARMER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f33e" +WOMAN_FARMER_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f33e" +WOMAN_FARMER_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f33e" +WOMAN_FARMER_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f33e" +COOK = "\U0001f9d1\u200d\U0001f373" +COOK_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fb\u200d\U0001f373" +COOK_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fc\u200d\U0001f373" +COOK_MEDIUM_SKIN_TONE = "\U0001f9d1\U0001f3fd\u200d\U0001f373" +COOK_MEDIUM_DARK_SKIN_TONE = "\U0001f9d1\U0001f3fe\u200d\U0001f373" +COOK_DARK_SKIN_TONE = "\U0001f9d1\U0001f3ff\u200d\U0001f373" +MAN_COOK = "\U0001f468\u200d\U0001f373" +MAN_COOK_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f373" +MAN_COOK_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f373" +MAN_COOK_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f373" +MAN_COOK_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f373" +MAN_COOK_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f373" +WOMAN_COOK = "\U0001f469\u200d\U0001f373" +WOMAN_COOK_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f373" +WOMAN_COOK_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f373" +WOMAN_COOK_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f373" +WOMAN_COOK_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f373" +WOMAN_COOK_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f373" +MECHANIC = "\U0001f9d1\u200d\U0001f527" +MECHANIC_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fb\u200d\U0001f527" +MECHANIC_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fc\u200d\U0001f527" +MECHANIC_MEDIUM_SKIN_TONE = "\U0001f9d1\U0001f3fd\u200d\U0001f527" +MECHANIC_MEDIUM_DARK_SKIN_TONE = "\U0001f9d1\U0001f3fe\u200d\U0001f527" +MECHANIC_DARK_SKIN_TONE = "\U0001f9d1\U0001f3ff\u200d\U0001f527" +MAN_MECHANIC = "\U0001f468\u200d\U0001f527" +MAN_MECHANIC_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f527" +MAN_MECHANIC_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f527" +MAN_MECHANIC_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f527" +MAN_MECHANIC_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f527" +MAN_MECHANIC_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f527" +WOMAN_MECHANIC = "\U0001f469\u200d\U0001f527" +WOMAN_MECHANIC_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f527" +WOMAN_MECHANIC_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f527" +WOMAN_MECHANIC_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f527" +WOMAN_MECHANIC_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f527" +WOMAN_MECHANIC_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f527" +FACTORY_WORKER = "\U0001f9d1\u200d\U0001f3ed" +FACTORY_WORKER_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fb\u200d\U0001f3ed" +FACTORY_WORKER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fc\u200d\U0001f3ed" +FACTORY_WORKER_MEDIUM_SKIN_TONE = "\U0001f9d1\U0001f3fd\u200d\U0001f3ed" +FACTORY_WORKER_MEDIUM_DARK_SKIN_TONE = "\U0001f9d1\U0001f3fe\u200d\U0001f3ed" +FACTORY_WORKER_DARK_SKIN_TONE = "\U0001f9d1\U0001f3ff\u200d\U0001f3ed" +MAN_FACTORY_WORKER = "\U0001f468\u200d\U0001f3ed" +MAN_FACTORY_WORKER_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f3ed" +MAN_FACTORY_WORKER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f3ed" +MAN_FACTORY_WORKER_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f3ed" +MAN_FACTORY_WORKER_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f3ed" +MAN_FACTORY_WORKER_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f3ed" +WOMAN_FACTORY_WORKER = "\U0001f469\u200d\U0001f3ed" +WOMAN_FACTORY_WORKER_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f3ed" +WOMAN_FACTORY_WORKER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f3ed" +WOMAN_FACTORY_WORKER_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f3ed" +WOMAN_FACTORY_WORKER_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f3ed" +WOMAN_FACTORY_WORKER_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f3ed" +OFFICE_WORKER = "\U0001f9d1\u200d\U0001f4bc" +OFFICE_WORKER_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fb\u200d\U0001f4bc" +OFFICE_WORKER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fc\u200d\U0001f4bc" +OFFICE_WORKER_MEDIUM_SKIN_TONE = "\U0001f9d1\U0001f3fd\u200d\U0001f4bc" +OFFICE_WORKER_MEDIUM_DARK_SKIN_TONE = "\U0001f9d1\U0001f3fe\u200d\U0001f4bc" +OFFICE_WORKER_DARK_SKIN_TONE = "\U0001f9d1\U0001f3ff\u200d\U0001f4bc" +MAN_OFFICE_WORKER = "\U0001f468\u200d\U0001f4bc" +MAN_OFFICE_WORKER_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f4bc" +MAN_OFFICE_WORKER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f4bc" +MAN_OFFICE_WORKER_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f4bc" +MAN_OFFICE_WORKER_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f4bc" +MAN_OFFICE_WORKER_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f4bc" +WOMAN_OFFICE_WORKER = "\U0001f469\u200d\U0001f4bc" +WOMAN_OFFICE_WORKER_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f4bc" +WOMAN_OFFICE_WORKER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f4bc" +WOMAN_OFFICE_WORKER_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f4bc" +WOMAN_OFFICE_WORKER_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f4bc" +WOMAN_OFFICE_WORKER_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f4bc" +SCIENTIST = "\U0001f9d1\u200d\U0001f52c" +SCIENTIST_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fb\u200d\U0001f52c" +SCIENTIST_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fc\u200d\U0001f52c" +SCIENTIST_MEDIUM_SKIN_TONE = "\U0001f9d1\U0001f3fd\u200d\U0001f52c" +SCIENTIST_MEDIUM_DARK_SKIN_TONE = "\U0001f9d1\U0001f3fe\u200d\U0001f52c" +SCIENTIST_DARK_SKIN_TONE = "\U0001f9d1\U0001f3ff\u200d\U0001f52c" +MAN_SCIENTIST = "\U0001f468\u200d\U0001f52c" +MAN_SCIENTIST_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f52c" +MAN_SCIENTIST_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f52c" +MAN_SCIENTIST_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f52c" +MAN_SCIENTIST_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f52c" +MAN_SCIENTIST_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f52c" +WOMAN_SCIENTIST = "\U0001f469\u200d\U0001f52c" +WOMAN_SCIENTIST_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f52c" +WOMAN_SCIENTIST_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f52c" +WOMAN_SCIENTIST_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f52c" +WOMAN_SCIENTIST_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f52c" +WOMAN_SCIENTIST_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f52c" +TECHNOLOGIST = "\U0001f9d1\u200d\U0001f4bb" +TECHNOLOGIST_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fb\u200d\U0001f4bb" +TECHNOLOGIST_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fc\u200d\U0001f4bb" +TECHNOLOGIST_MEDIUM_SKIN_TONE = "\U0001f9d1\U0001f3fd\u200d\U0001f4bb" +TECHNOLOGIST_MEDIUM_DARK_SKIN_TONE = "\U0001f9d1\U0001f3fe\u200d\U0001f4bb" +TECHNOLOGIST_DARK_SKIN_TONE = "\U0001f9d1\U0001f3ff\u200d\U0001f4bb" +MAN_TECHNOLOGIST = "\U0001f468\u200d\U0001f4bb" +MAN_TECHNOLOGIST_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f4bb" +MAN_TECHNOLOGIST_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f4bb" +MAN_TECHNOLOGIST_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f4bb" +MAN_TECHNOLOGIST_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f4bb" +MAN_TECHNOLOGIST_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f4bb" +WOMAN_TECHNOLOGIST = "\U0001f469\u200d\U0001f4bb" +WOMAN_TECHNOLOGIST_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f4bb" +WOMAN_TECHNOLOGIST_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f4bb" +WOMAN_TECHNOLOGIST_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f4bb" +WOMAN_TECHNOLOGIST_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f4bb" +WOMAN_TECHNOLOGIST_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f4bb" +SINGER = "\U0001f9d1\u200d\U0001f3a4" +SINGER_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fb\u200d\U0001f3a4" +SINGER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fc\u200d\U0001f3a4" +SINGER_MEDIUM_SKIN_TONE = "\U0001f9d1\U0001f3fd\u200d\U0001f3a4" +SINGER_MEDIUM_DARK_SKIN_TONE = "\U0001f9d1\U0001f3fe\u200d\U0001f3a4" +SINGER_DARK_SKIN_TONE = "\U0001f9d1\U0001f3ff\u200d\U0001f3a4" +MAN_SINGER = "\U0001f468\u200d\U0001f3a4" +MAN_SINGER_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f3a4" +MAN_SINGER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f3a4" +MAN_SINGER_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f3a4" +MAN_SINGER_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f3a4" +MAN_SINGER_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f3a4" +WOMAN_SINGER = "\U0001f469\u200d\U0001f3a4" +WOMAN_SINGER_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f3a4" +WOMAN_SINGER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f3a4" +WOMAN_SINGER_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f3a4" +WOMAN_SINGER_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f3a4" +WOMAN_SINGER_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f3a4" +ARTIST = "\U0001f9d1\u200d\U0001f3a8" +ARTIST_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fb\u200d\U0001f3a8" +ARTIST_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fc\u200d\U0001f3a8" +ARTIST_MEDIUM_SKIN_TONE = "\U0001f9d1\U0001f3fd\u200d\U0001f3a8" +ARTIST_MEDIUM_DARK_SKIN_TONE = "\U0001f9d1\U0001f3fe\u200d\U0001f3a8" +ARTIST_DARK_SKIN_TONE = "\U0001f9d1\U0001f3ff\u200d\U0001f3a8" +MAN_ARTIST = "\U0001f468\u200d\U0001f3a8" +MAN_ARTIST_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f3a8" +MAN_ARTIST_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f3a8" +MAN_ARTIST_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f3a8" +MAN_ARTIST_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f3a8" +MAN_ARTIST_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f3a8" +WOMAN_ARTIST = "\U0001f469\u200d\U0001f3a8" +WOMAN_ARTIST_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f3a8" +WOMAN_ARTIST_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f3a8" +WOMAN_ARTIST_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f3a8" +WOMAN_ARTIST_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f3a8" +WOMAN_ARTIST_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f3a8" +PILOT = "\U0001f9d1\u200d\u2708\ufe0f" +PILOT_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fb\u200d\u2708\ufe0f" +PILOT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fc\u200d\u2708\ufe0f" +PILOT_MEDIUM_SKIN_TONE = "\U0001f9d1\U0001f3fd\u200d\u2708\ufe0f" +PILOT_MEDIUM_DARK_SKIN_TONE = "\U0001f9d1\U0001f3fe\u200d\u2708\ufe0f" +PILOT_DARK_SKIN_TONE = "\U0001f9d1\U0001f3ff\u200d\u2708\ufe0f" +MAN_PILOT = "\U0001f468\u200d\u2708\ufe0f" +MAN_PILOT_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\u2708\ufe0f" +MAN_PILOT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\u2708\ufe0f" +MAN_PILOT_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\u2708\ufe0f" +MAN_PILOT_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\u2708\ufe0f" +MAN_PILOT_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\u2708\ufe0f" +WOMAN_PILOT = "\U0001f469\u200d\u2708\ufe0f" +WOMAN_PILOT_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\u2708\ufe0f" +WOMAN_PILOT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\u2708\ufe0f" +WOMAN_PILOT_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\u2708\ufe0f" +WOMAN_PILOT_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\u2708\ufe0f" +WOMAN_PILOT_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\u2708\ufe0f" +ASTRONAUT = "\U0001f9d1\u200d\U0001f680" +ASTRONAUT_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fb\u200d\U0001f680" +ASTRONAUT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fc\u200d\U0001f680" +ASTRONAUT_MEDIUM_SKIN_TONE = "\U0001f9d1\U0001f3fd\u200d\U0001f680" +ASTRONAUT_MEDIUM_DARK_SKIN_TONE = "\U0001f9d1\U0001f3fe\u200d\U0001f680" +ASTRONAUT_DARK_SKIN_TONE = "\U0001f9d1\U0001f3ff\u200d\U0001f680" +MAN_ASTRONAUT = "\U0001f468\u200d\U0001f680" +MAN_ASTRONAUT_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f680" +MAN_ASTRONAUT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f680" +MAN_ASTRONAUT_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f680" +MAN_ASTRONAUT_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f680" +MAN_ASTRONAUT_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f680" +WOMAN_ASTRONAUT = "\U0001f469\u200d\U0001f680" +WOMAN_ASTRONAUT_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f680" +WOMAN_ASTRONAUT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f680" +WOMAN_ASTRONAUT_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f680" +WOMAN_ASTRONAUT_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f680" +WOMAN_ASTRONAUT_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f680" +FIREFIGHTER = "\U0001f9d1\u200d\U0001f692" +FIREFIGHTER_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fb\u200d\U0001f692" +FIREFIGHTER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fc\u200d\U0001f692" +FIREFIGHTER_MEDIUM_SKIN_TONE = "\U0001f9d1\U0001f3fd\u200d\U0001f692" +FIREFIGHTER_MEDIUM_DARK_SKIN_TONE = "\U0001f9d1\U0001f3fe\u200d\U0001f692" +FIREFIGHTER_DARK_SKIN_TONE = "\U0001f9d1\U0001f3ff\u200d\U0001f692" +MAN_FIREFIGHTER = "\U0001f468\u200d\U0001f692" +MAN_FIREFIGHTER_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f692" +MAN_FIREFIGHTER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f692" +MAN_FIREFIGHTER_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f692" +MAN_FIREFIGHTER_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f692" +MAN_FIREFIGHTER_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f692" +WOMAN_FIREFIGHTER = "\U0001f469\u200d\U0001f692" +WOMAN_FIREFIGHTER_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f692" +WOMAN_FIREFIGHTER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f692" +WOMAN_FIREFIGHTER_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f692" +WOMAN_FIREFIGHTER_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f692" +WOMAN_FIREFIGHTER_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f692" +POLICE_OFFICER = "\U0001f46e" +POLICE_OFFICER_LIGHT_SKIN_TONE = "\U0001f46e\U0001f3fb" +POLICE_OFFICER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f46e\U0001f3fc" +POLICE_OFFICER_MEDIUM_SKIN_TONE = "\U0001f46e\U0001f3fd" +POLICE_OFFICER_MEDIUM_DARK_SKIN_TONE = "\U0001f46e\U0001f3fe" +POLICE_OFFICER_DARK_SKIN_TONE = "\U0001f46e\U0001f3ff" +MAN_POLICE_OFFICER = "\U0001f46e\u200d\u2642\ufe0f" +MAN_POLICE_OFFICER_LIGHT_SKIN_TONE = "\U0001f46e\U0001f3fb\u200d\u2642\ufe0f" +MAN_POLICE_OFFICER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f46e\U0001f3fc\u200d\u2642\ufe0f" +MAN_POLICE_OFFICER_MEDIUM_SKIN_TONE = "\U0001f46e\U0001f3fd\u200d\u2642\ufe0f" +MAN_POLICE_OFFICER_MEDIUM_DARK_SKIN_TONE = "\U0001f46e\U0001f3fe\u200d\u2642\ufe0f" +MAN_POLICE_OFFICER_DARK_SKIN_TONE = "\U0001f46e\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_POLICE_OFFICER = "\U0001f46e\u200d\u2640\ufe0f" +WOMAN_POLICE_OFFICER_LIGHT_SKIN_TONE = "\U0001f46e\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_POLICE_OFFICER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f46e\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_POLICE_OFFICER_MEDIUM_SKIN_TONE = "\U0001f46e\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_POLICE_OFFICER_MEDIUM_DARK_SKIN_TONE = "\U0001f46e\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_POLICE_OFFICER_DARK_SKIN_TONE = "\U0001f46e\U0001f3ff\u200d\u2640\ufe0f" +DETECTIVE = "\U0001f575\ufe0f" +DETECTIVE_LIGHT_SKIN_TONE = "\U0001f575\U0001f3fb" +DETECTIVE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f575\U0001f3fc" +DETECTIVE_MEDIUM_SKIN_TONE = "\U0001f575\U0001f3fd" +DETECTIVE_MEDIUM_DARK_SKIN_TONE = "\U0001f575\U0001f3fe" +DETECTIVE_DARK_SKIN_TONE = "\U0001f575\U0001f3ff" +MAN_DETECTIVE = "\U0001f575\ufe0f\u200d\u2642\ufe0f" +MAN_DETECTIVE_LIGHT_SKIN_TONE = "\U0001f575\U0001f3fb\u200d\u2642\ufe0f" +MAN_DETECTIVE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f575\U0001f3fc\u200d\u2642\ufe0f" +MAN_DETECTIVE_MEDIUM_SKIN_TONE = "\U0001f575\U0001f3fd\u200d\u2642\ufe0f" +MAN_DETECTIVE_MEDIUM_DARK_SKIN_TONE = "\U0001f575\U0001f3fe\u200d\u2642\ufe0f" +MAN_DETECTIVE_DARK_SKIN_TONE = "\U0001f575\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_DETECTIVE = "\U0001f575\ufe0f\u200d\u2640\ufe0f" +WOMAN_DETECTIVE_LIGHT_SKIN_TONE = "\U0001f575\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_DETECTIVE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f575\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_DETECTIVE_MEDIUM_SKIN_TONE = "\U0001f575\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_DETECTIVE_MEDIUM_DARK_SKIN_TONE = "\U0001f575\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_DETECTIVE_DARK_SKIN_TONE = "\U0001f575\U0001f3ff\u200d\u2640\ufe0f" +GUARD = "\U0001f482" +GUARD_LIGHT_SKIN_TONE = "\U0001f482\U0001f3fb" +GUARD_MEDIUM_LIGHT_SKIN_TONE = "\U0001f482\U0001f3fc" +GUARD_MEDIUM_SKIN_TONE = "\U0001f482\U0001f3fd" +GUARD_MEDIUM_DARK_SKIN_TONE = "\U0001f482\U0001f3fe" +GUARD_DARK_SKIN_TONE = "\U0001f482\U0001f3ff" +MAN_GUARD = "\U0001f482\u200d\u2642\ufe0f" +MAN_GUARD_LIGHT_SKIN_TONE = "\U0001f482\U0001f3fb\u200d\u2642\ufe0f" +MAN_GUARD_MEDIUM_LIGHT_SKIN_TONE = "\U0001f482\U0001f3fc\u200d\u2642\ufe0f" +MAN_GUARD_MEDIUM_SKIN_TONE = "\U0001f482\U0001f3fd\u200d\u2642\ufe0f" +MAN_GUARD_MEDIUM_DARK_SKIN_TONE = "\U0001f482\U0001f3fe\u200d\u2642\ufe0f" +MAN_GUARD_DARK_SKIN_TONE = "\U0001f482\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_GUARD = "\U0001f482\u200d\u2640\ufe0f" +WOMAN_GUARD_LIGHT_SKIN_TONE = "\U0001f482\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_GUARD_MEDIUM_LIGHT_SKIN_TONE = "\U0001f482\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_GUARD_MEDIUM_SKIN_TONE = "\U0001f482\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_GUARD_MEDIUM_DARK_SKIN_TONE = "\U0001f482\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_GUARD_DARK_SKIN_TONE = "\U0001f482\U0001f3ff\u200d\u2640\ufe0f" +NINJA = "\U0001f977" +NINJA_LIGHT_SKIN_TONE = "\U0001f977\U0001f3fb" +NINJA_MEDIUM_LIGHT_SKIN_TONE = "\U0001f977\U0001f3fc" +NINJA_MEDIUM_SKIN_TONE = "\U0001f977\U0001f3fd" +NINJA_MEDIUM_DARK_SKIN_TONE = "\U0001f977\U0001f3fe" +NINJA_DARK_SKIN_TONE = "\U0001f977\U0001f3ff" +CONSTRUCTION_WORKER = "\U0001f477" +CONSTRUCTION_WORKER_LIGHT_SKIN_TONE = "\U0001f477\U0001f3fb" +CONSTRUCTION_WORKER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f477\U0001f3fc" +CONSTRUCTION_WORKER_MEDIUM_SKIN_TONE = "\U0001f477\U0001f3fd" +CONSTRUCTION_WORKER_MEDIUM_DARK_SKIN_TONE = "\U0001f477\U0001f3fe" +CONSTRUCTION_WORKER_DARK_SKIN_TONE = "\U0001f477\U0001f3ff" +MAN_CONSTRUCTION_WORKER = "\U0001f477\u200d\u2642\ufe0f" +MAN_CONSTRUCTION_WORKER_LIGHT_SKIN_TONE = "\U0001f477\U0001f3fb\u200d\u2642\ufe0f" +MAN_CONSTRUCTION_WORKER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f477\U0001f3fc\u200d\u2642\ufe0f" +MAN_CONSTRUCTION_WORKER_MEDIUM_SKIN_TONE = "\U0001f477\U0001f3fd\u200d\u2642\ufe0f" +MAN_CONSTRUCTION_WORKER_MEDIUM_DARK_SKIN_TONE = "\U0001f477\U0001f3fe\u200d\u2642\ufe0f" +MAN_CONSTRUCTION_WORKER_DARK_SKIN_TONE = "\U0001f477\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_CONSTRUCTION_WORKER = "\U0001f477\u200d\u2640\ufe0f" +WOMAN_CONSTRUCTION_WORKER_LIGHT_SKIN_TONE = "\U0001f477\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_CONSTRUCTION_WORKER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f477\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_CONSTRUCTION_WORKER_MEDIUM_SKIN_TONE = "\U0001f477\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_CONSTRUCTION_WORKER_MEDIUM_DARK_SKIN_TONE = "\U0001f477\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_CONSTRUCTION_WORKER_DARK_SKIN_TONE = "\U0001f477\U0001f3ff\u200d\u2640\ufe0f" +PERSON_WITH_CROWN = "\U0001fac5" +PERSON_WITH_CROWN_LIGHT_SKIN_TONE = "\U0001fac5\U0001f3fb" +PERSON_WITH_CROWN_MEDIUM_LIGHT_SKIN_TONE = "\U0001fac5\U0001f3fc" +PERSON_WITH_CROWN_MEDIUM_SKIN_TONE = "\U0001fac5\U0001f3fd" +PERSON_WITH_CROWN_MEDIUM_DARK_SKIN_TONE = "\U0001fac5\U0001f3fe" +PERSON_WITH_CROWN_DARK_SKIN_TONE = "\U0001fac5\U0001f3ff" +PRINCE = "\U0001f934" +PRINCE_LIGHT_SKIN_TONE = "\U0001f934\U0001f3fb" +PRINCE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f934\U0001f3fc" +PRINCE_MEDIUM_SKIN_TONE = "\U0001f934\U0001f3fd" +PRINCE_MEDIUM_DARK_SKIN_TONE = "\U0001f934\U0001f3fe" +PRINCE_DARK_SKIN_TONE = "\U0001f934\U0001f3ff" +PRINCESS = "\U0001f478" +PRINCESS_LIGHT_SKIN_TONE = "\U0001f478\U0001f3fb" +PRINCESS_MEDIUM_LIGHT_SKIN_TONE = "\U0001f478\U0001f3fc" +PRINCESS_MEDIUM_SKIN_TONE = "\U0001f478\U0001f3fd" +PRINCESS_MEDIUM_DARK_SKIN_TONE = "\U0001f478\U0001f3fe" +PRINCESS_DARK_SKIN_TONE = "\U0001f478\U0001f3ff" +PERSON_WEARING_TURBAN = "\U0001f473" +PERSON_WEARING_TURBAN_LIGHT_SKIN_TONE = "\U0001f473\U0001f3fb" +PERSON_WEARING_TURBAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f473\U0001f3fc" +PERSON_WEARING_TURBAN_MEDIUM_SKIN_TONE = "\U0001f473\U0001f3fd" +PERSON_WEARING_TURBAN_MEDIUM_DARK_SKIN_TONE = "\U0001f473\U0001f3fe" +PERSON_WEARING_TURBAN_DARK_SKIN_TONE = "\U0001f473\U0001f3ff" +MAN_WEARING_TURBAN = "\U0001f473\u200d\u2642\ufe0f" +MAN_WEARING_TURBAN_LIGHT_SKIN_TONE = "\U0001f473\U0001f3fb\u200d\u2642\ufe0f" +MAN_WEARING_TURBAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f473\U0001f3fc\u200d\u2642\ufe0f" +MAN_WEARING_TURBAN_MEDIUM_SKIN_TONE = "\U0001f473\U0001f3fd\u200d\u2642\ufe0f" +MAN_WEARING_TURBAN_MEDIUM_DARK_SKIN_TONE = "\U0001f473\U0001f3fe\u200d\u2642\ufe0f" +MAN_WEARING_TURBAN_DARK_SKIN_TONE = "\U0001f473\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_WEARING_TURBAN = "\U0001f473\u200d\u2640\ufe0f" +WOMAN_WEARING_TURBAN_LIGHT_SKIN_TONE = "\U0001f473\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_WEARING_TURBAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f473\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_WEARING_TURBAN_MEDIUM_SKIN_TONE = "\U0001f473\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_WEARING_TURBAN_MEDIUM_DARK_SKIN_TONE = "\U0001f473\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_WEARING_TURBAN_DARK_SKIN_TONE = "\U0001f473\U0001f3ff\u200d\u2640\ufe0f" +PERSON_WITH_SKULLCAP = "\U0001f472" +PERSON_WITH_SKULLCAP_LIGHT_SKIN_TONE = "\U0001f472\U0001f3fb" +PERSON_WITH_SKULLCAP_MEDIUM_LIGHT_SKIN_TONE = "\U0001f472\U0001f3fc" +PERSON_WITH_SKULLCAP_MEDIUM_SKIN_TONE = "\U0001f472\U0001f3fd" +PERSON_WITH_SKULLCAP_MEDIUM_DARK_SKIN_TONE = "\U0001f472\U0001f3fe" +PERSON_WITH_SKULLCAP_DARK_SKIN_TONE = "\U0001f472\U0001f3ff" +WOMAN_WITH_HEADSCARF = "\U0001f9d5" +WOMAN_WITH_HEADSCARF_LIGHT_SKIN_TONE = "\U0001f9d5\U0001f3fb" +WOMAN_WITH_HEADSCARF_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d5\U0001f3fc" +WOMAN_WITH_HEADSCARF_MEDIUM_SKIN_TONE = "\U0001f9d5\U0001f3fd" +WOMAN_WITH_HEADSCARF_MEDIUM_DARK_SKIN_TONE = "\U0001f9d5\U0001f3fe" +WOMAN_WITH_HEADSCARF_DARK_SKIN_TONE = "\U0001f9d5\U0001f3ff" +PERSON_IN_TUXEDO = "\U0001f935" +PERSON_IN_TUXEDO_LIGHT_SKIN_TONE = "\U0001f935\U0001f3fb" +PERSON_IN_TUXEDO_MEDIUM_LIGHT_SKIN_TONE = "\U0001f935\U0001f3fc" +PERSON_IN_TUXEDO_MEDIUM_SKIN_TONE = "\U0001f935\U0001f3fd" +PERSON_IN_TUXEDO_MEDIUM_DARK_SKIN_TONE = "\U0001f935\U0001f3fe" +PERSON_IN_TUXEDO_DARK_SKIN_TONE = "\U0001f935\U0001f3ff" +MAN_IN_TUXEDO = "\U0001f935\u200d\u2642\ufe0f" +MAN_IN_TUXEDO_LIGHT_SKIN_TONE = "\U0001f935\U0001f3fb\u200d\u2642\ufe0f" +MAN_IN_TUXEDO_MEDIUM_LIGHT_SKIN_TONE = "\U0001f935\U0001f3fc\u200d\u2642\ufe0f" +MAN_IN_TUXEDO_MEDIUM_SKIN_TONE = "\U0001f935\U0001f3fd\u200d\u2642\ufe0f" +MAN_IN_TUXEDO_MEDIUM_DARK_SKIN_TONE = "\U0001f935\U0001f3fe\u200d\u2642\ufe0f" +MAN_IN_TUXEDO_DARK_SKIN_TONE = "\U0001f935\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_IN_TUXEDO = "\U0001f935\u200d\u2640\ufe0f" +WOMAN_IN_TUXEDO_LIGHT_SKIN_TONE = "\U0001f935\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_IN_TUXEDO_MEDIUM_LIGHT_SKIN_TONE = "\U0001f935\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_IN_TUXEDO_MEDIUM_SKIN_TONE = "\U0001f935\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_IN_TUXEDO_MEDIUM_DARK_SKIN_TONE = "\U0001f935\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_IN_TUXEDO_DARK_SKIN_TONE = "\U0001f935\U0001f3ff\u200d\u2640\ufe0f" +PERSON_WITH_VEIL = "\U0001f470" +PERSON_WITH_VEIL_LIGHT_SKIN_TONE = "\U0001f470\U0001f3fb" +PERSON_WITH_VEIL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f470\U0001f3fc" +PERSON_WITH_VEIL_MEDIUM_SKIN_TONE = "\U0001f470\U0001f3fd" +PERSON_WITH_VEIL_MEDIUM_DARK_SKIN_TONE = "\U0001f470\U0001f3fe" +PERSON_WITH_VEIL_DARK_SKIN_TONE = "\U0001f470\U0001f3ff" +MAN_WITH_VEIL = "\U0001f470\u200d\u2642\ufe0f" +MAN_WITH_VEIL_LIGHT_SKIN_TONE = "\U0001f470\U0001f3fb\u200d\u2642\ufe0f" +MAN_WITH_VEIL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f470\U0001f3fc\u200d\u2642\ufe0f" +MAN_WITH_VEIL_MEDIUM_SKIN_TONE = "\U0001f470\U0001f3fd\u200d\u2642\ufe0f" +MAN_WITH_VEIL_MEDIUM_DARK_SKIN_TONE = "\U0001f470\U0001f3fe\u200d\u2642\ufe0f" +MAN_WITH_VEIL_DARK_SKIN_TONE = "\U0001f470\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_WITH_VEIL = "\U0001f470\u200d\u2640\ufe0f" +WOMAN_WITH_VEIL_LIGHT_SKIN_TONE = "\U0001f470\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_WITH_VEIL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f470\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_WITH_VEIL_MEDIUM_SKIN_TONE = "\U0001f470\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_WITH_VEIL_MEDIUM_DARK_SKIN_TONE = "\U0001f470\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_WITH_VEIL_DARK_SKIN_TONE = "\U0001f470\U0001f3ff\u200d\u2640\ufe0f" +PREGNANT_WOMAN = "\U0001f930" +PREGNANT_WOMAN_LIGHT_SKIN_TONE = "\U0001f930\U0001f3fb" +PREGNANT_WOMAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f930\U0001f3fc" +PREGNANT_WOMAN_MEDIUM_SKIN_TONE = "\U0001f930\U0001f3fd" +PREGNANT_WOMAN_MEDIUM_DARK_SKIN_TONE = "\U0001f930\U0001f3fe" +PREGNANT_WOMAN_DARK_SKIN_TONE = "\U0001f930\U0001f3ff" +PREGNANT_MAN = "\U0001fac3" +PREGNANT_MAN_LIGHT_SKIN_TONE = "\U0001fac3\U0001f3fb" +PREGNANT_MAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001fac3\U0001f3fc" +PREGNANT_MAN_MEDIUM_SKIN_TONE = "\U0001fac3\U0001f3fd" +PREGNANT_MAN_MEDIUM_DARK_SKIN_TONE = "\U0001fac3\U0001f3fe" +PREGNANT_MAN_DARK_SKIN_TONE = "\U0001fac3\U0001f3ff" +PREGNANT_PERSON = "\U0001fac4" +PREGNANT_PERSON_LIGHT_SKIN_TONE = "\U0001fac4\U0001f3fb" +PREGNANT_PERSON_MEDIUM_LIGHT_SKIN_TONE = "\U0001fac4\U0001f3fc" +PREGNANT_PERSON_MEDIUM_SKIN_TONE = "\U0001fac4\U0001f3fd" +PREGNANT_PERSON_MEDIUM_DARK_SKIN_TONE = "\U0001fac4\U0001f3fe" +PREGNANT_PERSON_DARK_SKIN_TONE = "\U0001fac4\U0001f3ff" +BREAST_FEEDING = "\U0001f931" +BREAST_FEEDING_LIGHT_SKIN_TONE = "\U0001f931\U0001f3fb" +BREAST_FEEDING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f931\U0001f3fc" +BREAST_FEEDING_MEDIUM_SKIN_TONE = "\U0001f931\U0001f3fd" +BREAST_FEEDING_MEDIUM_DARK_SKIN_TONE = "\U0001f931\U0001f3fe" +BREAST_FEEDING_DARK_SKIN_TONE = "\U0001f931\U0001f3ff" +WOMAN_FEEDING_BABY = "\U0001f469\u200d\U0001f37c" +WOMAN_FEEDING_BABY_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f37c" +WOMAN_FEEDING_BABY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f37c" +WOMAN_FEEDING_BABY_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f37c" +WOMAN_FEEDING_BABY_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f37c" +WOMAN_FEEDING_BABY_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f37c" +MAN_FEEDING_BABY = "\U0001f468\u200d\U0001f37c" +MAN_FEEDING_BABY_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f37c" +MAN_FEEDING_BABY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f37c" +MAN_FEEDING_BABY_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f37c" +MAN_FEEDING_BABY_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f37c" +MAN_FEEDING_BABY_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f37c" +PERSON_FEEDING_BABY = "\U0001f9d1\u200d\U0001f37c" +PERSON_FEEDING_BABY_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fb\u200d\U0001f37c" +PERSON_FEEDING_BABY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fc\u200d\U0001f37c" +PERSON_FEEDING_BABY_MEDIUM_SKIN_TONE = "\U0001f9d1\U0001f3fd\u200d\U0001f37c" +PERSON_FEEDING_BABY_MEDIUM_DARK_SKIN_TONE = "\U0001f9d1\U0001f3fe\u200d\U0001f37c" +PERSON_FEEDING_BABY_DARK_SKIN_TONE = "\U0001f9d1\U0001f3ff\u200d\U0001f37c" +BABY_ANGEL = "\U0001f47c" +BABY_ANGEL_LIGHT_SKIN_TONE = "\U0001f47c\U0001f3fb" +BABY_ANGEL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f47c\U0001f3fc" +BABY_ANGEL_MEDIUM_SKIN_TONE = "\U0001f47c\U0001f3fd" +BABY_ANGEL_MEDIUM_DARK_SKIN_TONE = "\U0001f47c\U0001f3fe" +BABY_ANGEL_DARK_SKIN_TONE = "\U0001f47c\U0001f3ff" +SANTA_CLAUS = "\U0001f385" +SANTA_CLAUS_LIGHT_SKIN_TONE = "\U0001f385\U0001f3fb" +SANTA_CLAUS_MEDIUM_LIGHT_SKIN_TONE = "\U0001f385\U0001f3fc" +SANTA_CLAUS_MEDIUM_SKIN_TONE = "\U0001f385\U0001f3fd" +SANTA_CLAUS_MEDIUM_DARK_SKIN_TONE = "\U0001f385\U0001f3fe" +SANTA_CLAUS_DARK_SKIN_TONE = "\U0001f385\U0001f3ff" +MRS_CLAUS = "\U0001f936" +MRS_CLAUS_LIGHT_SKIN_TONE = "\U0001f936\U0001f3fb" +MRS_CLAUS_MEDIUM_LIGHT_SKIN_TONE = "\U0001f936\U0001f3fc" +MRS_CLAUS_MEDIUM_SKIN_TONE = "\U0001f936\U0001f3fd" +MRS_CLAUS_MEDIUM_DARK_SKIN_TONE = "\U0001f936\U0001f3fe" +MRS_CLAUS_DARK_SKIN_TONE = "\U0001f936\U0001f3ff" +MX_CLAUS = "\U0001f9d1\u200d\U0001f384" +MX_CLAUS_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fb\u200d\U0001f384" +MX_CLAUS_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fc\u200d\U0001f384" +MX_CLAUS_MEDIUM_SKIN_TONE = "\U0001f9d1\U0001f3fd\u200d\U0001f384" +MX_CLAUS_MEDIUM_DARK_SKIN_TONE = "\U0001f9d1\U0001f3fe\u200d\U0001f384" +MX_CLAUS_DARK_SKIN_TONE = "\U0001f9d1\U0001f3ff\u200d\U0001f384" +SUPERHERO = "\U0001f9b8" +SUPERHERO_LIGHT_SKIN_TONE = "\U0001f9b8\U0001f3fb" +SUPERHERO_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9b8\U0001f3fc" +SUPERHERO_MEDIUM_SKIN_TONE = "\U0001f9b8\U0001f3fd" +SUPERHERO_MEDIUM_DARK_SKIN_TONE = "\U0001f9b8\U0001f3fe" +SUPERHERO_DARK_SKIN_TONE = "\U0001f9b8\U0001f3ff" +MAN_SUPERHERO = "\U0001f9b8\u200d\u2642\ufe0f" +MAN_SUPERHERO_LIGHT_SKIN_TONE = "\U0001f9b8\U0001f3fb\u200d\u2642\ufe0f" +MAN_SUPERHERO_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9b8\U0001f3fc\u200d\u2642\ufe0f" +MAN_SUPERHERO_MEDIUM_SKIN_TONE = "\U0001f9b8\U0001f3fd\u200d\u2642\ufe0f" +MAN_SUPERHERO_MEDIUM_DARK_SKIN_TONE = "\U0001f9b8\U0001f3fe\u200d\u2642\ufe0f" +MAN_SUPERHERO_DARK_SKIN_TONE = "\U0001f9b8\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_SUPERHERO = "\U0001f9b8\u200d\u2640\ufe0f" +WOMAN_SUPERHERO_LIGHT_SKIN_TONE = "\U0001f9b8\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_SUPERHERO_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9b8\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_SUPERHERO_MEDIUM_SKIN_TONE = "\U0001f9b8\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_SUPERHERO_MEDIUM_DARK_SKIN_TONE = "\U0001f9b8\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_SUPERHERO_DARK_SKIN_TONE = "\U0001f9b8\U0001f3ff\u200d\u2640\ufe0f" +SUPERVILLAIN = "\U0001f9b9" +SUPERVILLAIN_LIGHT_SKIN_TONE = "\U0001f9b9\U0001f3fb" +SUPERVILLAIN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9b9\U0001f3fc" +SUPERVILLAIN_MEDIUM_SKIN_TONE = "\U0001f9b9\U0001f3fd" +SUPERVILLAIN_MEDIUM_DARK_SKIN_TONE = "\U0001f9b9\U0001f3fe" +SUPERVILLAIN_DARK_SKIN_TONE = "\U0001f9b9\U0001f3ff" +MAN_SUPERVILLAIN = "\U0001f9b9\u200d\u2642\ufe0f" +MAN_SUPERVILLAIN_LIGHT_SKIN_TONE = "\U0001f9b9\U0001f3fb\u200d\u2642\ufe0f" +MAN_SUPERVILLAIN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9b9\U0001f3fc\u200d\u2642\ufe0f" +MAN_SUPERVILLAIN_MEDIUM_SKIN_TONE = "\U0001f9b9\U0001f3fd\u200d\u2642\ufe0f" +MAN_SUPERVILLAIN_MEDIUM_DARK_SKIN_TONE = "\U0001f9b9\U0001f3fe\u200d\u2642\ufe0f" +MAN_SUPERVILLAIN_DARK_SKIN_TONE = "\U0001f9b9\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_SUPERVILLAIN = "\U0001f9b9\u200d\u2640\ufe0f" +WOMAN_SUPERVILLAIN_LIGHT_SKIN_TONE = "\U0001f9b9\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_SUPERVILLAIN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9b9\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_SUPERVILLAIN_MEDIUM_SKIN_TONE = "\U0001f9b9\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_SUPERVILLAIN_MEDIUM_DARK_SKIN_TONE = "\U0001f9b9\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_SUPERVILLAIN_DARK_SKIN_TONE = "\U0001f9b9\U0001f3ff\u200d\u2640\ufe0f" +MAGE = "\U0001f9d9" +MAGE_LIGHT_SKIN_TONE = "\U0001f9d9\U0001f3fb" +MAGE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d9\U0001f3fc" +MAGE_MEDIUM_SKIN_TONE = "\U0001f9d9\U0001f3fd" +MAGE_MEDIUM_DARK_SKIN_TONE = "\U0001f9d9\U0001f3fe" +MAGE_DARK_SKIN_TONE = "\U0001f9d9\U0001f3ff" +MAN_MAGE = "\U0001f9d9\u200d\u2642\ufe0f" +MAN_MAGE_LIGHT_SKIN_TONE = "\U0001f9d9\U0001f3fb\u200d\u2642\ufe0f" +MAN_MAGE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d9\U0001f3fc\u200d\u2642\ufe0f" +MAN_MAGE_MEDIUM_SKIN_TONE = "\U0001f9d9\U0001f3fd\u200d\u2642\ufe0f" +MAN_MAGE_MEDIUM_DARK_SKIN_TONE = "\U0001f9d9\U0001f3fe\u200d\u2642\ufe0f" +MAN_MAGE_DARK_SKIN_TONE = "\U0001f9d9\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_MAGE = "\U0001f9d9\u200d\u2640\ufe0f" +WOMAN_MAGE_LIGHT_SKIN_TONE = "\U0001f9d9\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_MAGE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d9\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_MAGE_MEDIUM_SKIN_TONE = "\U0001f9d9\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_MAGE_MEDIUM_DARK_SKIN_TONE = "\U0001f9d9\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_MAGE_DARK_SKIN_TONE = "\U0001f9d9\U0001f3ff\u200d\u2640\ufe0f" +FAIRY = "\U0001f9da" +FAIRY_LIGHT_SKIN_TONE = "\U0001f9da\U0001f3fb" +FAIRY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9da\U0001f3fc" +FAIRY_MEDIUM_SKIN_TONE = "\U0001f9da\U0001f3fd" +FAIRY_MEDIUM_DARK_SKIN_TONE = "\U0001f9da\U0001f3fe" +FAIRY_DARK_SKIN_TONE = "\U0001f9da\U0001f3ff" +MAN_FAIRY = "\U0001f9da\u200d\u2642\ufe0f" +MAN_FAIRY_LIGHT_SKIN_TONE = "\U0001f9da\U0001f3fb\u200d\u2642\ufe0f" +MAN_FAIRY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9da\U0001f3fc\u200d\u2642\ufe0f" +MAN_FAIRY_MEDIUM_SKIN_TONE = "\U0001f9da\U0001f3fd\u200d\u2642\ufe0f" +MAN_FAIRY_MEDIUM_DARK_SKIN_TONE = "\U0001f9da\U0001f3fe\u200d\u2642\ufe0f" +MAN_FAIRY_DARK_SKIN_TONE = "\U0001f9da\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_FAIRY = "\U0001f9da\u200d\u2640\ufe0f" +WOMAN_FAIRY_LIGHT_SKIN_TONE = "\U0001f9da\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_FAIRY_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9da\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_FAIRY_MEDIUM_SKIN_TONE = "\U0001f9da\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_FAIRY_MEDIUM_DARK_SKIN_TONE = "\U0001f9da\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_FAIRY_DARK_SKIN_TONE = "\U0001f9da\U0001f3ff\u200d\u2640\ufe0f" +VAMPIRE = "\U0001f9db" +VAMPIRE_LIGHT_SKIN_TONE = "\U0001f9db\U0001f3fb" +VAMPIRE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9db\U0001f3fc" +VAMPIRE_MEDIUM_SKIN_TONE = "\U0001f9db\U0001f3fd" +VAMPIRE_MEDIUM_DARK_SKIN_TONE = "\U0001f9db\U0001f3fe" +VAMPIRE_DARK_SKIN_TONE = "\U0001f9db\U0001f3ff" +MAN_VAMPIRE = "\U0001f9db\u200d\u2642\ufe0f" +MAN_VAMPIRE_LIGHT_SKIN_TONE = "\U0001f9db\U0001f3fb\u200d\u2642\ufe0f" +MAN_VAMPIRE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9db\U0001f3fc\u200d\u2642\ufe0f" +MAN_VAMPIRE_MEDIUM_SKIN_TONE = "\U0001f9db\U0001f3fd\u200d\u2642\ufe0f" +MAN_VAMPIRE_MEDIUM_DARK_SKIN_TONE = "\U0001f9db\U0001f3fe\u200d\u2642\ufe0f" +MAN_VAMPIRE_DARK_SKIN_TONE = "\U0001f9db\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_VAMPIRE = "\U0001f9db\u200d\u2640\ufe0f" +WOMAN_VAMPIRE_LIGHT_SKIN_TONE = "\U0001f9db\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_VAMPIRE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9db\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_VAMPIRE_MEDIUM_SKIN_TONE = "\U0001f9db\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_VAMPIRE_MEDIUM_DARK_SKIN_TONE = "\U0001f9db\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_VAMPIRE_DARK_SKIN_TONE = "\U0001f9db\U0001f3ff\u200d\u2640\ufe0f" +MERPERSON = "\U0001f9dc" +MERPERSON_LIGHT_SKIN_TONE = "\U0001f9dc\U0001f3fb" +MERPERSON_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9dc\U0001f3fc" +MERPERSON_MEDIUM_SKIN_TONE = "\U0001f9dc\U0001f3fd" +MERPERSON_MEDIUM_DARK_SKIN_TONE = "\U0001f9dc\U0001f3fe" +MERPERSON_DARK_SKIN_TONE = "\U0001f9dc\U0001f3ff" +MERMAN = "\U0001f9dc\u200d\u2642\ufe0f" +MERMAN_LIGHT_SKIN_TONE = "\U0001f9dc\U0001f3fb\u200d\u2642\ufe0f" +MERMAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9dc\U0001f3fc\u200d\u2642\ufe0f" +MERMAN_MEDIUM_SKIN_TONE = "\U0001f9dc\U0001f3fd\u200d\u2642\ufe0f" +MERMAN_MEDIUM_DARK_SKIN_TONE = "\U0001f9dc\U0001f3fe\u200d\u2642\ufe0f" +MERMAN_DARK_SKIN_TONE = "\U0001f9dc\U0001f3ff\u200d\u2642\ufe0f" +MERMAID = "\U0001f9dc\u200d\u2640\ufe0f" +MERMAID_LIGHT_SKIN_TONE = "\U0001f9dc\U0001f3fb\u200d\u2640\ufe0f" +MERMAID_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9dc\U0001f3fc\u200d\u2640\ufe0f" +MERMAID_MEDIUM_SKIN_TONE = "\U0001f9dc\U0001f3fd\u200d\u2640\ufe0f" +MERMAID_MEDIUM_DARK_SKIN_TONE = "\U0001f9dc\U0001f3fe\u200d\u2640\ufe0f" +MERMAID_DARK_SKIN_TONE = "\U0001f9dc\U0001f3ff\u200d\u2640\ufe0f" +ELF = "\U0001f9dd" +ELF_LIGHT_SKIN_TONE = "\U0001f9dd\U0001f3fb" +ELF_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9dd\U0001f3fc" +ELF_MEDIUM_SKIN_TONE = "\U0001f9dd\U0001f3fd" +ELF_MEDIUM_DARK_SKIN_TONE = "\U0001f9dd\U0001f3fe" +ELF_DARK_SKIN_TONE = "\U0001f9dd\U0001f3ff" +MAN_ELF = "\U0001f9dd\u200d\u2642\ufe0f" +MAN_ELF_LIGHT_SKIN_TONE = "\U0001f9dd\U0001f3fb\u200d\u2642\ufe0f" +MAN_ELF_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9dd\U0001f3fc\u200d\u2642\ufe0f" +MAN_ELF_MEDIUM_SKIN_TONE = "\U0001f9dd\U0001f3fd\u200d\u2642\ufe0f" +MAN_ELF_MEDIUM_DARK_SKIN_TONE = "\U0001f9dd\U0001f3fe\u200d\u2642\ufe0f" +MAN_ELF_DARK_SKIN_TONE = "\U0001f9dd\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_ELF = "\U0001f9dd\u200d\u2640\ufe0f" +WOMAN_ELF_LIGHT_SKIN_TONE = "\U0001f9dd\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_ELF_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9dd\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_ELF_MEDIUM_SKIN_TONE = "\U0001f9dd\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_ELF_MEDIUM_DARK_SKIN_TONE = "\U0001f9dd\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_ELF_DARK_SKIN_TONE = "\U0001f9dd\U0001f3ff\u200d\u2640\ufe0f" +GENIE = "\U0001f9de" +MAN_GENIE = "\U0001f9de\u200d\u2642\ufe0f" +WOMAN_GENIE = "\U0001f9de\u200d\u2640\ufe0f" +ZOMBIE = "\U0001f9df" +MAN_ZOMBIE = "\U0001f9df\u200d\u2642\ufe0f" +WOMAN_ZOMBIE = "\U0001f9df\u200d\u2640\ufe0f" +TROLL = "\U0001f9cc" +PERSON_GETTING_MASSAGE = "\U0001f486" +PERSON_GETTING_MASSAGE_LIGHT_SKIN_TONE = "\U0001f486\U0001f3fb" +PERSON_GETTING_MASSAGE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f486\U0001f3fc" +PERSON_GETTING_MASSAGE_MEDIUM_SKIN_TONE = "\U0001f486\U0001f3fd" +PERSON_GETTING_MASSAGE_MEDIUM_DARK_SKIN_TONE = "\U0001f486\U0001f3fe" +PERSON_GETTING_MASSAGE_DARK_SKIN_TONE = "\U0001f486\U0001f3ff" +MAN_GETTING_MASSAGE = "\U0001f486\u200d\u2642\ufe0f" +MAN_GETTING_MASSAGE_LIGHT_SKIN_TONE = "\U0001f486\U0001f3fb\u200d\u2642\ufe0f" +MAN_GETTING_MASSAGE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f486\U0001f3fc\u200d\u2642\ufe0f" +MAN_GETTING_MASSAGE_MEDIUM_SKIN_TONE = "\U0001f486\U0001f3fd\u200d\u2642\ufe0f" +MAN_GETTING_MASSAGE_MEDIUM_DARK_SKIN_TONE = "\U0001f486\U0001f3fe\u200d\u2642\ufe0f" +MAN_GETTING_MASSAGE_DARK_SKIN_TONE = "\U0001f486\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_GETTING_MASSAGE = "\U0001f486\u200d\u2640\ufe0f" +WOMAN_GETTING_MASSAGE_LIGHT_SKIN_TONE = "\U0001f486\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_GETTING_MASSAGE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f486\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_GETTING_MASSAGE_MEDIUM_SKIN_TONE = "\U0001f486\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_GETTING_MASSAGE_MEDIUM_DARK_SKIN_TONE = "\U0001f486\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_GETTING_MASSAGE_DARK_SKIN_TONE = "\U0001f486\U0001f3ff\u200d\u2640\ufe0f" +PERSON_GETTING_HAIRCUT = "\U0001f487" +PERSON_GETTING_HAIRCUT_LIGHT_SKIN_TONE = "\U0001f487\U0001f3fb" +PERSON_GETTING_HAIRCUT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f487\U0001f3fc" +PERSON_GETTING_HAIRCUT_MEDIUM_SKIN_TONE = "\U0001f487\U0001f3fd" +PERSON_GETTING_HAIRCUT_MEDIUM_DARK_SKIN_TONE = "\U0001f487\U0001f3fe" +PERSON_GETTING_HAIRCUT_DARK_SKIN_TONE = "\U0001f487\U0001f3ff" +MAN_GETTING_HAIRCUT = "\U0001f487\u200d\u2642\ufe0f" +MAN_GETTING_HAIRCUT_LIGHT_SKIN_TONE = "\U0001f487\U0001f3fb\u200d\u2642\ufe0f" +MAN_GETTING_HAIRCUT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f487\U0001f3fc\u200d\u2642\ufe0f" +MAN_GETTING_HAIRCUT_MEDIUM_SKIN_TONE = "\U0001f487\U0001f3fd\u200d\u2642\ufe0f" +MAN_GETTING_HAIRCUT_MEDIUM_DARK_SKIN_TONE = "\U0001f487\U0001f3fe\u200d\u2642\ufe0f" +MAN_GETTING_HAIRCUT_DARK_SKIN_TONE = "\U0001f487\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_GETTING_HAIRCUT = "\U0001f487\u200d\u2640\ufe0f" +WOMAN_GETTING_HAIRCUT_LIGHT_SKIN_TONE = "\U0001f487\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_GETTING_HAIRCUT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f487\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_GETTING_HAIRCUT_MEDIUM_SKIN_TONE = "\U0001f487\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_GETTING_HAIRCUT_MEDIUM_DARK_SKIN_TONE = "\U0001f487\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_GETTING_HAIRCUT_DARK_SKIN_TONE = "\U0001f487\U0001f3ff\u200d\u2640\ufe0f" +PERSON_WALKING = "\U0001f6b6" +PERSON_WALKING_LIGHT_SKIN_TONE = "\U0001f6b6\U0001f3fb" +PERSON_WALKING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f6b6\U0001f3fc" +PERSON_WALKING_MEDIUM_SKIN_TONE = "\U0001f6b6\U0001f3fd" +PERSON_WALKING_MEDIUM_DARK_SKIN_TONE = "\U0001f6b6\U0001f3fe" +PERSON_WALKING_DARK_SKIN_TONE = "\U0001f6b6\U0001f3ff" +MAN_WALKING = "\U0001f6b6\u200d\u2642\ufe0f" +MAN_WALKING_LIGHT_SKIN_TONE = "\U0001f6b6\U0001f3fb\u200d\u2642\ufe0f" +MAN_WALKING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f6b6\U0001f3fc\u200d\u2642\ufe0f" +MAN_WALKING_MEDIUM_SKIN_TONE = "\U0001f6b6\U0001f3fd\u200d\u2642\ufe0f" +MAN_WALKING_MEDIUM_DARK_SKIN_TONE = "\U0001f6b6\U0001f3fe\u200d\u2642\ufe0f" +MAN_WALKING_DARK_SKIN_TONE = "\U0001f6b6\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_WALKING = "\U0001f6b6\u200d\u2640\ufe0f" +WOMAN_WALKING_LIGHT_SKIN_TONE = "\U0001f6b6\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_WALKING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f6b6\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_WALKING_MEDIUM_SKIN_TONE = "\U0001f6b6\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_WALKING_MEDIUM_DARK_SKIN_TONE = "\U0001f6b6\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_WALKING_DARK_SKIN_TONE = "\U0001f6b6\U0001f3ff\u200d\u2640\ufe0f" +PERSON_STANDING = "\U0001f9cd" +PERSON_STANDING_LIGHT_SKIN_TONE = "\U0001f9cd\U0001f3fb" +PERSON_STANDING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9cd\U0001f3fc" +PERSON_STANDING_MEDIUM_SKIN_TONE = "\U0001f9cd\U0001f3fd" +PERSON_STANDING_MEDIUM_DARK_SKIN_TONE = "\U0001f9cd\U0001f3fe" +PERSON_STANDING_DARK_SKIN_TONE = "\U0001f9cd\U0001f3ff" +MAN_STANDING = "\U0001f9cd\u200d\u2642\ufe0f" +MAN_STANDING_LIGHT_SKIN_TONE = "\U0001f9cd\U0001f3fb\u200d\u2642\ufe0f" +MAN_STANDING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9cd\U0001f3fc\u200d\u2642\ufe0f" +MAN_STANDING_MEDIUM_SKIN_TONE = "\U0001f9cd\U0001f3fd\u200d\u2642\ufe0f" +MAN_STANDING_MEDIUM_DARK_SKIN_TONE = "\U0001f9cd\U0001f3fe\u200d\u2642\ufe0f" +MAN_STANDING_DARK_SKIN_TONE = "\U0001f9cd\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_STANDING = "\U0001f9cd\u200d\u2640\ufe0f" +WOMAN_STANDING_LIGHT_SKIN_TONE = "\U0001f9cd\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_STANDING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9cd\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_STANDING_MEDIUM_SKIN_TONE = "\U0001f9cd\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_STANDING_MEDIUM_DARK_SKIN_TONE = "\U0001f9cd\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_STANDING_DARK_SKIN_TONE = "\U0001f9cd\U0001f3ff\u200d\u2640\ufe0f" +PERSON_KNEELING = "\U0001f9ce" +PERSON_KNEELING_LIGHT_SKIN_TONE = "\U0001f9ce\U0001f3fb" +PERSON_KNEELING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9ce\U0001f3fc" +PERSON_KNEELING_MEDIUM_SKIN_TONE = "\U0001f9ce\U0001f3fd" +PERSON_KNEELING_MEDIUM_DARK_SKIN_TONE = "\U0001f9ce\U0001f3fe" +PERSON_KNEELING_DARK_SKIN_TONE = "\U0001f9ce\U0001f3ff" +MAN_KNEELING = "\U0001f9ce\u200d\u2642\ufe0f" +MAN_KNEELING_LIGHT_SKIN_TONE = "\U0001f9ce\U0001f3fb\u200d\u2642\ufe0f" +MAN_KNEELING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9ce\U0001f3fc\u200d\u2642\ufe0f" +MAN_KNEELING_MEDIUM_SKIN_TONE = "\U0001f9ce\U0001f3fd\u200d\u2642\ufe0f" +MAN_KNEELING_MEDIUM_DARK_SKIN_TONE = "\U0001f9ce\U0001f3fe\u200d\u2642\ufe0f" +MAN_KNEELING_DARK_SKIN_TONE = "\U0001f9ce\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_KNEELING = "\U0001f9ce\u200d\u2640\ufe0f" +WOMAN_KNEELING_LIGHT_SKIN_TONE = "\U0001f9ce\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_KNEELING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9ce\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_KNEELING_MEDIUM_SKIN_TONE = "\U0001f9ce\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_KNEELING_MEDIUM_DARK_SKIN_TONE = "\U0001f9ce\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_KNEELING_DARK_SKIN_TONE = "\U0001f9ce\U0001f3ff\u200d\u2640\ufe0f" +PERSON_WITH_WHITE_CANE = "\U0001f9d1\u200d\U0001f9af" +PERSON_WITH_WHITE_CANE_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fb\u200d\U0001f9af" +PERSON_WITH_WHITE_CANE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fc\u200d\U0001f9af" +PERSON_WITH_WHITE_CANE_MEDIUM_SKIN_TONE = "\U0001f9d1\U0001f3fd\u200d\U0001f9af" +PERSON_WITH_WHITE_CANE_MEDIUM_DARK_SKIN_TONE = "\U0001f9d1\U0001f3fe\u200d\U0001f9af" +PERSON_WITH_WHITE_CANE_DARK_SKIN_TONE = "\U0001f9d1\U0001f3ff\u200d\U0001f9af" +MAN_WITH_WHITE_CANE = "\U0001f468\u200d\U0001f9af" +MAN_WITH_WHITE_CANE_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f9af" +MAN_WITH_WHITE_CANE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f9af" +MAN_WITH_WHITE_CANE_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f9af" +MAN_WITH_WHITE_CANE_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f9af" +MAN_WITH_WHITE_CANE_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f9af" +WOMAN_WITH_WHITE_CANE = "\U0001f469\u200d\U0001f9af" +WOMAN_WITH_WHITE_CANE_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f9af" +WOMAN_WITH_WHITE_CANE_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f9af" +WOMAN_WITH_WHITE_CANE_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f9af" +WOMAN_WITH_WHITE_CANE_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f9af" +WOMAN_WITH_WHITE_CANE_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f9af" +PERSON_IN_MOTORIZED_WHEELCHAIR = "\U0001f9d1\u200d\U0001f9bc" +PERSON_IN_MOTORIZED_WHEELCHAIR_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fb\u200d\U0001f9bc" +PERSON_IN_MOTORIZED_WHEELCHAIR_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fc\u200d\U0001f9bc" +PERSON_IN_MOTORIZED_WHEELCHAIR_MEDIUM_SKIN_TONE = "\U0001f9d1\U0001f3fd\u200d\U0001f9bc" +PERSON_IN_MOTORIZED_WHEELCHAIR_MEDIUM_DARK_SKIN_TONE = "\U0001f9d1\U0001f3fe\u200d\U0001f9bc" +PERSON_IN_MOTORIZED_WHEELCHAIR_DARK_SKIN_TONE = "\U0001f9d1\U0001f3ff\u200d\U0001f9bc" +MAN_IN_MOTORIZED_WHEELCHAIR = "\U0001f468\u200d\U0001f9bc" +MAN_IN_MOTORIZED_WHEELCHAIR_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f9bc" +MAN_IN_MOTORIZED_WHEELCHAIR_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f9bc" +MAN_IN_MOTORIZED_WHEELCHAIR_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f9bc" +MAN_IN_MOTORIZED_WHEELCHAIR_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f9bc" +MAN_IN_MOTORIZED_WHEELCHAIR_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f9bc" +WOMAN_IN_MOTORIZED_WHEELCHAIR = "\U0001f469\u200d\U0001f9bc" +WOMAN_IN_MOTORIZED_WHEELCHAIR_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f9bc" +WOMAN_IN_MOTORIZED_WHEELCHAIR_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f9bc" +WOMAN_IN_MOTORIZED_WHEELCHAIR_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f9bc" +WOMAN_IN_MOTORIZED_WHEELCHAIR_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f9bc" +WOMAN_IN_MOTORIZED_WHEELCHAIR_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f9bc" +PERSON_IN_MANUAL_WHEELCHAIR = "\U0001f9d1\u200d\U0001f9bd" +PERSON_IN_MANUAL_WHEELCHAIR_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fb\u200d\U0001f9bd" +PERSON_IN_MANUAL_WHEELCHAIR_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fc\u200d\U0001f9bd" +PERSON_IN_MANUAL_WHEELCHAIR_MEDIUM_SKIN_TONE = "\U0001f9d1\U0001f3fd\u200d\U0001f9bd" +PERSON_IN_MANUAL_WHEELCHAIR_MEDIUM_DARK_SKIN_TONE = "\U0001f9d1\U0001f3fe\u200d\U0001f9bd" +PERSON_IN_MANUAL_WHEELCHAIR_DARK_SKIN_TONE = "\U0001f9d1\U0001f3ff\u200d\U0001f9bd" +MAN_IN_MANUAL_WHEELCHAIR = "\U0001f468\u200d\U0001f9bd" +MAN_IN_MANUAL_WHEELCHAIR_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f9bd" +MAN_IN_MANUAL_WHEELCHAIR_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\U0001f9bd" +MAN_IN_MANUAL_WHEELCHAIR_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f9bd" +MAN_IN_MANUAL_WHEELCHAIR_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\U0001f9bd" +MAN_IN_MANUAL_WHEELCHAIR_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f9bd" +WOMAN_IN_MANUAL_WHEELCHAIR = "\U0001f469\u200d\U0001f9bd" +WOMAN_IN_MANUAL_WHEELCHAIR_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f9bd" +WOMAN_IN_MANUAL_WHEELCHAIR_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\U0001f9bd" +WOMAN_IN_MANUAL_WHEELCHAIR_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f9bd" +WOMAN_IN_MANUAL_WHEELCHAIR_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\U0001f9bd" +WOMAN_IN_MANUAL_WHEELCHAIR_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f9bd" +PERSON_RUNNING = "\U0001f3c3" +PERSON_RUNNING_LIGHT_SKIN_TONE = "\U0001f3c3\U0001f3fb" +PERSON_RUNNING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f3c3\U0001f3fc" +PERSON_RUNNING_MEDIUM_SKIN_TONE = "\U0001f3c3\U0001f3fd" +PERSON_RUNNING_MEDIUM_DARK_SKIN_TONE = "\U0001f3c3\U0001f3fe" +PERSON_RUNNING_DARK_SKIN_TONE = "\U0001f3c3\U0001f3ff" +MAN_RUNNING = "\U0001f3c3\u200d\u2642\ufe0f" +MAN_RUNNING_LIGHT_SKIN_TONE = "\U0001f3c3\U0001f3fb\u200d\u2642\ufe0f" +MAN_RUNNING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f3c3\U0001f3fc\u200d\u2642\ufe0f" +MAN_RUNNING_MEDIUM_SKIN_TONE = "\U0001f3c3\U0001f3fd\u200d\u2642\ufe0f" +MAN_RUNNING_MEDIUM_DARK_SKIN_TONE = "\U0001f3c3\U0001f3fe\u200d\u2642\ufe0f" +MAN_RUNNING_DARK_SKIN_TONE = "\U0001f3c3\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_RUNNING = "\U0001f3c3\u200d\u2640\ufe0f" +WOMAN_RUNNING_LIGHT_SKIN_TONE = "\U0001f3c3\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_RUNNING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f3c3\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_RUNNING_MEDIUM_SKIN_TONE = "\U0001f3c3\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_RUNNING_MEDIUM_DARK_SKIN_TONE = "\U0001f3c3\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_RUNNING_DARK_SKIN_TONE = "\U0001f3c3\U0001f3ff\u200d\u2640\ufe0f" +WOMAN_DANCING = "\U0001f483" +WOMAN_DANCING_LIGHT_SKIN_TONE = "\U0001f483\U0001f3fb" +WOMAN_DANCING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f483\U0001f3fc" +WOMAN_DANCING_MEDIUM_SKIN_TONE = "\U0001f483\U0001f3fd" +WOMAN_DANCING_MEDIUM_DARK_SKIN_TONE = "\U0001f483\U0001f3fe" +WOMAN_DANCING_DARK_SKIN_TONE = "\U0001f483\U0001f3ff" +MAN_DANCING = "\U0001f57a" +MAN_DANCING_LIGHT_SKIN_TONE = "\U0001f57a\U0001f3fb" +MAN_DANCING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f57a\U0001f3fc" +MAN_DANCING_MEDIUM_SKIN_TONE = "\U0001f57a\U0001f3fd" +MAN_DANCING_MEDIUM_DARK_SKIN_TONE = "\U0001f57a\U0001f3fe" +MAN_DANCING_DARK_SKIN_TONE = "\U0001f57a\U0001f3ff" +PERSON_IN_SUIT_LEVITATING = "\U0001f574\ufe0f" +PERSON_IN_SUIT_LEVITATING_LIGHT_SKIN_TONE = "\U0001f574\U0001f3fb" +PERSON_IN_SUIT_LEVITATING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f574\U0001f3fc" +PERSON_IN_SUIT_LEVITATING_MEDIUM_SKIN_TONE = "\U0001f574\U0001f3fd" +PERSON_IN_SUIT_LEVITATING_MEDIUM_DARK_SKIN_TONE = "\U0001f574\U0001f3fe" +PERSON_IN_SUIT_LEVITATING_DARK_SKIN_TONE = "\U0001f574\U0001f3ff" +PEOPLE_WITH_BUNNY_EARS = "\U0001f46f" +MEN_WITH_BUNNY_EARS = "\U0001f46f\u200d\u2642\ufe0f" +WOMEN_WITH_BUNNY_EARS = "\U0001f46f\u200d\u2640\ufe0f" +PERSON_IN_STEAMY_ROOM = "\U0001f9d6" +PERSON_IN_STEAMY_ROOM_LIGHT_SKIN_TONE = "\U0001f9d6\U0001f3fb" +PERSON_IN_STEAMY_ROOM_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d6\U0001f3fc" +PERSON_IN_STEAMY_ROOM_MEDIUM_SKIN_TONE = "\U0001f9d6\U0001f3fd" +PERSON_IN_STEAMY_ROOM_MEDIUM_DARK_SKIN_TONE = "\U0001f9d6\U0001f3fe" +PERSON_IN_STEAMY_ROOM_DARK_SKIN_TONE = "\U0001f9d6\U0001f3ff" +MAN_IN_STEAMY_ROOM = "\U0001f9d6\u200d\u2642\ufe0f" +MAN_IN_STEAMY_ROOM_LIGHT_SKIN_TONE = "\U0001f9d6\U0001f3fb\u200d\u2642\ufe0f" +MAN_IN_STEAMY_ROOM_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d6\U0001f3fc\u200d\u2642\ufe0f" +MAN_IN_STEAMY_ROOM_MEDIUM_SKIN_TONE = "\U0001f9d6\U0001f3fd\u200d\u2642\ufe0f" +MAN_IN_STEAMY_ROOM_MEDIUM_DARK_SKIN_TONE = "\U0001f9d6\U0001f3fe\u200d\u2642\ufe0f" +MAN_IN_STEAMY_ROOM_DARK_SKIN_TONE = "\U0001f9d6\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_IN_STEAMY_ROOM = "\U0001f9d6\u200d\u2640\ufe0f" +WOMAN_IN_STEAMY_ROOM_LIGHT_SKIN_TONE = "\U0001f9d6\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_IN_STEAMY_ROOM_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d6\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_IN_STEAMY_ROOM_MEDIUM_SKIN_TONE = "\U0001f9d6\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_IN_STEAMY_ROOM_MEDIUM_DARK_SKIN_TONE = "\U0001f9d6\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_IN_STEAMY_ROOM_DARK_SKIN_TONE = "\U0001f9d6\U0001f3ff\u200d\u2640\ufe0f" +PERSON_CLIMBING = "\U0001f9d7" +PERSON_CLIMBING_LIGHT_SKIN_TONE = "\U0001f9d7\U0001f3fb" +PERSON_CLIMBING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d7\U0001f3fc" +PERSON_CLIMBING_MEDIUM_SKIN_TONE = "\U0001f9d7\U0001f3fd" +PERSON_CLIMBING_MEDIUM_DARK_SKIN_TONE = "\U0001f9d7\U0001f3fe" +PERSON_CLIMBING_DARK_SKIN_TONE = "\U0001f9d7\U0001f3ff" +MAN_CLIMBING = "\U0001f9d7\u200d\u2642\ufe0f" +MAN_CLIMBING_LIGHT_SKIN_TONE = "\U0001f9d7\U0001f3fb\u200d\u2642\ufe0f" +MAN_CLIMBING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d7\U0001f3fc\u200d\u2642\ufe0f" +MAN_CLIMBING_MEDIUM_SKIN_TONE = "\U0001f9d7\U0001f3fd\u200d\u2642\ufe0f" +MAN_CLIMBING_MEDIUM_DARK_SKIN_TONE = "\U0001f9d7\U0001f3fe\u200d\u2642\ufe0f" +MAN_CLIMBING_DARK_SKIN_TONE = "\U0001f9d7\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_CLIMBING = "\U0001f9d7\u200d\u2640\ufe0f" +WOMAN_CLIMBING_LIGHT_SKIN_TONE = "\U0001f9d7\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_CLIMBING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d7\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_CLIMBING_MEDIUM_SKIN_TONE = "\U0001f9d7\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_CLIMBING_MEDIUM_DARK_SKIN_TONE = "\U0001f9d7\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_CLIMBING_DARK_SKIN_TONE = "\U0001f9d7\U0001f3ff\u200d\u2640\ufe0f" +PERSON_FENCING = "\U0001f93a" +HORSE_RACING = "\U0001f3c7" +HORSE_RACING_LIGHT_SKIN_TONE = "\U0001f3c7\U0001f3fb" +HORSE_RACING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f3c7\U0001f3fc" +HORSE_RACING_MEDIUM_SKIN_TONE = "\U0001f3c7\U0001f3fd" +HORSE_RACING_MEDIUM_DARK_SKIN_TONE = "\U0001f3c7\U0001f3fe" +HORSE_RACING_DARK_SKIN_TONE = "\U0001f3c7\U0001f3ff" +SKIER = "\u26f7\ufe0f" +SNOWBOARDER = "\U0001f3c2" +SNOWBOARDER_LIGHT_SKIN_TONE = "\U0001f3c2\U0001f3fb" +SNOWBOARDER_MEDIUM_LIGHT_SKIN_TONE = "\U0001f3c2\U0001f3fc" +SNOWBOARDER_MEDIUM_SKIN_TONE = "\U0001f3c2\U0001f3fd" +SNOWBOARDER_MEDIUM_DARK_SKIN_TONE = "\U0001f3c2\U0001f3fe" +SNOWBOARDER_DARK_SKIN_TONE = "\U0001f3c2\U0001f3ff" +PERSON_GOLFING = "\U0001f3cc\ufe0f" +PERSON_GOLFING_LIGHT_SKIN_TONE = "\U0001f3cc\U0001f3fb" +PERSON_GOLFING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f3cc\U0001f3fc" +PERSON_GOLFING_MEDIUM_SKIN_TONE = "\U0001f3cc\U0001f3fd" +PERSON_GOLFING_MEDIUM_DARK_SKIN_TONE = "\U0001f3cc\U0001f3fe" +PERSON_GOLFING_DARK_SKIN_TONE = "\U0001f3cc\U0001f3ff" +MAN_GOLFING = "\U0001f3cc\ufe0f\u200d\u2642\ufe0f" +MAN_GOLFING_LIGHT_SKIN_TONE = "\U0001f3cc\U0001f3fb\u200d\u2642\ufe0f" +MAN_GOLFING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f3cc\U0001f3fc\u200d\u2642\ufe0f" +MAN_GOLFING_MEDIUM_SKIN_TONE = "\U0001f3cc\U0001f3fd\u200d\u2642\ufe0f" +MAN_GOLFING_MEDIUM_DARK_SKIN_TONE = "\U0001f3cc\U0001f3fe\u200d\u2642\ufe0f" +MAN_GOLFING_DARK_SKIN_TONE = "\U0001f3cc\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_GOLFING = "\U0001f3cc\ufe0f\u200d\u2640\ufe0f" +WOMAN_GOLFING_LIGHT_SKIN_TONE = "\U0001f3cc\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_GOLFING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f3cc\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_GOLFING_MEDIUM_SKIN_TONE = "\U0001f3cc\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_GOLFING_MEDIUM_DARK_SKIN_TONE = "\U0001f3cc\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_GOLFING_DARK_SKIN_TONE = "\U0001f3cc\U0001f3ff\u200d\u2640\ufe0f" +PERSON_SURFING = "\U0001f3c4" +PERSON_SURFING_LIGHT_SKIN_TONE = "\U0001f3c4\U0001f3fb" +PERSON_SURFING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f3c4\U0001f3fc" +PERSON_SURFING_MEDIUM_SKIN_TONE = "\U0001f3c4\U0001f3fd" +PERSON_SURFING_MEDIUM_DARK_SKIN_TONE = "\U0001f3c4\U0001f3fe" +PERSON_SURFING_DARK_SKIN_TONE = "\U0001f3c4\U0001f3ff" +MAN_SURFING = "\U0001f3c4\u200d\u2642\ufe0f" +MAN_SURFING_LIGHT_SKIN_TONE = "\U0001f3c4\U0001f3fb\u200d\u2642\ufe0f" +MAN_SURFING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f3c4\U0001f3fc\u200d\u2642\ufe0f" +MAN_SURFING_MEDIUM_SKIN_TONE = "\U0001f3c4\U0001f3fd\u200d\u2642\ufe0f" +MAN_SURFING_MEDIUM_DARK_SKIN_TONE = "\U0001f3c4\U0001f3fe\u200d\u2642\ufe0f" +MAN_SURFING_DARK_SKIN_TONE = "\U0001f3c4\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_SURFING = "\U0001f3c4\u200d\u2640\ufe0f" +WOMAN_SURFING_LIGHT_SKIN_TONE = "\U0001f3c4\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_SURFING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f3c4\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_SURFING_MEDIUM_SKIN_TONE = "\U0001f3c4\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_SURFING_MEDIUM_DARK_SKIN_TONE = "\U0001f3c4\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_SURFING_DARK_SKIN_TONE = "\U0001f3c4\U0001f3ff\u200d\u2640\ufe0f" +PERSON_ROWING_BOAT = "\U0001f6a3" +PERSON_ROWING_BOAT_LIGHT_SKIN_TONE = "\U0001f6a3\U0001f3fb" +PERSON_ROWING_BOAT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f6a3\U0001f3fc" +PERSON_ROWING_BOAT_MEDIUM_SKIN_TONE = "\U0001f6a3\U0001f3fd" +PERSON_ROWING_BOAT_MEDIUM_DARK_SKIN_TONE = "\U0001f6a3\U0001f3fe" +PERSON_ROWING_BOAT_DARK_SKIN_TONE = "\U0001f6a3\U0001f3ff" +MAN_ROWING_BOAT = "\U0001f6a3\u200d\u2642\ufe0f" +MAN_ROWING_BOAT_LIGHT_SKIN_TONE = "\U0001f6a3\U0001f3fb\u200d\u2642\ufe0f" +MAN_ROWING_BOAT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f6a3\U0001f3fc\u200d\u2642\ufe0f" +MAN_ROWING_BOAT_MEDIUM_SKIN_TONE = "\U0001f6a3\U0001f3fd\u200d\u2642\ufe0f" +MAN_ROWING_BOAT_MEDIUM_DARK_SKIN_TONE = "\U0001f6a3\U0001f3fe\u200d\u2642\ufe0f" +MAN_ROWING_BOAT_DARK_SKIN_TONE = "\U0001f6a3\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_ROWING_BOAT = "\U0001f6a3\u200d\u2640\ufe0f" +WOMAN_ROWING_BOAT_LIGHT_SKIN_TONE = "\U0001f6a3\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_ROWING_BOAT_MEDIUM_LIGHT_SKIN_TONE = "\U0001f6a3\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_ROWING_BOAT_MEDIUM_SKIN_TONE = "\U0001f6a3\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_ROWING_BOAT_MEDIUM_DARK_SKIN_TONE = "\U0001f6a3\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_ROWING_BOAT_DARK_SKIN_TONE = "\U0001f6a3\U0001f3ff\u200d\u2640\ufe0f" +PERSON_SWIMMING = "\U0001f3ca" +PERSON_SWIMMING_LIGHT_SKIN_TONE = "\U0001f3ca\U0001f3fb" +PERSON_SWIMMING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f3ca\U0001f3fc" +PERSON_SWIMMING_MEDIUM_SKIN_TONE = "\U0001f3ca\U0001f3fd" +PERSON_SWIMMING_MEDIUM_DARK_SKIN_TONE = "\U0001f3ca\U0001f3fe" +PERSON_SWIMMING_DARK_SKIN_TONE = "\U0001f3ca\U0001f3ff" +MAN_SWIMMING = "\U0001f3ca\u200d\u2642\ufe0f" +MAN_SWIMMING_LIGHT_SKIN_TONE = "\U0001f3ca\U0001f3fb\u200d\u2642\ufe0f" +MAN_SWIMMING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f3ca\U0001f3fc\u200d\u2642\ufe0f" +MAN_SWIMMING_MEDIUM_SKIN_TONE = "\U0001f3ca\U0001f3fd\u200d\u2642\ufe0f" +MAN_SWIMMING_MEDIUM_DARK_SKIN_TONE = "\U0001f3ca\U0001f3fe\u200d\u2642\ufe0f" +MAN_SWIMMING_DARK_SKIN_TONE = "\U0001f3ca\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_SWIMMING = "\U0001f3ca\u200d\u2640\ufe0f" +WOMAN_SWIMMING_LIGHT_SKIN_TONE = "\U0001f3ca\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_SWIMMING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f3ca\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_SWIMMING_MEDIUM_SKIN_TONE = "\U0001f3ca\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_SWIMMING_MEDIUM_DARK_SKIN_TONE = "\U0001f3ca\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_SWIMMING_DARK_SKIN_TONE = "\U0001f3ca\U0001f3ff\u200d\u2640\ufe0f" +PERSON_BOUNCING_BALL = "\u26f9\ufe0f" +PERSON_BOUNCING_BALL_LIGHT_SKIN_TONE = "\u26f9\U0001f3fb" +PERSON_BOUNCING_BALL_MEDIUM_LIGHT_SKIN_TONE = "\u26f9\U0001f3fc" +PERSON_BOUNCING_BALL_MEDIUM_SKIN_TONE = "\u26f9\U0001f3fd" +PERSON_BOUNCING_BALL_MEDIUM_DARK_SKIN_TONE = "\u26f9\U0001f3fe" +PERSON_BOUNCING_BALL_DARK_SKIN_TONE = "\u26f9\U0001f3ff" +MAN_BOUNCING_BALL = "\u26f9\ufe0f\u200d\u2642\ufe0f" +MAN_BOUNCING_BALL_LIGHT_SKIN_TONE = "\u26f9\U0001f3fb\u200d\u2642\ufe0f" +MAN_BOUNCING_BALL_MEDIUM_LIGHT_SKIN_TONE = "\u26f9\U0001f3fc\u200d\u2642\ufe0f" +MAN_BOUNCING_BALL_MEDIUM_SKIN_TONE = "\u26f9\U0001f3fd\u200d\u2642\ufe0f" +MAN_BOUNCING_BALL_MEDIUM_DARK_SKIN_TONE = "\u26f9\U0001f3fe\u200d\u2642\ufe0f" +MAN_BOUNCING_BALL_DARK_SKIN_TONE = "\u26f9\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_BOUNCING_BALL = "\u26f9\ufe0f\u200d\u2640\ufe0f" +WOMAN_BOUNCING_BALL_LIGHT_SKIN_TONE = "\u26f9\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_BOUNCING_BALL_MEDIUM_LIGHT_SKIN_TONE = "\u26f9\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_BOUNCING_BALL_MEDIUM_SKIN_TONE = "\u26f9\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_BOUNCING_BALL_MEDIUM_DARK_SKIN_TONE = "\u26f9\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_BOUNCING_BALL_DARK_SKIN_TONE = "\u26f9\U0001f3ff\u200d\u2640\ufe0f" +PERSON_LIFTING_WEIGHTS = "\U0001f3cb\ufe0f" +PERSON_LIFTING_WEIGHTS_LIGHT_SKIN_TONE = "\U0001f3cb\U0001f3fb" +PERSON_LIFTING_WEIGHTS_MEDIUM_LIGHT_SKIN_TONE = "\U0001f3cb\U0001f3fc" +PERSON_LIFTING_WEIGHTS_MEDIUM_SKIN_TONE = "\U0001f3cb\U0001f3fd" +PERSON_LIFTING_WEIGHTS_MEDIUM_DARK_SKIN_TONE = "\U0001f3cb\U0001f3fe" +PERSON_LIFTING_WEIGHTS_DARK_SKIN_TONE = "\U0001f3cb\U0001f3ff" +MAN_LIFTING_WEIGHTS = "\U0001f3cb\ufe0f\u200d\u2642\ufe0f" +MAN_LIFTING_WEIGHTS_LIGHT_SKIN_TONE = "\U0001f3cb\U0001f3fb\u200d\u2642\ufe0f" +MAN_LIFTING_WEIGHTS_MEDIUM_LIGHT_SKIN_TONE = "\U0001f3cb\U0001f3fc\u200d\u2642\ufe0f" +MAN_LIFTING_WEIGHTS_MEDIUM_SKIN_TONE = "\U0001f3cb\U0001f3fd\u200d\u2642\ufe0f" +MAN_LIFTING_WEIGHTS_MEDIUM_DARK_SKIN_TONE = "\U0001f3cb\U0001f3fe\u200d\u2642\ufe0f" +MAN_LIFTING_WEIGHTS_DARK_SKIN_TONE = "\U0001f3cb\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_LIFTING_WEIGHTS = "\U0001f3cb\ufe0f\u200d\u2640\ufe0f" +WOMAN_LIFTING_WEIGHTS_LIGHT_SKIN_TONE = "\U0001f3cb\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_LIFTING_WEIGHTS_MEDIUM_LIGHT_SKIN_TONE = "\U0001f3cb\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_LIFTING_WEIGHTS_MEDIUM_SKIN_TONE = "\U0001f3cb\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_LIFTING_WEIGHTS_MEDIUM_DARK_SKIN_TONE = "\U0001f3cb\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_LIFTING_WEIGHTS_DARK_SKIN_TONE = "\U0001f3cb\U0001f3ff\u200d\u2640\ufe0f" +PERSON_BIKING = "\U0001f6b4" +PERSON_BIKING_LIGHT_SKIN_TONE = "\U0001f6b4\U0001f3fb" +PERSON_BIKING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f6b4\U0001f3fc" +PERSON_BIKING_MEDIUM_SKIN_TONE = "\U0001f6b4\U0001f3fd" +PERSON_BIKING_MEDIUM_DARK_SKIN_TONE = "\U0001f6b4\U0001f3fe" +PERSON_BIKING_DARK_SKIN_TONE = "\U0001f6b4\U0001f3ff" +MAN_BIKING = "\U0001f6b4\u200d\u2642\ufe0f" +MAN_BIKING_LIGHT_SKIN_TONE = "\U0001f6b4\U0001f3fb\u200d\u2642\ufe0f" +MAN_BIKING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f6b4\U0001f3fc\u200d\u2642\ufe0f" +MAN_BIKING_MEDIUM_SKIN_TONE = "\U0001f6b4\U0001f3fd\u200d\u2642\ufe0f" +MAN_BIKING_MEDIUM_DARK_SKIN_TONE = "\U0001f6b4\U0001f3fe\u200d\u2642\ufe0f" +MAN_BIKING_DARK_SKIN_TONE = "\U0001f6b4\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_BIKING = "\U0001f6b4\u200d\u2640\ufe0f" +WOMAN_BIKING_LIGHT_SKIN_TONE = "\U0001f6b4\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_BIKING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f6b4\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_BIKING_MEDIUM_SKIN_TONE = "\U0001f6b4\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_BIKING_MEDIUM_DARK_SKIN_TONE = "\U0001f6b4\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_BIKING_DARK_SKIN_TONE = "\U0001f6b4\U0001f3ff\u200d\u2640\ufe0f" +PERSON_MOUNTAIN_BIKING = "\U0001f6b5" +PERSON_MOUNTAIN_BIKING_LIGHT_SKIN_TONE = "\U0001f6b5\U0001f3fb" +PERSON_MOUNTAIN_BIKING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f6b5\U0001f3fc" +PERSON_MOUNTAIN_BIKING_MEDIUM_SKIN_TONE = "\U0001f6b5\U0001f3fd" +PERSON_MOUNTAIN_BIKING_MEDIUM_DARK_SKIN_TONE = "\U0001f6b5\U0001f3fe" +PERSON_MOUNTAIN_BIKING_DARK_SKIN_TONE = "\U0001f6b5\U0001f3ff" +MAN_MOUNTAIN_BIKING = "\U0001f6b5\u200d\u2642\ufe0f" +MAN_MOUNTAIN_BIKING_LIGHT_SKIN_TONE = "\U0001f6b5\U0001f3fb\u200d\u2642\ufe0f" +MAN_MOUNTAIN_BIKING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f6b5\U0001f3fc\u200d\u2642\ufe0f" +MAN_MOUNTAIN_BIKING_MEDIUM_SKIN_TONE = "\U0001f6b5\U0001f3fd\u200d\u2642\ufe0f" +MAN_MOUNTAIN_BIKING_MEDIUM_DARK_SKIN_TONE = "\U0001f6b5\U0001f3fe\u200d\u2642\ufe0f" +MAN_MOUNTAIN_BIKING_DARK_SKIN_TONE = "\U0001f6b5\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_MOUNTAIN_BIKING = "\U0001f6b5\u200d\u2640\ufe0f" +WOMAN_MOUNTAIN_BIKING_LIGHT_SKIN_TONE = "\U0001f6b5\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_MOUNTAIN_BIKING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f6b5\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_MOUNTAIN_BIKING_MEDIUM_SKIN_TONE = "\U0001f6b5\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_MOUNTAIN_BIKING_MEDIUM_DARK_SKIN_TONE = "\U0001f6b5\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_MOUNTAIN_BIKING_DARK_SKIN_TONE = "\U0001f6b5\U0001f3ff\u200d\u2640\ufe0f" +PERSON_CARTWHEELING = "\U0001f938" +PERSON_CARTWHEELING_LIGHT_SKIN_TONE = "\U0001f938\U0001f3fb" +PERSON_CARTWHEELING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f938\U0001f3fc" +PERSON_CARTWHEELING_MEDIUM_SKIN_TONE = "\U0001f938\U0001f3fd" +PERSON_CARTWHEELING_MEDIUM_DARK_SKIN_TONE = "\U0001f938\U0001f3fe" +PERSON_CARTWHEELING_DARK_SKIN_TONE = "\U0001f938\U0001f3ff" +MAN_CARTWHEELING = "\U0001f938\u200d\u2642\ufe0f" +MAN_CARTWHEELING_LIGHT_SKIN_TONE = "\U0001f938\U0001f3fb\u200d\u2642\ufe0f" +MAN_CARTWHEELING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f938\U0001f3fc\u200d\u2642\ufe0f" +MAN_CARTWHEELING_MEDIUM_SKIN_TONE = "\U0001f938\U0001f3fd\u200d\u2642\ufe0f" +MAN_CARTWHEELING_MEDIUM_DARK_SKIN_TONE = "\U0001f938\U0001f3fe\u200d\u2642\ufe0f" +MAN_CARTWHEELING_DARK_SKIN_TONE = "\U0001f938\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_CARTWHEELING = "\U0001f938\u200d\u2640\ufe0f" +WOMAN_CARTWHEELING_LIGHT_SKIN_TONE = "\U0001f938\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_CARTWHEELING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f938\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_CARTWHEELING_MEDIUM_SKIN_TONE = "\U0001f938\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_CARTWHEELING_MEDIUM_DARK_SKIN_TONE = "\U0001f938\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_CARTWHEELING_DARK_SKIN_TONE = "\U0001f938\U0001f3ff\u200d\u2640\ufe0f" +PEOPLE_WRESTLING = "\U0001f93c" +MEN_WRESTLING = "\U0001f93c\u200d\u2642\ufe0f" +WOMEN_WRESTLING = "\U0001f93c\u200d\u2640\ufe0f" +PERSON_PLAYING_WATER_POLO = "\U0001f93d" +PERSON_PLAYING_WATER_POLO_LIGHT_SKIN_TONE = "\U0001f93d\U0001f3fb" +PERSON_PLAYING_WATER_POLO_MEDIUM_LIGHT_SKIN_TONE = "\U0001f93d\U0001f3fc" +PERSON_PLAYING_WATER_POLO_MEDIUM_SKIN_TONE = "\U0001f93d\U0001f3fd" +PERSON_PLAYING_WATER_POLO_MEDIUM_DARK_SKIN_TONE = "\U0001f93d\U0001f3fe" +PERSON_PLAYING_WATER_POLO_DARK_SKIN_TONE = "\U0001f93d\U0001f3ff" +MAN_PLAYING_WATER_POLO = "\U0001f93d\u200d\u2642\ufe0f" +MAN_PLAYING_WATER_POLO_LIGHT_SKIN_TONE = "\U0001f93d\U0001f3fb\u200d\u2642\ufe0f" +MAN_PLAYING_WATER_POLO_MEDIUM_LIGHT_SKIN_TONE = "\U0001f93d\U0001f3fc\u200d\u2642\ufe0f" +MAN_PLAYING_WATER_POLO_MEDIUM_SKIN_TONE = "\U0001f93d\U0001f3fd\u200d\u2642\ufe0f" +MAN_PLAYING_WATER_POLO_MEDIUM_DARK_SKIN_TONE = "\U0001f93d\U0001f3fe\u200d\u2642\ufe0f" +MAN_PLAYING_WATER_POLO_DARK_SKIN_TONE = "\U0001f93d\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_PLAYING_WATER_POLO = "\U0001f93d\u200d\u2640\ufe0f" +WOMAN_PLAYING_WATER_POLO_LIGHT_SKIN_TONE = "\U0001f93d\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_PLAYING_WATER_POLO_MEDIUM_LIGHT_SKIN_TONE = "\U0001f93d\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_PLAYING_WATER_POLO_MEDIUM_SKIN_TONE = "\U0001f93d\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_PLAYING_WATER_POLO_MEDIUM_DARK_SKIN_TONE = "\U0001f93d\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_PLAYING_WATER_POLO_DARK_SKIN_TONE = "\U0001f93d\U0001f3ff\u200d\u2640\ufe0f" +PERSON_PLAYING_HANDBALL = "\U0001f93e" +PERSON_PLAYING_HANDBALL_LIGHT_SKIN_TONE = "\U0001f93e\U0001f3fb" +PERSON_PLAYING_HANDBALL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f93e\U0001f3fc" +PERSON_PLAYING_HANDBALL_MEDIUM_SKIN_TONE = "\U0001f93e\U0001f3fd" +PERSON_PLAYING_HANDBALL_MEDIUM_DARK_SKIN_TONE = "\U0001f93e\U0001f3fe" +PERSON_PLAYING_HANDBALL_DARK_SKIN_TONE = "\U0001f93e\U0001f3ff" +MAN_PLAYING_HANDBALL = "\U0001f93e\u200d\u2642\ufe0f" +MAN_PLAYING_HANDBALL_LIGHT_SKIN_TONE = "\U0001f93e\U0001f3fb\u200d\u2642\ufe0f" +MAN_PLAYING_HANDBALL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f93e\U0001f3fc\u200d\u2642\ufe0f" +MAN_PLAYING_HANDBALL_MEDIUM_SKIN_TONE = "\U0001f93e\U0001f3fd\u200d\u2642\ufe0f" +MAN_PLAYING_HANDBALL_MEDIUM_DARK_SKIN_TONE = "\U0001f93e\U0001f3fe\u200d\u2642\ufe0f" +MAN_PLAYING_HANDBALL_DARK_SKIN_TONE = "\U0001f93e\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_PLAYING_HANDBALL = "\U0001f93e\u200d\u2640\ufe0f" +WOMAN_PLAYING_HANDBALL_LIGHT_SKIN_TONE = "\U0001f93e\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_PLAYING_HANDBALL_MEDIUM_LIGHT_SKIN_TONE = "\U0001f93e\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_PLAYING_HANDBALL_MEDIUM_SKIN_TONE = "\U0001f93e\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_PLAYING_HANDBALL_MEDIUM_DARK_SKIN_TONE = "\U0001f93e\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_PLAYING_HANDBALL_DARK_SKIN_TONE = "\U0001f93e\U0001f3ff\u200d\u2640\ufe0f" +PERSON_JUGGLING = "\U0001f939" +PERSON_JUGGLING_LIGHT_SKIN_TONE = "\U0001f939\U0001f3fb" +PERSON_JUGGLING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f939\U0001f3fc" +PERSON_JUGGLING_MEDIUM_SKIN_TONE = "\U0001f939\U0001f3fd" +PERSON_JUGGLING_MEDIUM_DARK_SKIN_TONE = "\U0001f939\U0001f3fe" +PERSON_JUGGLING_DARK_SKIN_TONE = "\U0001f939\U0001f3ff" +MAN_JUGGLING = "\U0001f939\u200d\u2642\ufe0f" +MAN_JUGGLING_LIGHT_SKIN_TONE = "\U0001f939\U0001f3fb\u200d\u2642\ufe0f" +MAN_JUGGLING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f939\U0001f3fc\u200d\u2642\ufe0f" +MAN_JUGGLING_MEDIUM_SKIN_TONE = "\U0001f939\U0001f3fd\u200d\u2642\ufe0f" +MAN_JUGGLING_MEDIUM_DARK_SKIN_TONE = "\U0001f939\U0001f3fe\u200d\u2642\ufe0f" +MAN_JUGGLING_DARK_SKIN_TONE = "\U0001f939\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_JUGGLING = "\U0001f939\u200d\u2640\ufe0f" +WOMAN_JUGGLING_LIGHT_SKIN_TONE = "\U0001f939\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_JUGGLING_MEDIUM_LIGHT_SKIN_TONE = "\U0001f939\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_JUGGLING_MEDIUM_SKIN_TONE = "\U0001f939\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_JUGGLING_MEDIUM_DARK_SKIN_TONE = "\U0001f939\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_JUGGLING_DARK_SKIN_TONE = "\U0001f939\U0001f3ff\u200d\u2640\ufe0f" +PERSON_IN_LOTUS_POSITION = "\U0001f9d8" +PERSON_IN_LOTUS_POSITION_LIGHT_SKIN_TONE = "\U0001f9d8\U0001f3fb" +PERSON_IN_LOTUS_POSITION_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d8\U0001f3fc" +PERSON_IN_LOTUS_POSITION_MEDIUM_SKIN_TONE = "\U0001f9d8\U0001f3fd" +PERSON_IN_LOTUS_POSITION_MEDIUM_DARK_SKIN_TONE = "\U0001f9d8\U0001f3fe" +PERSON_IN_LOTUS_POSITION_DARK_SKIN_TONE = "\U0001f9d8\U0001f3ff" +MAN_IN_LOTUS_POSITION = "\U0001f9d8\u200d\u2642\ufe0f" +MAN_IN_LOTUS_POSITION_LIGHT_SKIN_TONE = "\U0001f9d8\U0001f3fb\u200d\u2642\ufe0f" +MAN_IN_LOTUS_POSITION_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d8\U0001f3fc\u200d\u2642\ufe0f" +MAN_IN_LOTUS_POSITION_MEDIUM_SKIN_TONE = "\U0001f9d8\U0001f3fd\u200d\u2642\ufe0f" +MAN_IN_LOTUS_POSITION_MEDIUM_DARK_SKIN_TONE = "\U0001f9d8\U0001f3fe\u200d\u2642\ufe0f" +MAN_IN_LOTUS_POSITION_DARK_SKIN_TONE = "\U0001f9d8\U0001f3ff\u200d\u2642\ufe0f" +WOMAN_IN_LOTUS_POSITION = "\U0001f9d8\u200d\u2640\ufe0f" +WOMAN_IN_LOTUS_POSITION_LIGHT_SKIN_TONE = "\U0001f9d8\U0001f3fb\u200d\u2640\ufe0f" +WOMAN_IN_LOTUS_POSITION_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d8\U0001f3fc\u200d\u2640\ufe0f" +WOMAN_IN_LOTUS_POSITION_MEDIUM_SKIN_TONE = "\U0001f9d8\U0001f3fd\u200d\u2640\ufe0f" +WOMAN_IN_LOTUS_POSITION_MEDIUM_DARK_SKIN_TONE = "\U0001f9d8\U0001f3fe\u200d\u2640\ufe0f" +WOMAN_IN_LOTUS_POSITION_DARK_SKIN_TONE = "\U0001f9d8\U0001f3ff\u200d\u2640\ufe0f" +PERSON_TAKING_BATH = "\U0001f6c0" +PERSON_TAKING_BATH_LIGHT_SKIN_TONE = "\U0001f6c0\U0001f3fb" +PERSON_TAKING_BATH_MEDIUM_LIGHT_SKIN_TONE = "\U0001f6c0\U0001f3fc" +PERSON_TAKING_BATH_MEDIUM_SKIN_TONE = "\U0001f6c0\U0001f3fd" +PERSON_TAKING_BATH_MEDIUM_DARK_SKIN_TONE = "\U0001f6c0\U0001f3fe" +PERSON_TAKING_BATH_DARK_SKIN_TONE = "\U0001f6c0\U0001f3ff" +PERSON_IN_BED = "\U0001f6cc" +PERSON_IN_BED_LIGHT_SKIN_TONE = "\U0001f6cc\U0001f3fb" +PERSON_IN_BED_MEDIUM_LIGHT_SKIN_TONE = "\U0001f6cc\U0001f3fc" +PERSON_IN_BED_MEDIUM_SKIN_TONE = "\U0001f6cc\U0001f3fd" +PERSON_IN_BED_MEDIUM_DARK_SKIN_TONE = "\U0001f6cc\U0001f3fe" +PERSON_IN_BED_DARK_SKIN_TONE = "\U0001f6cc\U0001f3ff" +PEOPLE_HOLDING_HANDS = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1" +PEOPLE_HOLDING_HANDS_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fb\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fb" +PEOPLE_HOLDING_HANDS_LIGHT_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f9d1\U0001f3fb\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fc" +PEOPLE_HOLDING_HANDS_LIGHT_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f9d1\U0001f3fb\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fd" +PEOPLE_HOLDING_HANDS_LIGHT_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f9d1\U0001f3fb\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fe" +PEOPLE_HOLDING_HANDS_LIGHT_SKIN_TONE_DARK_SKIN_TONE = "\U0001f9d1\U0001f3fb\u200d\U0001f91d\u200d\U0001f9d1\U0001f3ff" +PEOPLE_HOLDING_HANDS_MEDIUM_LIGHT_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f9d1\U0001f3fc\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fb" +PEOPLE_HOLDING_HANDS_MEDIUM_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3fc\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fc" +PEOPLE_HOLDING_HANDS_MEDIUM_LIGHT_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f9d1\U0001f3fc\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fd" +PEOPLE_HOLDING_HANDS_MEDIUM_LIGHT_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f9d1\U0001f3fc\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fe" +PEOPLE_HOLDING_HANDS_MEDIUM_LIGHT_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f9d1\U0001f3fc\u200d\U0001f91d\u200d\U0001f9d1\U0001f3ff" +PEOPLE_HOLDING_HANDS_MEDIUM_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f9d1\U0001f3fd\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fb" +PEOPLE_HOLDING_HANDS_MEDIUM_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f9d1\U0001f3fd\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fc" +PEOPLE_HOLDING_HANDS_MEDIUM_SKIN_TONE = "\U0001f9d1\U0001f3fd\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fd" +PEOPLE_HOLDING_HANDS_MEDIUM_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f9d1\U0001f3fd\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fe" +PEOPLE_HOLDING_HANDS_MEDIUM_SKIN_TONE_DARK_SKIN_TONE = "\U0001f9d1\U0001f3fd\u200d\U0001f91d\u200d\U0001f9d1\U0001f3ff" +PEOPLE_HOLDING_HANDS_MEDIUM_DARK_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f9d1\U0001f3fe\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fb" +PEOPLE_HOLDING_HANDS_MEDIUM_DARK_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f9d1\U0001f3fe\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fc" +PEOPLE_HOLDING_HANDS_MEDIUM_DARK_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f9d1\U0001f3fe\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fd" +PEOPLE_HOLDING_HANDS_MEDIUM_DARK_SKIN_TONE = "\U0001f9d1\U0001f3fe\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fe" +PEOPLE_HOLDING_HANDS_MEDIUM_DARK_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f9d1\U0001f3fe\u200d\U0001f91d\u200d\U0001f9d1\U0001f3ff" +PEOPLE_HOLDING_HANDS_DARK_SKIN_TONE_LIGHT_SKIN_TONE = "\U0001f9d1\U0001f3ff\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fb" +PEOPLE_HOLDING_HANDS_DARK_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f9d1\U0001f3ff\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fc" +PEOPLE_HOLDING_HANDS_DARK_SKIN_TONE_MEDIUM_SKIN_TONE = "\U0001f9d1\U0001f3ff\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fd" +PEOPLE_HOLDING_HANDS_DARK_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f9d1\U0001f3ff\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fe" +PEOPLE_HOLDING_HANDS_DARK_SKIN_TONE = "\U0001f9d1\U0001f3ff\u200d\U0001f91d\u200d\U0001f9d1\U0001f3ff" +WOMEN_HOLDING_HANDS = "\U0001f46d" +WOMEN_HOLDING_HANDS_LIGHT_SKIN_TONE = "\U0001f46d\U0001f3fb" +WOMEN_HOLDING_HANDS_LIGHT_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fb\u200d\U0001f91d\u200d\U0001f469\U0001f3fc" +WOMEN_HOLDING_HANDS_LIGHT_SKIN_TONE_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f91d\u200d\U0001f469\U0001f3fd" +WOMEN_HOLDING_HANDS_LIGHT_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fb\u200d\U0001f91d\u200d\U0001f469\U0001f3fe" +WOMEN_HOLDING_HANDS_LIGHT_SKIN_TONE_DARK_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\U0001f91d\u200d\U0001f469\U0001f3ff" +WOMEN_HOLDING_HANDS_MEDIUM_LIGHT_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fc\u200d\U0001f91d\u200d\U0001f469\U0001f3fb" +WOMEN_HOLDING_HANDS_MEDIUM_LIGHT_SKIN_TONE = "\U0001f46d\U0001f3fc" +WOMEN_HOLDING_HANDS_MEDIUM_LIGHT_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f469\U0001f3fc\u200d\U0001f91d\u200d\U0001f469\U0001f3fd" +WOMEN_HOLDING_HANDS_MEDIUM_LIGHT_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fc\u200d\U0001f91d\u200d\U0001f469\U0001f3fe" +WOMEN_HOLDING_HANDS_MEDIUM_LIGHT_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fc\u200d\U0001f91d\u200d\U0001f469\U0001f3ff" +WOMEN_HOLDING_HANDS_MEDIUM_SKIN_TONE_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f91d\u200d\U0001f469\U0001f3fb" +WOMEN_HOLDING_HANDS_MEDIUM_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fd\u200d\U0001f91d\u200d\U0001f469\U0001f3fc" +WOMEN_HOLDING_HANDS_MEDIUM_SKIN_TONE = "\U0001f46d\U0001f3fd" +WOMEN_HOLDING_HANDS_MEDIUM_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fd\u200d\U0001f91d\u200d\U0001f469\U0001f3fe" +WOMEN_HOLDING_HANDS_MEDIUM_SKIN_TONE_DARK_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\U0001f91d\u200d\U0001f469\U0001f3ff" +WOMEN_HOLDING_HANDS_MEDIUM_DARK_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fe\u200d\U0001f91d\u200d\U0001f469\U0001f3fb" +WOMEN_HOLDING_HANDS_MEDIUM_DARK_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fe\u200d\U0001f91d\u200d\U0001f469\U0001f3fc" +WOMEN_HOLDING_HANDS_MEDIUM_DARK_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f469\U0001f3fe\u200d\U0001f91d\u200d\U0001f469\U0001f3fd" +WOMEN_HOLDING_HANDS_MEDIUM_DARK_SKIN_TONE = "\U0001f46d\U0001f3fe" +WOMEN_HOLDING_HANDS_MEDIUM_DARK_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fe\u200d\U0001f91d\u200d\U0001f469\U0001f3ff" +WOMEN_HOLDING_HANDS_DARK_SKIN_TONE_LIGHT_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f91d\u200d\U0001f469\U0001f3fb" +WOMEN_HOLDING_HANDS_DARK_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3ff\u200d\U0001f91d\u200d\U0001f469\U0001f3fc" +WOMEN_HOLDING_HANDS_DARK_SKIN_TONE_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\U0001f91d\u200d\U0001f469\U0001f3fd" +WOMEN_HOLDING_HANDS_DARK_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3ff\u200d\U0001f91d\u200d\U0001f469\U0001f3fe" +WOMEN_HOLDING_HANDS_DARK_SKIN_TONE = "\U0001f46d\U0001f3ff" +WOMAN_AND_MAN_HOLDING_HANDS = "\U0001f46b" +WOMAN_AND_MAN_HOLDING_HANDS_LIGHT_SKIN_TONE = "\U0001f46b\U0001f3fb" +WOMAN_AND_MAN_HOLDING_HANDS_LIGHT_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fb\u200d\U0001f91d\u200d\U0001f468\U0001f3fc" +WOMAN_AND_MAN_HOLDING_HANDS_LIGHT_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f469\U0001f3fb\u200d\U0001f91d\u200d\U0001f468\U0001f3fd" +WOMAN_AND_MAN_HOLDING_HANDS_LIGHT_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fb\u200d\U0001f91d\u200d\U0001f468\U0001f3fe" +WOMAN_AND_MAN_HOLDING_HANDS_LIGHT_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fb\u200d\U0001f91d\u200d\U0001f468\U0001f3ff" +WOMAN_AND_MAN_HOLDING_HANDS_MEDIUM_LIGHT_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fc\u200d\U0001f91d\u200d\U0001f468\U0001f3fb" +WOMAN_AND_MAN_HOLDING_HANDS_MEDIUM_LIGHT_SKIN_TONE = "\U0001f46b\U0001f3fc" +WOMAN_AND_MAN_HOLDING_HANDS_MEDIUM_LIGHT_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f469\U0001f3fc\u200d\U0001f91d\u200d\U0001f468\U0001f3fd" +WOMAN_AND_MAN_HOLDING_HANDS_MEDIUM_LIGHT_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fc\u200d\U0001f91d\u200d\U0001f468\U0001f3fe" +WOMAN_AND_MAN_HOLDING_HANDS_MEDIUM_LIGHT_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fc\u200d\U0001f91d\u200d\U0001f468\U0001f3ff" +WOMAN_AND_MAN_HOLDING_HANDS_MEDIUM_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fd\u200d\U0001f91d\u200d\U0001f468\U0001f3fb" +WOMAN_AND_MAN_HOLDING_HANDS_MEDIUM_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fd\u200d\U0001f91d\u200d\U0001f468\U0001f3fc" +WOMAN_AND_MAN_HOLDING_HANDS_MEDIUM_SKIN_TONE = "\U0001f46b\U0001f3fd" +WOMAN_AND_MAN_HOLDING_HANDS_MEDIUM_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fd\u200d\U0001f91d\u200d\U0001f468\U0001f3fe" +WOMAN_AND_MAN_HOLDING_HANDS_MEDIUM_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fd\u200d\U0001f91d\u200d\U0001f468\U0001f3ff" +WOMAN_AND_MAN_HOLDING_HANDS_MEDIUM_DARK_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fe\u200d\U0001f91d\u200d\U0001f468\U0001f3fb" +WOMAN_AND_MAN_HOLDING_HANDS_MEDIUM_DARK_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fe\u200d\U0001f91d\u200d\U0001f468\U0001f3fc" +WOMAN_AND_MAN_HOLDING_HANDS_MEDIUM_DARK_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f469\U0001f3fe\u200d\U0001f91d\u200d\U0001f468\U0001f3fd" +WOMAN_AND_MAN_HOLDING_HANDS_MEDIUM_DARK_SKIN_TONE = "\U0001f46b\U0001f3fe" +WOMAN_AND_MAN_HOLDING_HANDS_MEDIUM_DARK_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fe\u200d\U0001f91d\u200d\U0001f468\U0001f3ff" +WOMAN_AND_MAN_HOLDING_HANDS_DARK_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3ff\u200d\U0001f91d\u200d\U0001f468\U0001f3fb" +WOMAN_AND_MAN_HOLDING_HANDS_DARK_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3ff\u200d\U0001f91d\u200d\U0001f468\U0001f3fc" +WOMAN_AND_MAN_HOLDING_HANDS_DARK_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f469\U0001f3ff\u200d\U0001f91d\u200d\U0001f468\U0001f3fd" +WOMAN_AND_MAN_HOLDING_HANDS_DARK_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3ff\u200d\U0001f91d\u200d\U0001f468\U0001f3fe" +WOMAN_AND_MAN_HOLDING_HANDS_DARK_SKIN_TONE = "\U0001f46b\U0001f3ff" +MEN_HOLDING_HANDS = "\U0001f46c" +MEN_HOLDING_HANDS_LIGHT_SKIN_TONE = "\U0001f46c\U0001f3fb" +MEN_HOLDING_HANDS_LIGHT_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f468\U0001f3fb\u200d\U0001f91d\u200d\U0001f468\U0001f3fc" +MEN_HOLDING_HANDS_LIGHT_SKIN_TONE_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f91d\u200d\U0001f468\U0001f3fd" +MEN_HOLDING_HANDS_LIGHT_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f468\U0001f3fb\u200d\U0001f91d\u200d\U0001f468\U0001f3fe" +MEN_HOLDING_HANDS_LIGHT_SKIN_TONE_DARK_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\U0001f91d\u200d\U0001f468\U0001f3ff" +MEN_HOLDING_HANDS_MEDIUM_LIGHT_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f468\U0001f3fc\u200d\U0001f91d\u200d\U0001f468\U0001f3fb" +MEN_HOLDING_HANDS_MEDIUM_LIGHT_SKIN_TONE = "\U0001f46c\U0001f3fc" +MEN_HOLDING_HANDS_MEDIUM_LIGHT_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f468\U0001f3fc\u200d\U0001f91d\u200d\U0001f468\U0001f3fd" +MEN_HOLDING_HANDS_MEDIUM_LIGHT_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f468\U0001f3fc\u200d\U0001f91d\u200d\U0001f468\U0001f3fe" +MEN_HOLDING_HANDS_MEDIUM_LIGHT_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f468\U0001f3fc\u200d\U0001f91d\u200d\U0001f468\U0001f3ff" +MEN_HOLDING_HANDS_MEDIUM_SKIN_TONE_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f91d\u200d\U0001f468\U0001f3fb" +MEN_HOLDING_HANDS_MEDIUM_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f468\U0001f3fd\u200d\U0001f91d\u200d\U0001f468\U0001f3fc" +MEN_HOLDING_HANDS_MEDIUM_SKIN_TONE = "\U0001f46c\U0001f3fd" +MEN_HOLDING_HANDS_MEDIUM_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f468\U0001f3fd\u200d\U0001f91d\u200d\U0001f468\U0001f3fe" +MEN_HOLDING_HANDS_MEDIUM_SKIN_TONE_DARK_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\U0001f91d\u200d\U0001f468\U0001f3ff" +MEN_HOLDING_HANDS_MEDIUM_DARK_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f468\U0001f3fe\u200d\U0001f91d\u200d\U0001f468\U0001f3fb" +MEN_HOLDING_HANDS_MEDIUM_DARK_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f468\U0001f3fe\u200d\U0001f91d\u200d\U0001f468\U0001f3fc" +MEN_HOLDING_HANDS_MEDIUM_DARK_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f468\U0001f3fe\u200d\U0001f91d\u200d\U0001f468\U0001f3fd" +MEN_HOLDING_HANDS_MEDIUM_DARK_SKIN_TONE = "\U0001f46c\U0001f3fe" +MEN_HOLDING_HANDS_MEDIUM_DARK_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f468\U0001f3fe\u200d\U0001f91d\u200d\U0001f468\U0001f3ff" +MEN_HOLDING_HANDS_DARK_SKIN_TONE_LIGHT_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f91d\u200d\U0001f468\U0001f3fb" +MEN_HOLDING_HANDS_DARK_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f468\U0001f3ff\u200d\U0001f91d\u200d\U0001f468\U0001f3fc" +MEN_HOLDING_HANDS_DARK_SKIN_TONE_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\U0001f91d\u200d\U0001f468\U0001f3fd" +MEN_HOLDING_HANDS_DARK_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f468\U0001f3ff\u200d\U0001f91d\u200d\U0001f468\U0001f3fe" +MEN_HOLDING_HANDS_DARK_SKIN_TONE = "\U0001f46c\U0001f3ff" +KISS = "\U0001f48f" +KISS_LIGHT_SKIN_TONE = "\U0001f48f\U0001f3fb" +KISS_MEDIUM_LIGHT_SKIN_TONE = "\U0001f48f\U0001f3fc" +KISS_MEDIUM_SKIN_TONE = "\U0001f48f\U0001f3fd" +KISS_MEDIUM_DARK_SKIN_TONE = "\U0001f48f\U0001f3fe" +KISS_DARK_SKIN_TONE = "\U0001f48f\U0001f3ff" +KISS_PERSON_PERSON_LIGHT_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f9d1\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f9d1\U0001f3fc" +KISS_PERSON_PERSON_LIGHT_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f9d1\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f9d1\U0001f3fd" +KISS_PERSON_PERSON_LIGHT_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f9d1\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f9d1\U0001f3fe" +KISS_PERSON_PERSON_LIGHT_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f9d1\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f9d1\U0001f3ff" +KISS_PERSON_PERSON_MEDIUM_LIGHT_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f9d1\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f9d1\U0001f3fb" +KISS_PERSON_PERSON_MEDIUM_LIGHT_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f9d1\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f9d1\U0001f3fd" +KISS_PERSON_PERSON_MEDIUM_LIGHT_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f9d1\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f9d1\U0001f3fe" +KISS_PERSON_PERSON_MEDIUM_LIGHT_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f9d1\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f9d1\U0001f3ff" +KISS_PERSON_PERSON_MEDIUM_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f9d1\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f9d1\U0001f3fb" +KISS_PERSON_PERSON_MEDIUM_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f9d1\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f9d1\U0001f3fc" +KISS_PERSON_PERSON_MEDIUM_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f9d1\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f9d1\U0001f3fe" +KISS_PERSON_PERSON_MEDIUM_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f9d1\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f9d1\U0001f3ff" +KISS_PERSON_PERSON_MEDIUM_DARK_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f9d1\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f9d1\U0001f3fb" +KISS_PERSON_PERSON_MEDIUM_DARK_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f9d1\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f9d1\U0001f3fc" +KISS_PERSON_PERSON_MEDIUM_DARK_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f9d1\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f9d1\U0001f3fd" +KISS_PERSON_PERSON_MEDIUM_DARK_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f9d1\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f9d1\U0001f3ff" +KISS_PERSON_PERSON_DARK_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f9d1\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f9d1\U0001f3fb" +KISS_PERSON_PERSON_DARK_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f9d1\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f9d1\U0001f3fc" +KISS_PERSON_PERSON_DARK_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f9d1\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f9d1\U0001f3fd" +KISS_PERSON_PERSON_DARK_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f9d1\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f9d1\U0001f3fe" +KISS_WOMAN_MAN = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468" +KISS_WOMAN_MAN_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fb" +KISS_WOMAN_MAN_LIGHT_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fc" +KISS_WOMAN_MAN_LIGHT_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fd" +KISS_WOMAN_MAN_LIGHT_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fe" +KISS_WOMAN_MAN_LIGHT_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3ff" +KISS_WOMAN_MAN_MEDIUM_LIGHT_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fb" +KISS_WOMAN_MAN_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fc" +KISS_WOMAN_MAN_MEDIUM_LIGHT_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fd" +KISS_WOMAN_MAN_MEDIUM_LIGHT_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fe" +KISS_WOMAN_MAN_MEDIUM_LIGHT_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3ff" +KISS_WOMAN_MAN_MEDIUM_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fb" +KISS_WOMAN_MAN_MEDIUM_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fc" +KISS_WOMAN_MAN_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fd" +KISS_WOMAN_MAN_MEDIUM_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fe" +KISS_WOMAN_MAN_MEDIUM_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3ff" +KISS_WOMAN_MAN_MEDIUM_DARK_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fb" +KISS_WOMAN_MAN_MEDIUM_DARK_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fc" +KISS_WOMAN_MAN_MEDIUM_DARK_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fd" +KISS_WOMAN_MAN_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fe" +KISS_WOMAN_MAN_MEDIUM_DARK_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3ff" +KISS_WOMAN_MAN_DARK_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fb" +KISS_WOMAN_MAN_DARK_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fc" +KISS_WOMAN_MAN_DARK_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fd" +KISS_WOMAN_MAN_DARK_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fe" +KISS_WOMAN_MAN_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3ff" +KISS_MAN_MAN = "\U0001f468\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468" +KISS_MAN_MAN_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fb" +KISS_MAN_MAN_LIGHT_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fc" +KISS_MAN_MAN_LIGHT_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fd" +KISS_MAN_MAN_LIGHT_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fe" +KISS_MAN_MAN_LIGHT_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3ff" +KISS_MAN_MAN_MEDIUM_LIGHT_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fb" +KISS_MAN_MAN_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fc" +KISS_MAN_MAN_MEDIUM_LIGHT_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fd" +KISS_MAN_MAN_MEDIUM_LIGHT_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fe" +KISS_MAN_MAN_MEDIUM_LIGHT_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3ff" +KISS_MAN_MAN_MEDIUM_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fb" +KISS_MAN_MAN_MEDIUM_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fc" +KISS_MAN_MAN_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fd" +KISS_MAN_MAN_MEDIUM_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fe" +KISS_MAN_MAN_MEDIUM_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3ff" +KISS_MAN_MAN_MEDIUM_DARK_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fb" +KISS_MAN_MAN_MEDIUM_DARK_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fc" +KISS_MAN_MAN_MEDIUM_DARK_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fd" +KISS_MAN_MAN_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fe" +KISS_MAN_MAN_MEDIUM_DARK_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3ff" +KISS_MAN_MAN_DARK_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fb" +KISS_MAN_MAN_DARK_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fc" +KISS_MAN_MAN_DARK_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fd" +KISS_MAN_MAN_DARK_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fe" +KISS_MAN_MAN_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3ff" +KISS_WOMAN_WOMAN = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469" +KISS_WOMAN_WOMAN_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fb" +KISS_WOMAN_WOMAN_LIGHT_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fc" +KISS_WOMAN_WOMAN_LIGHT_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fd" +KISS_WOMAN_WOMAN_LIGHT_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fe" +KISS_WOMAN_WOMAN_LIGHT_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3ff" +KISS_WOMAN_WOMAN_MEDIUM_LIGHT_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fb" +KISS_WOMAN_WOMAN_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fc" +KISS_WOMAN_WOMAN_MEDIUM_LIGHT_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fd" +KISS_WOMAN_WOMAN_MEDIUM_LIGHT_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fe" +KISS_WOMAN_WOMAN_MEDIUM_LIGHT_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3ff" +KISS_WOMAN_WOMAN_MEDIUM_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fb" +KISS_WOMAN_WOMAN_MEDIUM_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fc" +KISS_WOMAN_WOMAN_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fd" +KISS_WOMAN_WOMAN_MEDIUM_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fe" +KISS_WOMAN_WOMAN_MEDIUM_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3ff" +KISS_WOMAN_WOMAN_MEDIUM_DARK_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fb" +KISS_WOMAN_WOMAN_MEDIUM_DARK_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fc" +KISS_WOMAN_WOMAN_MEDIUM_DARK_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fd" +KISS_WOMAN_WOMAN_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fe" +KISS_WOMAN_WOMAN_MEDIUM_DARK_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3ff" +KISS_WOMAN_WOMAN_DARK_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fb" +KISS_WOMAN_WOMAN_DARK_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fc" +KISS_WOMAN_WOMAN_DARK_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fd" +KISS_WOMAN_WOMAN_DARK_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fe" +KISS_WOMAN_WOMAN_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3ff" +COUPLE_WITH_HEART = "\U0001f491" +COUPLE_WITH_HEART_LIGHT_SKIN_TONE = "\U0001f491\U0001f3fb" +COUPLE_WITH_HEART_MEDIUM_LIGHT_SKIN_TONE = "\U0001f491\U0001f3fc" +COUPLE_WITH_HEART_MEDIUM_SKIN_TONE = "\U0001f491\U0001f3fd" +COUPLE_WITH_HEART_MEDIUM_DARK_SKIN_TONE = "\U0001f491\U0001f3fe" +COUPLE_WITH_HEART_DARK_SKIN_TONE = "\U0001f491\U0001f3ff" +COUPLE_WITH_HEART_PERSON_PERSON_LIGHT_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f9d1\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f9d1\U0001f3fc" +COUPLE_WITH_HEART_PERSON_PERSON_LIGHT_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f9d1\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f9d1\U0001f3fd" +COUPLE_WITH_HEART_PERSON_PERSON_LIGHT_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f9d1\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f9d1\U0001f3fe" +COUPLE_WITH_HEART_PERSON_PERSON_LIGHT_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f9d1\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f9d1\U0001f3ff" +COUPLE_WITH_HEART_PERSON_PERSON_MEDIUM_LIGHT_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f9d1\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f9d1\U0001f3fb" +COUPLE_WITH_HEART_PERSON_PERSON_MEDIUM_LIGHT_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f9d1\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f9d1\U0001f3fd" +COUPLE_WITH_HEART_PERSON_PERSON_MEDIUM_LIGHT_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f9d1\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f9d1\U0001f3fe" +COUPLE_WITH_HEART_PERSON_PERSON_MEDIUM_LIGHT_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f9d1\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f9d1\U0001f3ff" +COUPLE_WITH_HEART_PERSON_PERSON_MEDIUM_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f9d1\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f9d1\U0001f3fb" +COUPLE_WITH_HEART_PERSON_PERSON_MEDIUM_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f9d1\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f9d1\U0001f3fc" +COUPLE_WITH_HEART_PERSON_PERSON_MEDIUM_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f9d1\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f9d1\U0001f3fe" +COUPLE_WITH_HEART_PERSON_PERSON_MEDIUM_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f9d1\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f9d1\U0001f3ff" +COUPLE_WITH_HEART_PERSON_PERSON_MEDIUM_DARK_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f9d1\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f9d1\U0001f3fb" +COUPLE_WITH_HEART_PERSON_PERSON_MEDIUM_DARK_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f9d1\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f9d1\U0001f3fc" +COUPLE_WITH_HEART_PERSON_PERSON_MEDIUM_DARK_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f9d1\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f9d1\U0001f3fd" +COUPLE_WITH_HEART_PERSON_PERSON_MEDIUM_DARK_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f9d1\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f9d1\U0001f3ff" +COUPLE_WITH_HEART_PERSON_PERSON_DARK_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f9d1\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f9d1\U0001f3fb" +COUPLE_WITH_HEART_PERSON_PERSON_DARK_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f9d1\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f9d1\U0001f3fc" +COUPLE_WITH_HEART_PERSON_PERSON_DARK_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f9d1\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f9d1\U0001f3fd" +COUPLE_WITH_HEART_PERSON_PERSON_DARK_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f9d1\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f9d1\U0001f3fe" +COUPLE_WITH_HEART_WOMAN_MAN = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f468" +COUPLE_WITH_HEART_WOMAN_MAN_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fb" +COUPLE_WITH_HEART_WOMAN_MAN_LIGHT_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fc" +COUPLE_WITH_HEART_WOMAN_MAN_LIGHT_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fd" +COUPLE_WITH_HEART_WOMAN_MAN_LIGHT_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fe" +COUPLE_WITH_HEART_WOMAN_MAN_LIGHT_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3ff" +COUPLE_WITH_HEART_WOMAN_MAN_MEDIUM_LIGHT_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fb" +COUPLE_WITH_HEART_WOMAN_MAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fc" +COUPLE_WITH_HEART_WOMAN_MAN_MEDIUM_LIGHT_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fd" +COUPLE_WITH_HEART_WOMAN_MAN_MEDIUM_LIGHT_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fe" +COUPLE_WITH_HEART_WOMAN_MAN_MEDIUM_LIGHT_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3ff" +COUPLE_WITH_HEART_WOMAN_MAN_MEDIUM_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fb" +COUPLE_WITH_HEART_WOMAN_MAN_MEDIUM_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fc" +COUPLE_WITH_HEART_WOMAN_MAN_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fd" +COUPLE_WITH_HEART_WOMAN_MAN_MEDIUM_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fe" +COUPLE_WITH_HEART_WOMAN_MAN_MEDIUM_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3ff" +COUPLE_WITH_HEART_WOMAN_MAN_MEDIUM_DARK_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fb" +COUPLE_WITH_HEART_WOMAN_MAN_MEDIUM_DARK_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fc" +COUPLE_WITH_HEART_WOMAN_MAN_MEDIUM_DARK_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fd" +COUPLE_WITH_HEART_WOMAN_MAN_MEDIUM_DARK_SKIN_TONE = "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fe" +COUPLE_WITH_HEART_WOMAN_MAN_MEDIUM_DARK_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3ff" +COUPLE_WITH_HEART_WOMAN_MAN_DARK_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fb" +COUPLE_WITH_HEART_WOMAN_MAN_DARK_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fc" +COUPLE_WITH_HEART_WOMAN_MAN_DARK_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fd" +COUPLE_WITH_HEART_WOMAN_MAN_DARK_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fe" +COUPLE_WITH_HEART_WOMAN_MAN_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3ff" +COUPLE_WITH_HEART_MAN_MAN = "\U0001f468\u200d\u2764\ufe0f\u200d\U0001f468" +COUPLE_WITH_HEART_MAN_MAN_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fb" +COUPLE_WITH_HEART_MAN_MAN_LIGHT_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fc" +COUPLE_WITH_HEART_MAN_MAN_LIGHT_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fd" +COUPLE_WITH_HEART_MAN_MAN_LIGHT_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fe" +COUPLE_WITH_HEART_MAN_MAN_LIGHT_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3ff" +COUPLE_WITH_HEART_MAN_MAN_MEDIUM_LIGHT_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fb" +COUPLE_WITH_HEART_MAN_MAN_MEDIUM_LIGHT_SKIN_TONE = "\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fc" +COUPLE_WITH_HEART_MAN_MAN_MEDIUM_LIGHT_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fd" +COUPLE_WITH_HEART_MAN_MAN_MEDIUM_LIGHT_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fe" +COUPLE_WITH_HEART_MAN_MAN_MEDIUM_LIGHT_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3ff" +COUPLE_WITH_HEART_MAN_MAN_MEDIUM_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fb" +COUPLE_WITH_HEART_MAN_MAN_MEDIUM_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fc" +COUPLE_WITH_HEART_MAN_MAN_MEDIUM_SKIN_TONE = "\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fd" +COUPLE_WITH_HEART_MAN_MAN_MEDIUM_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fe" +COUPLE_WITH_HEART_MAN_MAN_MEDIUM_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3ff" +COUPLE_WITH_HEART_MAN_MAN_MEDIUM_DARK_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fb" +COUPLE_WITH_HEART_MAN_MAN_MEDIUM_DARK_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fc" +COUPLE_WITH_HEART_MAN_MAN_MEDIUM_DARK_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fd" +COUPLE_WITH_HEART_MAN_MAN_MEDIUM_DARK_SKIN_TONE = "\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fe" +COUPLE_WITH_HEART_MAN_MAN_MEDIUM_DARK_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3ff" +COUPLE_WITH_HEART_MAN_MAN_DARK_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fb" +COUPLE_WITH_HEART_MAN_MAN_DARK_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fc" +COUPLE_WITH_HEART_MAN_MAN_DARK_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fd" +COUPLE_WITH_HEART_MAN_MAN_DARK_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fe" +COUPLE_WITH_HEART_MAN_MAN_DARK_SKIN_TONE = "\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3ff" +COUPLE_WITH_HEART_WOMAN_WOMAN = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f469" +COUPLE_WITH_HEART_WOMAN_WOMAN_LIGHT_SKIN_TONE = "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fb" +COUPLE_WITH_HEART_WOMAN_WOMAN_LIGHT_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fc" +COUPLE_WITH_HEART_WOMAN_WOMAN_LIGHT_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fd" +COUPLE_WITH_HEART_WOMAN_WOMAN_LIGHT_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fe" +COUPLE_WITH_HEART_WOMAN_WOMAN_LIGHT_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3ff" +COUPLE_WITH_HEART_WOMAN_WOMAN_MEDIUM_LIGHT_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fb" +COUPLE_WITH_HEART_WOMAN_WOMAN_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fc" +COUPLE_WITH_HEART_WOMAN_WOMAN_MEDIUM_LIGHT_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fd" +COUPLE_WITH_HEART_WOMAN_WOMAN_MEDIUM_LIGHT_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fe" +COUPLE_WITH_HEART_WOMAN_WOMAN_MEDIUM_LIGHT_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3ff" +COUPLE_WITH_HEART_WOMAN_WOMAN_MEDIUM_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fb" +COUPLE_WITH_HEART_WOMAN_WOMAN_MEDIUM_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fc" +COUPLE_WITH_HEART_WOMAN_WOMAN_MEDIUM_SKIN_TONE = "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fd" +COUPLE_WITH_HEART_WOMAN_WOMAN_MEDIUM_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fe" +COUPLE_WITH_HEART_WOMAN_WOMAN_MEDIUM_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3ff" +COUPLE_WITH_HEART_WOMAN_WOMAN_MEDIUM_DARK_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fb" +COUPLE_WITH_HEART_WOMAN_WOMAN_MEDIUM_DARK_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fc" +COUPLE_WITH_HEART_WOMAN_WOMAN_MEDIUM_DARK_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fd" +COUPLE_WITH_HEART_WOMAN_WOMAN_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fe" +COUPLE_WITH_HEART_WOMAN_WOMAN_MEDIUM_DARK_SKIN_TONE_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3ff" +COUPLE_WITH_HEART_WOMAN_WOMAN_DARK_SKIN_TONE_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fb" +COUPLE_WITH_HEART_WOMAN_WOMAN_DARK_SKIN_TONE_MEDIUM_LIGHT_SKIN_TONE = \ + "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fc" +COUPLE_WITH_HEART_WOMAN_WOMAN_DARK_SKIN_TONE_MEDIUM_SKIN_TONE = \ + "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fd" +COUPLE_WITH_HEART_WOMAN_WOMAN_DARK_SKIN_TONE_MEDIUM_DARK_SKIN_TONE = \ + "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fe" +COUPLE_WITH_HEART_WOMAN_WOMAN_DARK_SKIN_TONE = "\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3ff" +FAMILY = "\U0001f46a" +FAMILY_MAN_WOMAN_BOY = "\U0001f468\u200d\U0001f469\u200d\U0001f466" +FAMILY_MAN_WOMAN_GIRL = "\U0001f468\u200d\U0001f469\u200d\U0001f467" +FAMILY_MAN_WOMAN_GIRL_BOY = "\U0001f468\u200d\U0001f469\u200d\U0001f467\u200d\U0001f466" +FAMILY_MAN_WOMAN_BOY_BOY = "\U0001f468\u200d\U0001f469\u200d\U0001f466\u200d\U0001f466" +FAMILY_MAN_WOMAN_GIRL_GIRL = "\U0001f468\u200d\U0001f469\u200d\U0001f467\u200d\U0001f467" +FAMILY_MAN_MAN_BOY = "\U0001f468\u200d\U0001f468\u200d\U0001f466" +FAMILY_MAN_MAN_GIRL = "\U0001f468\u200d\U0001f468\u200d\U0001f467" +FAMILY_MAN_MAN_GIRL_BOY = "\U0001f468\u200d\U0001f468\u200d\U0001f467\u200d\U0001f466" +FAMILY_MAN_MAN_BOY_BOY = "\U0001f468\u200d\U0001f468\u200d\U0001f466\u200d\U0001f466" +FAMILY_MAN_MAN_GIRL_GIRL = "\U0001f468\u200d\U0001f468\u200d\U0001f467\u200d\U0001f467" +FAMILY_WOMAN_WOMAN_BOY = "\U0001f469\u200d\U0001f469\u200d\U0001f466" +FAMILY_WOMAN_WOMAN_GIRL = "\U0001f469\u200d\U0001f469\u200d\U0001f467" +FAMILY_WOMAN_WOMAN_GIRL_BOY = "\U0001f469\u200d\U0001f469\u200d\U0001f467\u200d\U0001f466" +FAMILY_WOMAN_WOMAN_BOY_BOY = "\U0001f469\u200d\U0001f469\u200d\U0001f466\u200d\U0001f466" +FAMILY_WOMAN_WOMAN_GIRL_GIRL = "\U0001f469\u200d\U0001f469\u200d\U0001f467\u200d\U0001f467" +FAMILY_MAN_BOY = "\U0001f468\u200d\U0001f466" +FAMILY_MAN_BOY_BOY = "\U0001f468\u200d\U0001f466\u200d\U0001f466" +FAMILY_MAN_GIRL = "\U0001f468\u200d\U0001f467" +FAMILY_MAN_GIRL_BOY = "\U0001f468\u200d\U0001f467\u200d\U0001f466" +FAMILY_MAN_GIRL_GIRL = "\U0001f468\u200d\U0001f467\u200d\U0001f467" +FAMILY_WOMAN_BOY = "\U0001f469\u200d\U0001f466" +FAMILY_WOMAN_BOY_BOY = "\U0001f469\u200d\U0001f466\u200d\U0001f466" +FAMILY_WOMAN_GIRL = "\U0001f469\u200d\U0001f467" +FAMILY_WOMAN_GIRL_BOY = "\U0001f469\u200d\U0001f467\u200d\U0001f466" +FAMILY_WOMAN_GIRL_GIRL = "\U0001f469\u200d\U0001f467\u200d\U0001f467" +SPEAKING_HEAD = "\U0001f5e3\ufe0f" +BUST_IN_SILHOUETTE = "\U0001f464" +BUSTS_IN_SILHOUETTE = "\U0001f465" +PEOPLE_HUGGING = "\U0001fac2" +FOOTPRINTS = "\U0001f463" +LIGHT_SKIN_TONE = "\U0001f3fb" +MEDIUM_LIGHT_SKIN_TONE = "\U0001f3fc" +MEDIUM_SKIN_TONE = "\U0001f3fd" +MEDIUM_DARK_SKIN_TONE = "\U0001f3fe" +DARK_SKIN_TONE = "\U0001f3ff" +RED_HAIR = "\U0001f9b0" +CURLY_HAIR = "\U0001f9b1" +WHITE_HAIR = "\U0001f9b3" +BALD = "\U0001f9b2" +MONKEY_FACE = "\U0001f435" +MONKEY = "\U0001f412" +GORILLA = "\U0001f98d" +ORANGUTAN = "\U0001f9a7" +DOG_FACE = "\U0001f436" +DOG = "\U0001f415" +GUIDE_DOG = "\U0001f9ae" +SERVICE_DOG = "\U0001f415\u200d\U0001f9ba" +POODLE = "\U0001f429" +WOLF = "\U0001f43a" +FOX = "\U0001f98a" +RACCOON = "\U0001f99d" +CAT_FACE = "\U0001f431" +CAT = "\U0001f408" +BLACK_CAT = "\U0001f408\u200d\u2b1b" +LION = "\U0001f981" +TIGER_FACE = "\U0001f42f" +TIGER = "\U0001f405" +LEOPARD = "\U0001f406" +HORSE_FACE = "\U0001f434" +HORSE = "\U0001f40e" +UNICORN = "\U0001f984" +ZEBRA = "\U0001f993" +DEER = "\U0001f98c" +BISON = "\U0001f9ac" +COW_FACE = "\U0001f42e" +OX = "\U0001f402" +WATER_BUFFALO = "\U0001f403" +COW = "\U0001f404" +PIG_FACE = "\U0001f437" +PIG = "\U0001f416" +BOAR = "\U0001f417" +PIG_NOSE = "\U0001f43d" +RAM = "\U0001f40f" +EWE = "\U0001f411" +GOAT = "\U0001f410" +CAMEL = "\U0001f42a" +TWO_HUMP_CAMEL = "\U0001f42b" +LLAMA = "\U0001f999" +GIRAFFE = "\U0001f992" +ELEPHANT = "\U0001f418" +MAMMOTH = "\U0001f9a3" +RHINOCEROS = "\U0001f98f" +HIPPOPOTAMUS = "\U0001f99b" +MOUSE_FACE = "\U0001f42d" +MOUSE = "\U0001f401" +RAT = "\U0001f400" +HAMSTER = "\U0001f439" +RABBIT_FACE = "\U0001f430" +RABBIT = "\U0001f407" +CHIPMUNK = "\U0001f43f\ufe0f" +BEAVER = "\U0001f9ab" +HEDGEHOG = "\U0001f994" +BAT = "\U0001f987" +BEAR = "\U0001f43b" +POLAR_BEAR = "\U0001f43b\u200d\u2744\ufe0f" +KOALA = "\U0001f428" +PANDA = "\U0001f43c" +SLOTH = "\U0001f9a5" +OTTER = "\U0001f9a6" +SKUNK = "\U0001f9a8" +KANGAROO = "\U0001f998" +BADGER = "\U0001f9a1" +PAW_PRINTS = "\U0001f43e" +TURKEY = "\U0001f983" +CHICKEN = "\U0001f414" +ROOSTER = "\U0001f413" +HATCHING_CHICK = "\U0001f423" +BABY_CHICK = "\U0001f424" +FRONT_FACING_BABY_CHICK = "\U0001f425" +BIRD = "\U0001f426" +PENGUIN = "\U0001f427" +DOVE = "\U0001f54a\ufe0f" +EAGLE = "\U0001f985" +DUCK = "\U0001f986" +SWAN = "\U0001f9a2" +OWL = "\U0001f989" +DODO = "\U0001f9a4" +FEATHER = "\U0001fab6" +FLAMINGO = "\U0001f9a9" +PEACOCK = "\U0001f99a" +PARROT = "\U0001f99c" +FROG = "\U0001f438" +CROCODILE = "\U0001f40a" +TURTLE = "\U0001f422" +LIZARD = "\U0001f98e" +SNAKE = "\U0001f40d" +DRAGON_FACE = "\U0001f432" +DRAGON = "\U0001f409" +SAUROPOD = "\U0001f995" +T_REX = "\U0001f996" +SPOUTING_WHALE = "\U0001f433" +WHALE = "\U0001f40b" +DOLPHIN = "\U0001f42c" +SEAL = "\U0001f9ad" +FISH = "\U0001f41f" +TROPICAL_FISH = "\U0001f420" +BLOWFISH = "\U0001f421" +SHARK = "\U0001f988" +OCTOPUS = "\U0001f419" +SPIRAL_SHELL = "\U0001f41a" +CORAL = "\U0001fab8" +SNAIL = "\U0001f40c" +BUTTERFLY = "\U0001f98b" +BUG = "\U0001f41b" +ANT = "\U0001f41c" +HONEYBEE = "\U0001f41d" +BEETLE = "\U0001fab2" +LADY_BEETLE = "\U0001f41e" +CRICKET = "\U0001f997" +COCKROACH = "\U0001fab3" +SPIDER = "\U0001f577\ufe0f" +SPIDER_WEB = "\U0001f578\ufe0f" +SCORPION = "\U0001f982" +MOSQUITO = "\U0001f99f" +FLY = "\U0001fab0" +WORM = "\U0001fab1" +MICROBE = "\U0001f9a0" +BOUQUET = "\U0001f490" +CHERRY_BLOSSOM = "\U0001f338" +WHITE_FLOWER = "\U0001f4ae" +LOTUS = "\U0001fab7" +ROSETTE = "\U0001f3f5\ufe0f" +ROSE = "\U0001f339" +WILTED_FLOWER = "\U0001f940" +HIBISCUS = "\U0001f33a" +SUNFLOWER = "\U0001f33b" +BLOSSOM = "\U0001f33c" +TULIP = "\U0001f337" +SEEDLING = "\U0001f331" +POTTED_PLANT = "\U0001fab4" +EVERGREEN_TREE = "\U0001f332" +DECIDUOUS_TREE = "\U0001f333" +PALM_TREE = "\U0001f334" +CACTUS = "\U0001f335" +SHEAF_OF_RICE = "\U0001f33e" +HERB = "\U0001f33f" +SHAMROCK = "\u2618\ufe0f" +FOUR_LEAF_CLOVER = "\U0001f340" +MAPLE_LEAF = "\U0001f341" +FALLEN_LEAF = "\U0001f342" +LEAF_FLUTTERING_IN_WIND = "\U0001f343" +EMPTY_NEST = "\U0001fab9" +NEST_WITH_EGGS = "\U0001faba" +GRAPES = "\U0001f347" +MELON = "\U0001f348" +WATERMELON = "\U0001f349" +TANGERINE = "\U0001f34a" +LEMON = "\U0001f34b" +BANANA = "\U0001f34c" +PINEAPPLE = "\U0001f34d" +MANGO = "\U0001f96d" +RED_APPLE = "\U0001f34e" +GREEN_APPLE = "\U0001f34f" +PEAR = "\U0001f350" +PEACH = "\U0001f351" +CHERRIES = "\U0001f352" +STRAWBERRY = "\U0001f353" +BLUEBERRIES = "\U0001fad0" +KIWI_FRUIT = "\U0001f95d" +TOMATO = "\U0001f345" +OLIVE = "\U0001fad2" +COCONUT = "\U0001f965" +AVOCADO = "\U0001f951" +EGGPLANT = "\U0001f346" +POTATO = "\U0001f954" +CARROT = "\U0001f955" +EAR_OF_CORN = "\U0001f33d" +HOT_PEPPER = "\U0001f336\ufe0f" +BELL_PEPPER = "\U0001fad1" +CUCUMBER = "\U0001f952" +LEAFY_GREEN = "\U0001f96c" +BROCCOLI = "\U0001f966" +GARLIC = "\U0001f9c4" +ONION = "\U0001f9c5" +MUSHROOM = "\U0001f344" +PEANUTS = "\U0001f95c" +BEANS = "\U0001fad8" +CHESTNUT = "\U0001f330" +BREAD = "\U0001f35e" +CROISSANT = "\U0001f950" +BAGUETTE_BREAD = "\U0001f956" +FLATBREAD = "\U0001fad3" +PRETZEL = "\U0001f968" +BAGEL = "\U0001f96f" +PANCAKES = "\U0001f95e" +WAFFLE = "\U0001f9c7" +CHEESE_WEDGE = "\U0001f9c0" +MEAT_ON_BONE = "\U0001f356" +POULTRY_LEG = "\U0001f357" +CUT_OF_MEAT = "\U0001f969" +BACON = "\U0001f953" +HAMBURGER = "\U0001f354" +FRENCH_FRIES = "\U0001f35f" +PIZZA = "\U0001f355" +HOT_DOG = "\U0001f32d" +SANDWICH = "\U0001f96a" +TACO = "\U0001f32e" +BURRITO = "\U0001f32f" +TAMALE = "\U0001fad4" +STUFFED_FLATBREAD = "\U0001f959" +FALAFEL = "\U0001f9c6" +EGG = "\U0001f95a" +COOKING = "\U0001f373" +SHALLOW_PAN_OF_FOOD = "\U0001f958" +POT_OF_FOOD = "\U0001f372" +FONDUE = "\U0001fad5" +BOWL_WITH_SPOON = "\U0001f963" +GREEN_SALAD = "\U0001f957" +POPCORN = "\U0001f37f" +BUTTER = "\U0001f9c8" +SALT = "\U0001f9c2" +CANNED_FOOD = "\U0001f96b" +BENTO_BOX = "\U0001f371" +RICE_CRACKER = "\U0001f358" +RICE_BALL = "\U0001f359" +COOKED_RICE = "\U0001f35a" +CURRY_RICE = "\U0001f35b" +STEAMING_BOWL = "\U0001f35c" +SPAGHETTI = "\U0001f35d" +ROASTED_SWEET_POTATO = "\U0001f360" +ODEN = "\U0001f362" +SUSHI = "\U0001f363" +FRIED_SHRIMP = "\U0001f364" +FISH_CAKE_WITH_SWIRL = "\U0001f365" +MOON_CAKE = "\U0001f96e" +DANGO = "\U0001f361" +DUMPLING = "\U0001f95f" +FORTUNE_COOKIE = "\U0001f960" +TAKEOUT_BOX = "\U0001f961" +CRAB = "\U0001f980" +LOBSTER = "\U0001f99e" +SHRIMP = "\U0001f990" +SQUID = "\U0001f991" +OYSTER = "\U0001f9aa" +SOFT_ICE_CREAM = "\U0001f366" +SHAVED_ICE = "\U0001f367" +ICE_CREAM = "\U0001f368" +DOUGHNUT = "\U0001f369" +COOKIE = "\U0001f36a" +BIRTHDAY_CAKE = "\U0001f382" +SHORTCAKE = "\U0001f370" +CUPCAKE = "\U0001f9c1" +PIE = "\U0001f967" +CHOCOLATE_BAR = "\U0001f36b" +CANDY = "\U0001f36c" +LOLLIPOP = "\U0001f36d" +CUSTARD = "\U0001f36e" +HONEY_POT = "\U0001f36f" +BABY_BOTTLE = "\U0001f37c" +GLASS_OF_MILK = "\U0001f95b" +HOT_BEVERAGE = "\u2615" +TEAPOT = "\U0001fad6" +TEACUP_WITHOUT_HANDLE = "\U0001f375" +SAKE = "\U0001f376" +BOTTLE_WITH_POPPING_CORK = "\U0001f37e" +WINE_GLASS = "\U0001f377" +COCKTAIL_GLASS = "\U0001f378" +TROPICAL_DRINK = "\U0001f379" +BEER_MUG = "\U0001f37a" +CLINKING_BEER_MUGS = "\U0001f37b" +CLINKING_GLASSES = "\U0001f942" +TUMBLER_GLASS = "\U0001f943" +POURING_LIQUID = "\U0001fad7" +CUP_WITH_STRAW = "\U0001f964" +BUBBLE_TEA = "\U0001f9cb" +BEVERAGE_BOX = "\U0001f9c3" +MATE = "\U0001f9c9" +ICE = "\U0001f9ca" +CHOPSTICKS = "\U0001f962" +FORK_AND_KNIFE_WITH_PLATE = "\U0001f37d\ufe0f" +FORK_AND_KNIFE = "\U0001f374" +SPOON = "\U0001f944" +KITCHEN_KNIFE = "\U0001f52a" +JAR = "\U0001fad9" +AMPHORA = "\U0001f3fa" +GLOBE_SHOWING_EUROPE_AFRICA = "\U0001f30d" +GLOBE_SHOWING_AMERICAS = "\U0001f30e" +GLOBE_SHOWING_ASIA_AUSTRALIA = "\U0001f30f" +GLOBE_WITH_MERIDIANS = "\U0001f310" +WORLD_MAP = "\U0001f5fa\ufe0f" +MAP_OF_JAPAN = "\U0001f5fe" +COMPASS = "\U0001f9ed" +SNOW_CAPPED_MOUNTAIN = "\U0001f3d4\ufe0f" +MOUNTAIN = "\u26f0\ufe0f" +VOLCANO = "\U0001f30b" +MOUNT_FUJI = "\U0001f5fb" +CAMPING = "\U0001f3d5\ufe0f" +BEACH_WITH_UMBRELLA = "\U0001f3d6\ufe0f" +DESERT = "\U0001f3dc\ufe0f" +DESERT_ISLAND = "\U0001f3dd\ufe0f" +NATIONAL_PARK = "\U0001f3de\ufe0f" +STADIUM = "\U0001f3df\ufe0f" +CLASSICAL_BUILDING = "\U0001f3db\ufe0f" +BUILDING_CONSTRUCTION = "\U0001f3d7\ufe0f" +BRICK = "\U0001f9f1" +ROCK = "\U0001faa8" +WOOD = "\U0001fab5" +HUT = "\U0001f6d6" +HOUSES = "\U0001f3d8\ufe0f" +DERELICT_HOUSE = "\U0001f3da\ufe0f" +HOUSE = "\U0001f3e0" +HOUSE_WITH_GARDEN = "\U0001f3e1" +OFFICE_BUILDING = "\U0001f3e2" +JAPANESE_POST_OFFICE = "\U0001f3e3" +POST_OFFICE = "\U0001f3e4" +HOSPITAL = "\U0001f3e5" +BANK = "\U0001f3e6" +HOTEL = "\U0001f3e8" +LOVE_HOTEL = "\U0001f3e9" +CONVENIENCE_STORE = "\U0001f3ea" +SCHOOL = "\U0001f3eb" +DEPARTMENT_STORE = "\U0001f3ec" +FACTORY = "\U0001f3ed" +JAPANESE_CASTLE = "\U0001f3ef" +CASTLE = "\U0001f3f0" +WEDDING = "\U0001f492" +TOKYO_TOWER = "\U0001f5fc" +STATUE_OF_LIBERTY = "\U0001f5fd" +CHURCH = "\u26ea" +MOSQUE = "\U0001f54c" +HINDU_TEMPLE = "\U0001f6d5" +SYNAGOGUE = "\U0001f54d" +SHINTO_SHRINE = "\u26e9\ufe0f" +KAABA = "\U0001f54b" +FOUNTAIN = "\u26f2" +TENT = "\u26fa" +FOGGY = "\U0001f301" +NIGHT_WITH_STARS = "\U0001f303" +CITYSCAPE = "\U0001f3d9\ufe0f" +SUNRISE_OVER_MOUNTAINS = "\U0001f304" +SUNRISE = "\U0001f305" +CITYSCAPE_AT_DUSK = "\U0001f306" +SUNSET = "\U0001f307" +BRIDGE_AT_NIGHT = "\U0001f309" +HOT_SPRINGS = "\u2668\ufe0f" +CAROUSEL_HORSE = "\U0001f3a0" +PLAYGROUND_SLIDE = "\U0001f6dd" +FERRIS_WHEEL = "\U0001f3a1" +ROLLER_COASTER = "\U0001f3a2" +BARBER_POLE = "\U0001f488" +CIRCUS_TENT = "\U0001f3aa" +LOCOMOTIVE = "\U0001f682" +RAILWAY_CAR = "\U0001f683" +HIGH_SPEED_TRAIN = "\U0001f684" +BULLET_TRAIN = "\U0001f685" +TRAIN = "\U0001f686" +METRO = "\U0001f687" +LIGHT_RAIL = "\U0001f688" +STATION = "\U0001f689" +TRAM = "\U0001f68a" +MONORAIL = "\U0001f69d" +MOUNTAIN_RAILWAY = "\U0001f69e" +TRAM_CAR = "\U0001f68b" +BUS = "\U0001f68c" +ONCOMING_BUS = "\U0001f68d" +TROLLEYBUS = "\U0001f68e" +MINIBUS = "\U0001f690" +AMBULANCE = "\U0001f691" +FIRE_ENGINE = "\U0001f692" +POLICE_CAR = "\U0001f693" +ONCOMING_POLICE_CAR = "\U0001f694" +TAXI = "\U0001f695" +ONCOMING_TAXI = "\U0001f696" +AUTOMOBILE = "\U0001f697" +ONCOMING_AUTOMOBILE = "\U0001f698" +SPORT_UTILITY_VEHICLE = "\U0001f699" +PICKUP_TRUCK = "\U0001f6fb" +DELIVERY_TRUCK = "\U0001f69a" +ARTICULATED_LORRY = "\U0001f69b" +TRACTOR = "\U0001f69c" +RACING_CAR = "\U0001f3ce\ufe0f" +MOTORCYCLE = "\U0001f3cd\ufe0f" +MOTOR_SCOOTER = "\U0001f6f5" +MANUAL_WHEELCHAIR = "\U0001f9bd" +MOTORIZED_WHEELCHAIR = "\U0001f9bc" +AUTO_RICKSHAW = "\U0001f6fa" +BICYCLE = "\U0001f6b2" +KICK_SCOOTER = "\U0001f6f4" +SKATEBOARD = "\U0001f6f9" +ROLLER_SKATE = "\U0001f6fc" +BUS_STOP = "\U0001f68f" +MOTORWAY = "\U0001f6e3\ufe0f" +RAILWAY_TRACK = "\U0001f6e4\ufe0f" +OIL_DRUM = "\U0001f6e2\ufe0f" +FUEL_PUMP = "\u26fd" +WHEEL = "\U0001f6de" +POLICE_CAR_LIGHT = "\U0001f6a8" +HORIZONTAL_TRAFFIC_LIGHT = "\U0001f6a5" +VERTICAL_TRAFFIC_LIGHT = "\U0001f6a6" +STOP_SIGN = "\U0001f6d1" +CONSTRUCTION = "\U0001f6a7" +ANCHOR = "\u2693" +RING_BUOY = "\U0001f6df" +SAILBOAT = "\u26f5" +CANOE = "\U0001f6f6" +SPEEDBOAT = "\U0001f6a4" +PASSENGER_SHIP = "\U0001f6f3\ufe0f" +FERRY = "\u26f4\ufe0f" +MOTOR_BOAT = "\U0001f6e5\ufe0f" +SHIP = "\U0001f6a2" +AIRPLANE = "\u2708\ufe0f" +SMALL_AIRPLANE = "\U0001f6e9\ufe0f" +AIRPLANE_DEPARTURE = "\U0001f6eb" +AIRPLANE_ARRIVAL = "\U0001f6ec" +PARACHUTE = "\U0001fa82" +SEAT = "\U0001f4ba" +HELICOPTER = "\U0001f681" +SUSPENSION_RAILWAY = "\U0001f69f" +MOUNTAIN_CABLEWAY = "\U0001f6a0" +AERIAL_TRAMWAY = "\U0001f6a1" +SATELLITE = "\U0001f6f0\ufe0f" +ROCKET = "\U0001f680" +FLYING_SAUCER = "\U0001f6f8" +BELLHOP_BELL = "\U0001f6ce\ufe0f" +LUGGAGE = "\U0001f9f3" +HOURGLASS_DONE = "\u231b" +HOURGLASS_NOT_DONE = "\u23f3" +WATCH = "\u231a" +ALARM_CLOCK = "\u23f0" +STOPWATCH = "\u23f1\ufe0f" +TIMER_CLOCK = "\u23f2\ufe0f" +MANTELPIECE_CLOCK = "\U0001f570\ufe0f" +TWELVE_O_CLOCK = "\U0001f55b" +TWELVE_THIRTY = "\U0001f567" +ONE_O_CLOCK = "\U0001f550" +ONE_THIRTY = "\U0001f55c" +TWO_O_CLOCK = "\U0001f551" +TWO_THIRTY = "\U0001f55d" +THREE_O_CLOCK = "\U0001f552" +THREE_THIRTY = "\U0001f55e" +FOUR_O_CLOCK = "\U0001f553" +FOUR_THIRTY = "\U0001f55f" +FIVE_O_CLOCK = "\U0001f554" +FIVE_THIRTY = "\U0001f560" +SIX_O_CLOCK = "\U0001f555" +SIX_THIRTY = "\U0001f561" +SEVEN_O_CLOCK = "\U0001f556" +SEVEN_THIRTY = "\U0001f562" +EIGHT_O_CLOCK = "\U0001f557" +EIGHT_THIRTY = "\U0001f563" +NINE_O_CLOCK = "\U0001f558" +NINE_THIRTY = "\U0001f564" +TEN_O_CLOCK = "\U0001f559" +TEN_THIRTY = "\U0001f565" +ELEVEN_O_CLOCK = "\U0001f55a" +ELEVEN_THIRTY = "\U0001f566" +NEW_MOON = "\U0001f311" +WAXING_CRESCENT_MOON = "\U0001f312" +FIRST_QUARTER_MOON = "\U0001f313" +WAXING_GIBBOUS_MOON = "\U0001f314" +FULL_MOON = "\U0001f315" +WANING_GIBBOUS_MOON = "\U0001f316" +LAST_QUARTER_MOON = "\U0001f317" +WANING_CRESCENT_MOON = "\U0001f318" +CRESCENT_MOON = "\U0001f319" +NEW_MOON_FACE = "\U0001f31a" +FIRST_QUARTER_MOON_FACE = "\U0001f31b" +LAST_QUARTER_MOON_FACE = "\U0001f31c" +THERMOMETER = "\U0001f321\ufe0f" +SUN = "\u2600\ufe0f" +FULL_MOON_FACE = "\U0001f31d" +SUN_WITH_FACE = "\U0001f31e" +RINGED_PLANET = "\U0001fa90" +STAR = "\u2b50" +GLOWING_STAR = "\U0001f31f" +SHOOTING_STAR = "\U0001f320" +MILKY_WAY = "\U0001f30c" +CLOUD = "\u2601\ufe0f" +SUN_BEHIND_CLOUD = "\u26c5" +CLOUD_WITH_LIGHTNING_AND_RAIN = "\u26c8\ufe0f" +SUN_BEHIND_SMALL_CLOUD = "\U0001f324\ufe0f" +SUN_BEHIND_LARGE_CLOUD = "\U0001f325\ufe0f" +SUN_BEHIND_RAIN_CLOUD = "\U0001f326\ufe0f" +CLOUD_WITH_RAIN = "\U0001f327\ufe0f" +CLOUD_WITH_SNOW = "\U0001f328\ufe0f" +CLOUD_WITH_LIGHTNING = "\U0001f329\ufe0f" +TORNADO = "\U0001f32a\ufe0f" +FOG = "\U0001f32b\ufe0f" +WIND_FACE = "\U0001f32c\ufe0f" +CYCLONE = "\U0001f300" +RAINBOW = "\U0001f308" +CLOSED_UMBRELLA = "\U0001f302" +UMBRELLA = "\u2602\ufe0f" +UMBRELLA_WITH_RAIN_DROPS = "\u2614" +UMBRELLA_ON_GROUND = "\u26f1\ufe0f" +HIGH_VOLTAGE = "\u26a1" +SNOWFLAKE = "\u2744\ufe0f" +SNOWMAN = "\u2603\ufe0f" +SNOWMAN_WITHOUT_SNOW = "\u26c4" +COMET = "\u2604\ufe0f" +FIRE = "\U0001f525" +DROPLET = "\U0001f4a7" +WATER_WAVE = "\U0001f30a" +JACK_O_LANTERN = "\U0001f383" +CHRISTMAS_TREE = "\U0001f384" +FIREWORKS = "\U0001f386" +SPARKLER = "\U0001f387" +FIRECRACKER = "\U0001f9e8" +SPARKLES = "\u2728" +BALLOON = "\U0001f388" +PARTY_POPPER = "\U0001f389" +CONFETTI_BALL = "\U0001f38a" +TANABATA_TREE = "\U0001f38b" +PINE_DECORATION = "\U0001f38d" +JAPANESE_DOLLS = "\U0001f38e" +CARP_STREAMER = "\U0001f38f" +WIND_CHIME = "\U0001f390" +MOON_VIEWING_CEREMONY = "\U0001f391" +RED_ENVELOPE = "\U0001f9e7" +RIBBON = "\U0001f380" +WRAPPED_GIFT = "\U0001f381" +REMINDER_RIBBON = "\U0001f397\ufe0f" +ADMISSION_TICKETS = "\U0001f39f\ufe0f" +TICKET = "\U0001f3ab" +MILITARY_MEDAL = "\U0001f396\ufe0f" +TROPHY = "\U0001f3c6" +SPORTS_MEDAL = "\U0001f3c5" +FIRST_PLACE_MEDAL = "\U0001f947" +SECOND_PLACE_MEDAL = "\U0001f948" +THIRD_PLACE_MEDAL = "\U0001f949" +SOCCER_BALL = "\u26bd" +BASEBALL = "\u26be" +SOFTBALL = "\U0001f94e" +BASKETBALL = "\U0001f3c0" +VOLLEYBALL = "\U0001f3d0" +AMERICAN_FOOTBALL = "\U0001f3c8" +RUGBY_FOOTBALL = "\U0001f3c9" +TENNIS = "\U0001f3be" +FLYING_DISC = "\U0001f94f" +BOWLING = "\U0001f3b3" +CRICKET_GAME = "\U0001f3cf" +FIELD_HOCKEY = "\U0001f3d1" +ICE_HOCKEY = "\U0001f3d2" +LACROSSE = "\U0001f94d" +PING_PONG = "\U0001f3d3" +BADMINTON = "\U0001f3f8" +BOXING_GLOVE = "\U0001f94a" +MARTIAL_ARTS_UNIFORM = "\U0001f94b" +GOAL_NET = "\U0001f945" +FLAG_IN_HOLE = "\u26f3" +ICE_SKATE = "\u26f8\ufe0f" +FISHING_POLE = "\U0001f3a3" +DIVING_MASK = "\U0001f93f" +RUNNING_SHIRT = "\U0001f3bd" +SKIS = "\U0001f3bf" +SLED = "\U0001f6f7" +CURLING_STONE = "\U0001f94c" +BULLSEYE = "\U0001f3af" +YO_YO = "\U0001fa80" +KITE = "\U0001fa81" +POOL_8_BALL = "\U0001f3b1" +CRYSTAL_BALL = "\U0001f52e" +MAGIC_WAND = "\U0001fa84" +NAZAR_AMULET = "\U0001f9ff" +HAMSA = "\U0001faac" +VIDEO_GAME = "\U0001f3ae" +JOYSTICK = "\U0001f579\ufe0f" +SLOT_MACHINE = "\U0001f3b0" +GAME_DIE = "\U0001f3b2" +PUZZLE_PIECE = "\U0001f9e9" +TEDDY_BEAR = "\U0001f9f8" +PINATA = "\U0001fa85" +MIRROR_BALL = "\U0001faa9" +NESTING_DOLLS = "\U0001fa86" +SPADE_SUIT = "\u2660\ufe0f" +HEART_SUIT = "\u2665\ufe0f" +DIAMOND_SUIT = "\u2666\ufe0f" +CLUB_SUIT = "\u2663\ufe0f" +CHESS_PAWN = "\u265f\ufe0f" +JOKER = "\U0001f0cf" +MAHJONG_RED_DRAGON = "\U0001f004" +FLOWER_PLAYING_CARDS = "\U0001f3b4" +PERFORMING_ARTS = "\U0001f3ad" +FRAMED_PICTURE = "\U0001f5bc\ufe0f" +ARTIST_PALETTE = "\U0001f3a8" +THREAD = "\U0001f9f5" +SEWING_NEEDLE = "\U0001faa1" +YARN = "\U0001f9f6" +KNOT = "\U0001faa2" +GLASSES = "\U0001f453" +SUNGLASSES = "\U0001f576\ufe0f" +GOGGLES = "\U0001f97d" +LAB_COAT = "\U0001f97c" +SAFETY_VEST = "\U0001f9ba" +NECKTIE = "\U0001f454" +T_SHIRT = "\U0001f455" +JEANS = "\U0001f456" +SCARF = "\U0001f9e3" +GLOVES = "\U0001f9e4" +COAT = "\U0001f9e5" +SOCKS = "\U0001f9e6" +DRESS = "\U0001f457" +KIMONO = "\U0001f458" +SARI = "\U0001f97b" +ONE_PIECE_SWIMSUIT = "\U0001fa71" +BRIEFS = "\U0001fa72" +SHORTS = "\U0001fa73" +BIKINI = "\U0001f459" +WOMAN_S_CLOTHES = "\U0001f45a" +PURSE = "\U0001f45b" +HANDBAG = "\U0001f45c" +CLUTCH_BAG = "\U0001f45d" +SHOPPING_BAGS = "\U0001f6cd\ufe0f" +BACKPACK = "\U0001f392" +THONG_SANDAL = "\U0001fa74" +MAN_S_SHOE = "\U0001f45e" +RUNNING_SHOE = "\U0001f45f" +HIKING_BOOT = "\U0001f97e" +FLAT_SHOE = "\U0001f97f" +HIGH_HEELED_SHOE = "\U0001f460" +WOMAN_S_SANDAL = "\U0001f461" +BALLET_SHOES = "\U0001fa70" +WOMAN_S_BOOT = "\U0001f462" +CROWN = "\U0001f451" +WOMAN_S_HAT = "\U0001f452" +TOP_HAT = "\U0001f3a9" +GRADUATION_CAP = "\U0001f393" +BILLED_CAP = "\U0001f9e2" +MILITARY_HELMET = "\U0001fa96" +RESCUE_WORKER_S_HELMET = "\u26d1\ufe0f" +PRAYER_BEADS = "\U0001f4ff" +LIPSTICK = "\U0001f484" +RING = "\U0001f48d" +GEM_STONE = "\U0001f48e" +MUTED_SPEAKER = "\U0001f507" +SPEAKER_LOW_VOLUME = "\U0001f508" +SPEAKER_MEDIUM_VOLUME = "\U0001f509" +SPEAKER_HIGH_VOLUME = "\U0001f50a" +LOUDSPEAKER = "\U0001f4e2" +MEGAPHONE = "\U0001f4e3" +POSTAL_HORN = "\U0001f4ef" +BELL = "\U0001f514" +BELL_WITH_SLASH = "\U0001f515" +MUSICAL_SCORE = "\U0001f3bc" +MUSICAL_NOTE = "\U0001f3b5" +MUSICAL_NOTES = "\U0001f3b6" +STUDIO_MICROPHONE = "\U0001f399\ufe0f" +LEVEL_SLIDER = "\U0001f39a\ufe0f" +CONTROL_KNOBS = "\U0001f39b\ufe0f" +MICROPHONE = "\U0001f3a4" +HEADPHONE = "\U0001f3a7" +RADIO = "\U0001f4fb" +SAXOPHONE = "\U0001f3b7" +ACCORDION = "\U0001fa97" +GUITAR = "\U0001f3b8" +MUSICAL_KEYBOARD = "\U0001f3b9" +TRUMPET = "\U0001f3ba" +VIOLIN = "\U0001f3bb" +BANJO = "\U0001fa95" +DRUM = "\U0001f941" +LONG_DRUM = "\U0001fa98" +MOBILE_PHONE = "\U0001f4f1" +MOBILE_PHONE_WITH_ARROW = "\U0001f4f2" +TELEPHONE = "\u260e\ufe0f" +TELEPHONE_RECEIVER = "\U0001f4de" +PAGER = "\U0001f4df" +FAX_MACHINE = "\U0001f4e0" +BATTERY = "\U0001f50b" +LOW_BATTERY = "\U0001faab" +ELECTRIC_PLUG = "\U0001f50c" +LAPTOP = "\U0001f4bb" +DESKTOP_COMPUTER = "\U0001f5a5\ufe0f" +PRINTER = "\U0001f5a8\ufe0f" +KEYBOARD = "\u2328\ufe0f" +COMPUTER_MOUSE = "\U0001f5b1\ufe0f" +TRACKBALL = "\U0001f5b2\ufe0f" +COMPUTER_DISK = "\U0001f4bd" +FLOPPY_DISK = "\U0001f4be" +OPTICAL_DISK = "\U0001f4bf" +DVD = "\U0001f4c0" +ABACUS = "\U0001f9ee" +MOVIE_CAMERA = "\U0001f3a5" +FILM_FRAMES = "\U0001f39e\ufe0f" +FILM_PROJECTOR = "\U0001f4fd\ufe0f" +CLAPPER_BOARD = "\U0001f3ac" +TELEVISION = "\U0001f4fa" +CAMERA = "\U0001f4f7" +CAMERA_WITH_FLASH = "\U0001f4f8" +VIDEO_CAMERA = "\U0001f4f9" +VIDEOCASSETTE = "\U0001f4fc" +MAGNIFYING_GLASS_TILTED_LEFT = "\U0001f50d" +MAGNIFYING_GLASS_TILTED_RIGHT = "\U0001f50e" +CANDLE = "\U0001f56f\ufe0f" +LIGHT_BULB = "\U0001f4a1" +FLASHLIGHT = "\U0001f526" +RED_PAPER_LANTERN = "\U0001f3ee" +DIYA_LAMP = "\U0001fa94" +NOTEBOOK_WITH_DECORATIVE_COVER = "\U0001f4d4" +CLOSED_BOOK = "\U0001f4d5" +OPEN_BOOK = "\U0001f4d6" +GREEN_BOOK = "\U0001f4d7" +BLUE_BOOK = "\U0001f4d8" +ORANGE_BOOK = "\U0001f4d9" +BOOKS = "\U0001f4da" +NOTEBOOK = "\U0001f4d3" +LEDGER = "\U0001f4d2" +PAGE_WITH_CURL = "\U0001f4c3" +SCROLL = "\U0001f4dc" +PAGE_FACING_UP = "\U0001f4c4" +NEWSPAPER = "\U0001f4f0" +ROLLED_UP_NEWSPAPER = "\U0001f5de\ufe0f" +BOOKMARK_TABS = "\U0001f4d1" +BOOKMARK = "\U0001f516" +LABEL = "\U0001f3f7\ufe0f" +MONEY_BAG = "\U0001f4b0" +COIN = "\U0001fa99" +YEN_BANKNOTE = "\U0001f4b4" +DOLLAR_BANKNOTE = "\U0001f4b5" +EURO_BANKNOTE = "\U0001f4b6" +POUND_BANKNOTE = "\U0001f4b7" +MONEY_WITH_WINGS = "\U0001f4b8" +CREDIT_CARD = "\U0001f4b3" +RECEIPT = "\U0001f9fe" +CHART_INCREASING_WITH_YEN = "\U0001f4b9" +ENVELOPE = "\u2709\ufe0f" +E_MAIL = "\U0001f4e7" +INCOMING_ENVELOPE = "\U0001f4e8" +ENVELOPE_WITH_ARROW = "\U0001f4e9" +OUTBOX_TRAY = "\U0001f4e4" +INBOX_TRAY = "\U0001f4e5" +PACKAGE = "\U0001f4e6" +CLOSED_MAILBOX_WITH_RAISED_FLAG = "\U0001f4eb" +CLOSED_MAILBOX_WITH_LOWERED_FLAG = "\U0001f4ea" +OPEN_MAILBOX_WITH_RAISED_FLAG = "\U0001f4ec" +OPEN_MAILBOX_WITH_LOWERED_FLAG = "\U0001f4ed" +POSTBOX = "\U0001f4ee" +BALLOT_BOX_WITH_BALLOT = "\U0001f5f3\ufe0f" +PENCIL = "\u270f\ufe0f" +BLACK_NIB = "\u2712\ufe0f" +FOUNTAIN_PEN = "\U0001f58b\ufe0f" +PEN = "\U0001f58a\ufe0f" +PAINTBRUSH = "\U0001f58c\ufe0f" +CRAYON = "\U0001f58d\ufe0f" +MEMO = "\U0001f4dd" +BRIEFCASE = "\U0001f4bc" +FILE_FOLDER = "\U0001f4c1" +OPEN_FILE_FOLDER = "\U0001f4c2" +CARD_INDEX_DIVIDERS = "\U0001f5c2\ufe0f" +CALENDAR = "\U0001f4c5" +TEAR_OFF_CALENDAR = "\U0001f4c6" +SPIRAL_NOTEPAD = "\U0001f5d2\ufe0f" +SPIRAL_CALENDAR = "\U0001f5d3\ufe0f" +CARD_INDEX = "\U0001f4c7" +CHART_INCREASING = "\U0001f4c8" +CHART_DECREASING = "\U0001f4c9" +BAR_CHART = "\U0001f4ca" +CLIPBOARD = "\U0001f4cb" +PUSHPIN = "\U0001f4cc" +ROUND_PUSHPIN = "\U0001f4cd" +PAPERCLIP = "\U0001f4ce" +LINKED_PAPERCLIPS = "\U0001f587\ufe0f" +STRAIGHT_RULER = "\U0001f4cf" +TRIANGULAR_RULER = "\U0001f4d0" +SCISSORS = "\u2702\ufe0f" +CARD_FILE_BOX = "\U0001f5c3\ufe0f" +FILE_CABINET = "\U0001f5c4\ufe0f" +WASTEBASKET = "\U0001f5d1\ufe0f" +LOCKED = "\U0001f512" +UNLOCKED = "\U0001f513" +LOCKED_WITH_PEN = "\U0001f50f" +LOCKED_WITH_KEY = "\U0001f510" +KEY = "\U0001f511" +OLD_KEY = "\U0001f5dd\ufe0f" +HAMMER = "\U0001f528" +AXE = "\U0001fa93" +PICK = "\u26cf\ufe0f" +HAMMER_AND_PICK = "\u2692\ufe0f" +HAMMER_AND_WRENCH = "\U0001f6e0\ufe0f" +DAGGER = "\U0001f5e1\ufe0f" +CROSSED_SWORDS = "\u2694\ufe0f" +WATER_PISTOL = "\U0001f52b" +BOOMERANG = "\U0001fa83" +BOW_AND_ARROW = "\U0001f3f9" +SHIELD = "\U0001f6e1\ufe0f" +CARPENTRY_SAW = "\U0001fa9a" +WRENCH = "\U0001f527" +SCREWDRIVER = "\U0001fa9b" +NUT_AND_BOLT = "\U0001f529" +GEAR = "\u2699\ufe0f" +CLAMP = "\U0001f5dc\ufe0f" +BALANCE_SCALE = "\u2696\ufe0f" +WHITE_CANE = "\U0001f9af" +LINK = "\U0001f517" +CHAINS = "\u26d3\ufe0f" +HOOK = "\U0001fa9d" +TOOLBOX = "\U0001f9f0" +MAGNET = "\U0001f9f2" +LADDER = "\U0001fa9c" +ALEMBIC = "\u2697\ufe0f" +TEST_TUBE = "\U0001f9ea" +PETRI_DISH = "\U0001f9eb" +DNA = "\U0001f9ec" +MICROSCOPE = "\U0001f52c" +TELESCOPE = "\U0001f52d" +SATELLITE_ANTENNA = "\U0001f4e1" +SYRINGE = "\U0001f489" +DROP_OF_BLOOD = "\U0001fa78" +PILL = "\U0001f48a" +ADHESIVE_BANDAGE = "\U0001fa79" +CRUTCH = "\U0001fa7c" +STETHOSCOPE = "\U0001fa7a" +X_RAY = "\U0001fa7b" +DOOR = "\U0001f6aa" +ELEVATOR = "\U0001f6d7" +MIRROR = "\U0001fa9e" +WINDOW = "\U0001fa9f" +BED = "\U0001f6cf\ufe0f" +COUCH_AND_LAMP = "\U0001f6cb\ufe0f" +CHAIR = "\U0001fa91" +TOILET = "\U0001f6bd" +PLUNGER = "\U0001faa0" +SHOWER = "\U0001f6bf" +BATHTUB = "\U0001f6c1" +MOUSE_TRAP = "\U0001faa4" +RAZOR = "\U0001fa92" +LOTION_BOTTLE = "\U0001f9f4" +SAFETY_PIN = "\U0001f9f7" +BROOM = "\U0001f9f9" +BASKET = "\U0001f9fa" +ROLL_OF_PAPER = "\U0001f9fb" +BUCKET = "\U0001faa3" +SOAP = "\U0001f9fc" +BUBBLES = "\U0001fae7" +TOOTHBRUSH = "\U0001faa5" +SPONGE = "\U0001f9fd" +FIRE_EXTINGUISHER = "\U0001f9ef" +SHOPPING_CART = "\U0001f6d2" +CIGARETTE = "\U0001f6ac" +COFFIN = "\u26b0\ufe0f" +HEADSTONE = "\U0001faa6" +FUNERAL_URN = "\u26b1\ufe0f" +MOAI = "\U0001f5ff" +PLACARD = "\U0001faa7" +IDENTIFICATION_CARD = "\U0001faaa" +ATM_SIGN = "\U0001f3e7" +LITTER_IN_BIN_SIGN = "\U0001f6ae" +POTABLE_WATER = "\U0001f6b0" +WHEELCHAIR_SYMBOL = "\u267f" +MEN_S_ROOM = "\U0001f6b9" +WOMEN_S_ROOM = "\U0001f6ba" +RESTROOM = "\U0001f6bb" +BABY_SYMBOL = "\U0001f6bc" +WATER_CLOSET = "\U0001f6be" +PASSPORT_CONTROL = "\U0001f6c2" +CUSTOMS = "\U0001f6c3" +BAGGAGE_CLAIM = "\U0001f6c4" +LEFT_LUGGAGE = "\U0001f6c5" +WARNING = "\u26a0\ufe0f" +CHILDREN_CROSSING = "\U0001f6b8" +NO_ENTRY = "\u26d4" +PROHIBITED = "\U0001f6ab" +NO_BICYCLES = "\U0001f6b3" +NO_SMOKING = "\U0001f6ad" +NO_LITTERING = "\U0001f6af" +NON_POTABLE_WATER = "\U0001f6b1" +NO_PEDESTRIANS = "\U0001f6b7" +NO_MOBILE_PHONES = "\U0001f4f5" +NO_ONE_UNDER_EIGHTEEN = "\U0001f51e" +RADIOACTIVE = "\u2622\ufe0f" +BIOHAZARD = "\u2623\ufe0f" +UP_ARROW = "\u2b06\ufe0f" +UP_RIGHT_ARROW = "\u2197\ufe0f" +RIGHT_ARROW = "\u27a1\ufe0f" +DOWN_RIGHT_ARROW = "\u2198\ufe0f" +DOWN_ARROW = "\u2b07\ufe0f" +DOWN_LEFT_ARROW = "\u2199\ufe0f" +LEFT_ARROW = "\u2b05\ufe0f" +UP_LEFT_ARROW = "\u2196\ufe0f" +UP_DOWN_ARROW = "\u2195\ufe0f" +LEFT_RIGHT_ARROW = "\u2194\ufe0f" +RIGHT_ARROW_CURVING_LEFT = "\u21a9\ufe0f" +LEFT_ARROW_CURVING_RIGHT = "\u21aa\ufe0f" +RIGHT_ARROW_CURVING_UP = "\u2934\ufe0f" +RIGHT_ARROW_CURVING_DOWN = "\u2935\ufe0f" +CLOCKWISE_VERTICAL_ARROWS = "\U0001f503" +COUNTERCLOCKWISE_ARROWS_BUTTON = "\U0001f504" +BACK_ARROW = "\U0001f519" +END_ARROW = "\U0001f51a" +ON_ARROW = "\U0001f51b" +SOON_ARROW = "\U0001f51c" +TOP_ARROW = "\U0001f51d" +PLACE_OF_WORSHIP = "\U0001f6d0" +ATOM_SYMBOL = "\u269b\ufe0f" +OM = "\U0001f549\ufe0f" +STAR_OF_DAVID = "\u2721\ufe0f" +WHEEL_OF_DHARMA = "\u2638\ufe0f" +YIN_YANG = "\u262f\ufe0f" +LATIN_CROSS = "\u271d\ufe0f" +ORTHODOX_CROSS = "\u2626\ufe0f" +STAR_AND_CRESCENT = "\u262a\ufe0f" +PEACE_SYMBOL = "\u262e\ufe0f" +MENORAH = "\U0001f54e" +DOTTED_SIX_POINTED_STAR = "\U0001f52f" +ARIES = "\u2648" +TAURUS = "\u2649" +GEMINI = "\u264a" +CANCER = "\u264b" +LEO = "\u264c" +VIRGO = "\u264d" +LIBRA = "\u264e" +SCORPIO = "\u264f" +SAGITTARIUS = "\u2650" +CAPRICORN = "\u2651" +AQUARIUS = "\u2652" +PISCES = "\u2653" +OPHIUCHUS = "\u26ce" +SHUFFLE_TRACKS_BUTTON = "\U0001f500" +REPEAT_BUTTON = "\U0001f501" +REPEAT_SINGLE_BUTTON = "\U0001f502" +PLAY_BUTTON = "\u25b6\ufe0f" +FAST_FORWARD_BUTTON = "\u23e9" +NEXT_TRACK_BUTTON = "\u23ed\ufe0f" +PLAY_OR_PAUSE_BUTTON = "\u23ef\ufe0f" +REVERSE_BUTTON = "\u25c0\ufe0f" +FAST_REVERSE_BUTTON = "\u23ea" +LAST_TRACK_BUTTON = "\u23ee\ufe0f" +UPWARDS_BUTTON = "\U0001f53c" +FAST_UP_BUTTON = "\u23eb" +DOWNWARDS_BUTTON = "\U0001f53d" +FAST_DOWN_BUTTON = "\u23ec" +PAUSE_BUTTON = "\u23f8\ufe0f" +STOP_BUTTON = "\u23f9\ufe0f" +RECORD_BUTTON = "\u23fa\ufe0f" +EJECT_BUTTON = "\u23cf\ufe0f" +CINEMA = "\U0001f3a6" +DIM_BUTTON = "\U0001f505" +BRIGHT_BUTTON = "\U0001f506" +ANTENNA_BARS = "\U0001f4f6" +VIBRATION_MODE = "\U0001f4f3" +MOBILE_PHONE_OFF = "\U0001f4f4" +FEMALE_SIGN = "\u2640\ufe0f" +MALE_SIGN = "\u2642\ufe0f" +TRANSGENDER_SYMBOL = "\u26a7\ufe0f" +MULTIPLY = "\u2716\ufe0f" +PLUS = "\u2795" +MINUS = "\u2796" +DIVIDE = "\u2797" +HEAVY_EQUALS_SIGN = "\U0001f7f0" +INFINITY = "\u267e\ufe0f" +DOUBLE_EXCLAMATION_MARK = "\u203c\ufe0f" +EXCLAMATION_QUESTION_MARK = "\u2049\ufe0f" +RED_QUESTION_MARK = "\u2753" +WHITE_QUESTION_MARK = "\u2754" +WHITE_EXCLAMATION_MARK = "\u2755" +RED_EXCLAMATION_MARK = "\u2757" +WAVY_DASH = "\u3030\ufe0f" +CURRENCY_EXCHANGE = "\U0001f4b1" +HEAVY_DOLLAR_SIGN = "\U0001f4b2" +MEDICAL_SYMBOL = "\u2695\ufe0f" +RECYCLING_SYMBOL = "\u267b\ufe0f" +FLEUR_DE_LIS = "\u269c\ufe0f" +TRIDENT_EMBLEM = "\U0001f531" +NAME_BADGE = "\U0001f4db" +JAPANESE_SYMBOL_FOR_BEGINNER = "\U0001f530" +HOLLOW_RED_CIRCLE = "\u2b55" +CHECK_MARK_BUTTON = "\u2705" +CHECK_BOX_WITH_CHECK = "\u2611\ufe0f" +CHECK_MARK = "\u2714\ufe0f" +CROSS_MARK = "\u274c" +CROSS_MARK_BUTTON = "\u274e" +CURLY_LOOP = "\u27b0" +DOUBLE_CURLY_LOOP = "\u27bf" +PART_ALTERNATION_MARK = "\u303d\ufe0f" +EIGHT_SPOKED_ASTERISK = "\u2733\ufe0f" +EIGHT_POINTED_STAR = "\u2734\ufe0f" +SPARKLE = "\u2747\ufe0f" +COPYRIGHT = "\xa9\ufe0f" +REGISTERED = "\xae\ufe0f" +TRADE_MARK = "\u2122\ufe0f" +KEYCAP_NUMBER_SIGN = "#\ufe0f\u20e3" +KEYCAP_ASTERISK = "*\ufe0f\u20e3" +KEYCAP_DIGIT_ZERO = "0\ufe0f\u20e3" +KEYCAP_DIGIT_ONE = "1\ufe0f\u20e3" +KEYCAP_DIGIT_TWO = "2\ufe0f\u20e3" +KEYCAP_DIGIT_THREE = "3\ufe0f\u20e3" +KEYCAP_DIGIT_FOUR = "4\ufe0f\u20e3" +KEYCAP_DIGIT_FIVE = "5\ufe0f\u20e3" +KEYCAP_DIGIT_SIX = "6\ufe0f\u20e3" +KEYCAP_DIGIT_SEVEN = "7\ufe0f\u20e3" +KEYCAP_DIGIT_EIGHT = "8\ufe0f\u20e3" +KEYCAP_DIGIT_NINE = "9\ufe0f\u20e3" +KEYCAP_10 = "\U0001f51f" +INPUT_LATIN_UPPERCASE = "\U0001f520" +INPUT_LATIN_LOWERCASE = "\U0001f521" +INPUT_NUMBERS = "\U0001f522" +INPUT_SYMBOLS = "\U0001f523" +INPUT_LATIN_LETTERS = "\U0001f524" +A_BUTTON_BLOOD_TYPE = "\U0001f170\ufe0f" +AB_BUTTON_BLOOD_TYPE = "\U0001f18e" +B_BUTTON_BLOOD_TYPE = "\U0001f171\ufe0f" +CL_BUTTON = "\U0001f191" +COOL_BUTTON = "\U0001f192" +FREE_BUTTON = "\U0001f193" +INFORMATION = "\u2139\ufe0f" +ID_BUTTON = "\U0001f194" +CIRCLED_M = "\u24c2\ufe0f" +NEW_BUTTON = "\U0001f195" +NG_BUTTON = "\U0001f196" +O_BUTTON_BLOOD_TYPE = "\U0001f17e\ufe0f" +OK_BUTTON = "\U0001f197" +P_BUTTON = "\U0001f17f\ufe0f" +SOS_BUTTON = "\U0001f198" +UP_BUTTON = "\U0001f199" +VS_BUTTON = "\U0001f19a" +JAPANESE_HERE_BUTTON = "\U0001f201" +JAPANESE_SERVICE_CHARGE_BUTTON = "\U0001f202\ufe0f" +JAPANESE_MONTHLY_AMOUNT_BUTTON = "\U0001f237\ufe0f" +JAPANESE_NOT_FREE_OF_CHARGE_BUTTON = "\U0001f236" +JAPANESE_RESERVED_BUTTON = "\U0001f22f" +JAPANESE_BARGAIN_BUTTON = "\U0001f250" +JAPANESE_DISCOUNT_BUTTON = "\U0001f239" +JAPANESE_FREE_OF_CHARGE_BUTTON = "\U0001f21a" +JAPANESE_PROHIBITED_BUTTON = "\U0001f232" +JAPANESE_ACCEPTABLE_BUTTON = "\U0001f251" +JAPANESE_APPLICATION_BUTTON = "\U0001f238" +JAPANESE_PASSING_GRADE_BUTTON = "\U0001f234" +JAPANESE_VACANCY_BUTTON = "\U0001f233" +JAPANESE_CONGRATULATIONS_BUTTON = "\u3297\ufe0f" +JAPANESE_SECRET_BUTTON = "\u3299\ufe0f" +JAPANESE_OPEN_FOR_BUSINESS_BUTTON = "\U0001f23a" +JAPANESE_NO_VACANCY_BUTTON = "\U0001f235" +RED_CIRCLE = "\U0001f534" +ORANGE_CIRCLE = "\U0001f7e0" +YELLOW_CIRCLE = "\U0001f7e1" +GREEN_CIRCLE = "\U0001f7e2" +BLUE_CIRCLE = "\U0001f535" +PURPLE_CIRCLE = "\U0001f7e3" +BROWN_CIRCLE = "\U0001f7e4" +BLACK_CIRCLE = "\u26ab" +WHITE_CIRCLE = "\u26aa" +RED_SQUARE = "\U0001f7e5" +ORANGE_SQUARE = "\U0001f7e7" +YELLOW_SQUARE = "\U0001f7e8" +GREEN_SQUARE = "\U0001f7e9" +BLUE_SQUARE = "\U0001f7e6" +PURPLE_SQUARE = "\U0001f7ea" +BROWN_SQUARE = "\U0001f7eb" +BLACK_LARGE_SQUARE = "\u2b1b" +WHITE_LARGE_SQUARE = "\u2b1c" +BLACK_MEDIUM_SQUARE = "\u25fc\ufe0f" +WHITE_MEDIUM_SQUARE = "\u25fb\ufe0f" +BLACK_MEDIUM_SMALL_SQUARE = "\u25fe" +WHITE_MEDIUM_SMALL_SQUARE = "\u25fd" +BLACK_SMALL_SQUARE = "\u25aa\ufe0f" +WHITE_SMALL_SQUARE = "\u25ab\ufe0f" +LARGE_ORANGE_DIAMOND = "\U0001f536" +LARGE_BLUE_DIAMOND = "\U0001f537" +SMALL_ORANGE_DIAMOND = "\U0001f538" +SMALL_BLUE_DIAMOND = "\U0001f539" +RED_TRIANGLE_POINTED_UP = "\U0001f53a" +RED_TRIANGLE_POINTED_DOWN = "\U0001f53b" +DIAMOND_WITH_A_DOT = "\U0001f4a0" +RADIO_BUTTON = "\U0001f518" +WHITE_SQUARE_BUTTON = "\U0001f533" +BLACK_SQUARE_BUTTON = "\U0001f532" +CHEQUERED_FLAG = "\U0001f3c1" +TRIANGULAR_FLAG = "\U0001f6a9" +CROSSED_FLAGS = "\U0001f38c" +BLACK_FLAG = "\U0001f3f4" +WHITE_FLAG = "\U0001f3f3\ufe0f" +RAINBOW_FLAG = "\U0001f3f3\ufe0f\u200d\U0001f308" +TRANSGENDER_FLAG = "\U0001f3f3\ufe0f\u200d\u26a7\ufe0f" +PIRATE_FLAG = "\U0001f3f4\u200d\u2620\ufe0f" +FLAG_ASCENSION_ISLAND = "\U0001f1e6\U0001f1e8" +FLAG_ANDORRA = "\U0001f1e6\U0001f1e9" +FLAG_UNITED_ARAB_EMIRATES = "\U0001f1e6\U0001f1ea" +FLAG_AFGHANISTAN = "\U0001f1e6\U0001f1eb" +FLAG_ANTIGUA_ANDAMP_BARBUDA = "\U0001f1e6\U0001f1ec" +FLAG_ANGUILLA = "\U0001f1e6\U0001f1ee" +FLAG_ALBANIA = "\U0001f1e6\U0001f1f1" +FLAG_ARMENIA = "\U0001f1e6\U0001f1f2" +FLAG_ANGOLA = "\U0001f1e6\U0001f1f4" +FLAG_ANTARCTICA = "\U0001f1e6\U0001f1f6" +FLAG_ARGENTINA = "\U0001f1e6\U0001f1f7" +FLAG_AMERICAN_SAMOA = "\U0001f1e6\U0001f1f8" +FLAG_AUSTRIA = "\U0001f1e6\U0001f1f9" +FLAG_AUSTRALIA = "\U0001f1e6\U0001f1fa" +FLAG_ARUBA = "\U0001f1e6\U0001f1fc" +FLAG_ALAND_ISLANDS = "\U0001f1e6\U0001f1fd" +FLAG_AZERBAIJAN = "\U0001f1e6\U0001f1ff" +FLAG_BOSNIA_ANDAMP_HERZEGOVINA = "\U0001f1e7\U0001f1e6" +FLAG_BARBADOS = "\U0001f1e7\U0001f1e7" +FLAG_BANGLADESH = "\U0001f1e7\U0001f1e9" +FLAG_BELGIUM = "\U0001f1e7\U0001f1ea" +FLAG_BURKINA_FASO = "\U0001f1e7\U0001f1eb" +FLAG_BULGARIA = "\U0001f1e7\U0001f1ec" +FLAG_BAHRAIN = "\U0001f1e7\U0001f1ed" +FLAG_BURUNDI = "\U0001f1e7\U0001f1ee" +FLAG_BENIN = "\U0001f1e7\U0001f1ef" +FLAG_ST_BARTHELEMY = "\U0001f1e7\U0001f1f1" +FLAG_BERMUDA = "\U0001f1e7\U0001f1f2" +FLAG_BRUNEI = "\U0001f1e7\U0001f1f3" +FLAG_BOLIVIA = "\U0001f1e7\U0001f1f4" +FLAG_CARIBBEAN_NETHERLANDS = "\U0001f1e7\U0001f1f6" +FLAG_BRAZIL = "\U0001f1e7\U0001f1f7" +FLAG_BAHAMAS = "\U0001f1e7\U0001f1f8" +FLAG_BHUTAN = "\U0001f1e7\U0001f1f9" +FLAG_BOUVET_ISLAND = "\U0001f1e7\U0001f1fb" +FLAG_BOTSWANA = "\U0001f1e7\U0001f1fc" +FLAG_BELARUS = "\U0001f1e7\U0001f1fe" +FLAG_BELIZE = "\U0001f1e7\U0001f1ff" +FLAG_CANADA = "\U0001f1e8\U0001f1e6" +FLAG_COCOS_KEELING_ISLANDS = "\U0001f1e8\U0001f1e8" +FLAG_CONGO_KINSHASA = "\U0001f1e8\U0001f1e9" +FLAG_CENTRAL_AFRICAN_REPUBLIC = "\U0001f1e8\U0001f1eb" +FLAG_CONGO_BRAZZAVILLE = "\U0001f1e8\U0001f1ec" +FLAG_SWITZERLAND = "\U0001f1e8\U0001f1ed" +FLAG_COTE_D_IVOIRE = "\U0001f1e8\U0001f1ee" +FLAG_COOK_ISLANDS = "\U0001f1e8\U0001f1f0" +FLAG_CHILE = "\U0001f1e8\U0001f1f1" +FLAG_CAMEROON = "\U0001f1e8\U0001f1f2" +FLAG_CHINA = "\U0001f1e8\U0001f1f3" +FLAG_COLOMBIA = "\U0001f1e8\U0001f1f4" +FLAG_CLIPPERTON_ISLAND = "\U0001f1e8\U0001f1f5" +FLAG_COSTA_RICA = "\U0001f1e8\U0001f1f7" +FLAG_CUBA = "\U0001f1e8\U0001f1fa" +FLAG_CAPE_VERDE = "\U0001f1e8\U0001f1fb" +FLAG_CURACAO = "\U0001f1e8\U0001f1fc" +FLAG_CHRISTMAS_ISLAND = "\U0001f1e8\U0001f1fd" +FLAG_CYPRUS = "\U0001f1e8\U0001f1fe" +FLAG_CZECHIA = "\U0001f1e8\U0001f1ff" +FLAG_GERMANY = "\U0001f1e9\U0001f1ea" +FLAG_DIEGO_GARCIA = "\U0001f1e9\U0001f1ec" +FLAG_DJIBOUTI = "\U0001f1e9\U0001f1ef" +FLAG_DENMARK = "\U0001f1e9\U0001f1f0" +FLAG_DOMINICA = "\U0001f1e9\U0001f1f2" +FLAG_DOMINICAN_REPUBLIC = "\U0001f1e9\U0001f1f4" +FLAG_ALGERIA = "\U0001f1e9\U0001f1ff" +FLAG_CEUTA_ANDAMP_MELILLA = "\U0001f1ea\U0001f1e6" +FLAG_ECUADOR = "\U0001f1ea\U0001f1e8" +FLAG_ESTONIA = "\U0001f1ea\U0001f1ea" +FLAG_EGYPT = "\U0001f1ea\U0001f1ec" +FLAG_WESTERN_SAHARA = "\U0001f1ea\U0001f1ed" +FLAG_ERITREA = "\U0001f1ea\U0001f1f7" +FLAG_SPAIN = "\U0001f1ea\U0001f1f8" +FLAG_ETHIOPIA = "\U0001f1ea\U0001f1f9" +FLAG_EUROPEAN_UNION = "\U0001f1ea\U0001f1fa" +FLAG_FINLAND = "\U0001f1eb\U0001f1ee" +FLAG_FIJI = "\U0001f1eb\U0001f1ef" +FLAG_FALKLAND_ISLANDS = "\U0001f1eb\U0001f1f0" +FLAG_MICRONESIA = "\U0001f1eb\U0001f1f2" +FLAG_FAROE_ISLANDS = "\U0001f1eb\U0001f1f4" +FLAG_FRANCE = "\U0001f1eb\U0001f1f7" +FLAG_GABON = "\U0001f1ec\U0001f1e6" +FLAG_UNITED_KINGDOM = "\U0001f1ec\U0001f1e7" +FLAG_GRENADA = "\U0001f1ec\U0001f1e9" +FLAG_GEORGIA = "\U0001f1ec\U0001f1ea" +FLAG_FRENCH_GUIANA = "\U0001f1ec\U0001f1eb" +FLAG_GUERNSEY = "\U0001f1ec\U0001f1ec" +FLAG_GHANA = "\U0001f1ec\U0001f1ed" +FLAG_GIBRALTAR = "\U0001f1ec\U0001f1ee" +FLAG_GREENLAND = "\U0001f1ec\U0001f1f1" +FLAG_GAMBIA = "\U0001f1ec\U0001f1f2" +FLAG_GUINEA = "\U0001f1ec\U0001f1f3" +FLAG_GUADELOUPE = "\U0001f1ec\U0001f1f5" +FLAG_EQUATORIAL_GUINEA = "\U0001f1ec\U0001f1f6" +FLAG_GREECE = "\U0001f1ec\U0001f1f7" +FLAG_SOUTH_GEORGIA_ANDAMP_SOUTH_SANDWICH_ISLANDS = "\U0001f1ec\U0001f1f8" +FLAG_GUATEMALA = "\U0001f1ec\U0001f1f9" +FLAG_GUAM = "\U0001f1ec\U0001f1fa" +FLAG_GUINEA_BISSAU = "\U0001f1ec\U0001f1fc" +FLAG_GUYANA = "\U0001f1ec\U0001f1fe" +FLAG_HONG_KONG_SAR_CHINA = "\U0001f1ed\U0001f1f0" +FLAG_HEARD_ANDAMP_MCDONALD_ISLANDS = "\U0001f1ed\U0001f1f2" +FLAG_HONDURAS = "\U0001f1ed\U0001f1f3" +FLAG_CROATIA = "\U0001f1ed\U0001f1f7" +FLAG_HAITI = "\U0001f1ed\U0001f1f9" +FLAG_HUNGARY = "\U0001f1ed\U0001f1fa" +FLAG_CANARY_ISLANDS = "\U0001f1ee\U0001f1e8" +FLAG_INDONESIA = "\U0001f1ee\U0001f1e9" +FLAG_IRELAND = "\U0001f1ee\U0001f1ea" +FLAG_ISRAEL = "\U0001f1ee\U0001f1f1" +FLAG_ISLE_OF_MAN = "\U0001f1ee\U0001f1f2" +FLAG_INDIA = "\U0001f1ee\U0001f1f3" +FLAG_BRITISH_INDIAN_OCEAN_TERRITORY = "\U0001f1ee\U0001f1f4" +FLAG_IRAQ = "\U0001f1ee\U0001f1f6" +FLAG_IRAN = "\U0001f1ee\U0001f1f7" +FLAG_ICELAND = "\U0001f1ee\U0001f1f8" +FLAG_ITALY = "\U0001f1ee\U0001f1f9" +FLAG_JERSEY = "\U0001f1ef\U0001f1ea" +FLAG_JAMAICA = "\U0001f1ef\U0001f1f2" +FLAG_JORDAN = "\U0001f1ef\U0001f1f4" +FLAG_JAPAN = "\U0001f1ef\U0001f1f5" +FLAG_KENYA = "\U0001f1f0\U0001f1ea" +FLAG_KYRGYZSTAN = "\U0001f1f0\U0001f1ec" +FLAG_CAMBODIA = "\U0001f1f0\U0001f1ed" +FLAG_KIRIBATI = "\U0001f1f0\U0001f1ee" +FLAG_COMOROS = "\U0001f1f0\U0001f1f2" +FLAG_ST_KITTS_ANDAMP_NEVIS = "\U0001f1f0\U0001f1f3" +FLAG_NORTH_KOREA = "\U0001f1f0\U0001f1f5" +FLAG_SOUTH_KOREA = "\U0001f1f0\U0001f1f7" +FLAG_KUWAIT = "\U0001f1f0\U0001f1fc" +FLAG_CAYMAN_ISLANDS = "\U0001f1f0\U0001f1fe" +FLAG_KAZAKHSTAN = "\U0001f1f0\U0001f1ff" +FLAG_LAOS = "\U0001f1f1\U0001f1e6" +FLAG_LEBANON = "\U0001f1f1\U0001f1e7" +FLAG_ST_LUCIA = "\U0001f1f1\U0001f1e8" +FLAG_LIECHTENSTEIN = "\U0001f1f1\U0001f1ee" +FLAG_SRI_LANKA = "\U0001f1f1\U0001f1f0" +FLAG_LIBERIA = "\U0001f1f1\U0001f1f7" +FLAG_LESOTHO = "\U0001f1f1\U0001f1f8" +FLAG_LITHUANIA = "\U0001f1f1\U0001f1f9" +FLAG_LUXEMBOURG = "\U0001f1f1\U0001f1fa" +FLAG_LATVIA = "\U0001f1f1\U0001f1fb" +FLAG_LIBYA = "\U0001f1f1\U0001f1fe" +FLAG_MOROCCO = "\U0001f1f2\U0001f1e6" +FLAG_MONACO = "\U0001f1f2\U0001f1e8" +FLAG_MOLDOVA = "\U0001f1f2\U0001f1e9" +FLAG_MONTENEGRO = "\U0001f1f2\U0001f1ea" +FLAG_ST_MARTIN = "\U0001f1f2\U0001f1eb" +FLAG_MADAGASCAR = "\U0001f1f2\U0001f1ec" +FLAG_MARSHALL_ISLANDS = "\U0001f1f2\U0001f1ed" +FLAG_NORTH_MACEDONIA = "\U0001f1f2\U0001f1f0" +FLAG_MALI = "\U0001f1f2\U0001f1f1" +FLAG_MYANMAR_BURMA = "\U0001f1f2\U0001f1f2" +FLAG_MONGOLIA = "\U0001f1f2\U0001f1f3" +FLAG_MACAO_SAR_CHINA = "\U0001f1f2\U0001f1f4" +FLAG_NORTHERN_MARIANA_ISLANDS = "\U0001f1f2\U0001f1f5" +FLAG_MARTINIQUE = "\U0001f1f2\U0001f1f6" +FLAG_MAURITANIA = "\U0001f1f2\U0001f1f7" +FLAG_MONTSERRAT = "\U0001f1f2\U0001f1f8" +FLAG_MALTA = "\U0001f1f2\U0001f1f9" +FLAG_MAURITIUS = "\U0001f1f2\U0001f1fa" +FLAG_MALDIVES = "\U0001f1f2\U0001f1fb" +FLAG_MALAWI = "\U0001f1f2\U0001f1fc" +FLAG_MEXICO = "\U0001f1f2\U0001f1fd" +FLAG_MALAYSIA = "\U0001f1f2\U0001f1fe" +FLAG_MOZAMBIQUE = "\U0001f1f2\U0001f1ff" +FLAG_NAMIBIA = "\U0001f1f3\U0001f1e6" +FLAG_NEW_CALEDONIA = "\U0001f1f3\U0001f1e8" +FLAG_NIGER = "\U0001f1f3\U0001f1ea" +FLAG_NORFOLK_ISLAND = "\U0001f1f3\U0001f1eb" +FLAG_NIGERIA = "\U0001f1f3\U0001f1ec" +FLAG_NICARAGUA = "\U0001f1f3\U0001f1ee" +FLAG_NETHERLANDS = "\U0001f1f3\U0001f1f1" +FLAG_NORWAY = "\U0001f1f3\U0001f1f4" +FLAG_NEPAL = "\U0001f1f3\U0001f1f5" +FLAG_NAURU = "\U0001f1f3\U0001f1f7" +FLAG_NIUE = "\U0001f1f3\U0001f1fa" +FLAG_NEW_ZEALAND = "\U0001f1f3\U0001f1ff" +FLAG_OMAN = "\U0001f1f4\U0001f1f2" +FLAG_PANAMA = "\U0001f1f5\U0001f1e6" +FLAG_PERU = "\U0001f1f5\U0001f1ea" +FLAG_FRENCH_POLYNESIA = "\U0001f1f5\U0001f1eb" +FLAG_PAPUA_NEW_GUINEA = "\U0001f1f5\U0001f1ec" +FLAG_PHILIPPINES = "\U0001f1f5\U0001f1ed" +FLAG_PAKISTAN = "\U0001f1f5\U0001f1f0" +FLAG_POLAND = "\U0001f1f5\U0001f1f1" +FLAG_ST_PIERRE_ANDAMP_MIQUELON = "\U0001f1f5\U0001f1f2" +FLAG_PITCAIRN_ISLANDS = "\U0001f1f5\U0001f1f3" +FLAG_PUERTO_RICO = "\U0001f1f5\U0001f1f7" +FLAG_PALESTINIAN_TERRITORIES = "\U0001f1f5\U0001f1f8" +FLAG_PORTUGAL = "\U0001f1f5\U0001f1f9" +FLAG_PALAU = "\U0001f1f5\U0001f1fc" +FLAG_PARAGUAY = "\U0001f1f5\U0001f1fe" +FLAG_QATAR = "\U0001f1f6\U0001f1e6" +FLAG_REUNION = "\U0001f1f7\U0001f1ea" +FLAG_ROMANIA = "\U0001f1f7\U0001f1f4" +FLAG_SERBIA = "\U0001f1f7\U0001f1f8" +FLAG_RUSSIA = "\U0001f1f7\U0001f1fa" +FLAG_RWANDA = "\U0001f1f7\U0001f1fc" +FLAG_SAUDI_ARABIA = "\U0001f1f8\U0001f1e6" +FLAG_SOLOMON_ISLANDS = "\U0001f1f8\U0001f1e7" +FLAG_SEYCHELLES = "\U0001f1f8\U0001f1e8" +FLAG_SUDAN = "\U0001f1f8\U0001f1e9" +FLAG_SWEDEN = "\U0001f1f8\U0001f1ea" +FLAG_SINGAPORE = "\U0001f1f8\U0001f1ec" +FLAG_ST_HELENA = "\U0001f1f8\U0001f1ed" +FLAG_SLOVENIA = "\U0001f1f8\U0001f1ee" +FLAG_SVALBARD_ANDAMP_JAN_MAYEN = "\U0001f1f8\U0001f1ef" +FLAG_SLOVAKIA = "\U0001f1f8\U0001f1f0" +FLAG_SIERRA_LEONE = "\U0001f1f8\U0001f1f1" +FLAG_SAN_MARINO = "\U0001f1f8\U0001f1f2" +FLAG_SENEGAL = "\U0001f1f8\U0001f1f3" +FLAG_SOMALIA = "\U0001f1f8\U0001f1f4" +FLAG_SURINAME = "\U0001f1f8\U0001f1f7" +FLAG_SOUTH_SUDAN = "\U0001f1f8\U0001f1f8" +FLAG_SAO_TOME_ANDAMP_PRINCIPE = "\U0001f1f8\U0001f1f9" +FLAG_EL_SALVADOR = "\U0001f1f8\U0001f1fb" +FLAG_SINT_MAARTEN = "\U0001f1f8\U0001f1fd" +FLAG_SYRIA = "\U0001f1f8\U0001f1fe" +FLAG_ESWATINI = "\U0001f1f8\U0001f1ff" +FLAG_TRISTAN_DA_CUNHA = "\U0001f1f9\U0001f1e6" +FLAG_TURKS_ANDAMP_CAICOS_ISLANDS = "\U0001f1f9\U0001f1e8" +FLAG_CHAD = "\U0001f1f9\U0001f1e9" +FLAG_FRENCH_SOUTHERN_TERRITORIES = "\U0001f1f9\U0001f1eb" +FLAG_TOGO = "\U0001f1f9\U0001f1ec" +FLAG_THAILAND = "\U0001f1f9\U0001f1ed" +FLAG_TAJIKISTAN = "\U0001f1f9\U0001f1ef" +FLAG_TOKELAU = "\U0001f1f9\U0001f1f0" +FLAG_TIMOR_LESTE = "\U0001f1f9\U0001f1f1" +FLAG_TURKMENISTAN = "\U0001f1f9\U0001f1f2" +FLAG_TUNISIA = "\U0001f1f9\U0001f1f3" +FLAG_TONGA = "\U0001f1f9\U0001f1f4" +FLAG_TURKEY = "\U0001f1f9\U0001f1f7" +FLAG_TRINIDAD_ANDAMP_TOBAGO = "\U0001f1f9\U0001f1f9" +FLAG_TUVALU = "\U0001f1f9\U0001f1fb" +FLAG_TAIWAN = "\U0001f1f9\U0001f1fc" +FLAG_TANZANIA = "\U0001f1f9\U0001f1ff" +FLAG_UKRAINE = "\U0001f1fa\U0001f1e6" +FLAG_UGANDA = "\U0001f1fa\U0001f1ec" +FLAG_U_S_OUTLYING_ISLANDS = "\U0001f1fa\U0001f1f2" +FLAG_UNITED_NATIONS = "\U0001f1fa\U0001f1f3" +FLAG_UNITED_STATES = "\U0001f1fa\U0001f1f8" +FLAG_URUGUAY = "\U0001f1fa\U0001f1fe" +FLAG_UZBEKISTAN = "\U0001f1fa\U0001f1ff" +FLAG_VATICAN_CITY = "\U0001f1fb\U0001f1e6" +FLAG_ST_VINCENT_ANDAMP_GRENADINES = "\U0001f1fb\U0001f1e8" +FLAG_VENEZUELA = "\U0001f1fb\U0001f1ea" +FLAG_BRITISH_VIRGIN_ISLANDS = "\U0001f1fb\U0001f1ec" +FLAG_U_S_VIRGIN_ISLANDS = "\U0001f1fb\U0001f1ee" +FLAG_VIETNAM = "\U0001f1fb\U0001f1f3" +FLAG_VANUATU = "\U0001f1fb\U0001f1fa" +FLAG_WALLIS_ANDAMP_FUTUNA = "\U0001f1fc\U0001f1eb" +FLAG_SAMOA = "\U0001f1fc\U0001f1f8" +FLAG_KOSOVO = "\U0001f1fd\U0001f1f0" +FLAG_YEMEN = "\U0001f1fe\U0001f1ea" +FLAG_MAYOTTE = "\U0001f1fe\U0001f1f9" +FLAG_SOUTH_AFRICA = "\U0001f1ff\U0001f1e6" +FLAG_ZAMBIA = "\U0001f1ff\U0001f1f2" +FLAG_ZIMBABWE = "\U0001f1ff\U0001f1fc" +FLAG_ENGLAND = "\U0001f3f4\U000e0067\U000e0062\U000e0065\U000e006e\U000e0067\U000e007f" +FLAG_SCOTLAND = "\U0001f3f4\U000e0067\U000e0062\U000e0073\U000e0063\U000e0074\U000e007f" +FLAG_WALES = "\U0001f3f4\U000e0067\U000e0062\U000e0077\U000e006c\U000e0073\U000e007f" +REGIONAL_INDICATOR_SYMBOL_LETTER_A = "\U0001f1e6" +REGIONAL_INDICATOR_SYMBOL_LETTER_B = "\U0001f1e7" +REGIONAL_INDICATOR_SYMBOL_LETTER_C = "\U0001f1e8" +REGIONAL_INDICATOR_SYMBOL_LETTER_D = "\U0001f1e9" +REGIONAL_INDICATOR_SYMBOL_LETTER_E = "\U0001f1ea" +REGIONAL_INDICATOR_SYMBOL_LETTER_F = "\U0001f1eb" +REGIONAL_INDICATOR_SYMBOL_LETTER_G = "\U0001f1ec" +REGIONAL_INDICATOR_SYMBOL_LETTER_H = "\U0001f1ed" +REGIONAL_INDICATOR_SYMBOL_LETTER_I = "\U0001f1ee" +REGIONAL_INDICATOR_SYMBOL_LETTER_J = "\U0001f1ef" +REGIONAL_INDICATOR_SYMBOL_LETTER_K = "\U0001f1f0" +REGIONAL_INDICATOR_SYMBOL_LETTER_L = "\U0001f1f1" +REGIONAL_INDICATOR_SYMBOL_LETTER_M = "\U0001f1f2" +REGIONAL_INDICATOR_SYMBOL_LETTER_N = "\U0001f1f3" +REGIONAL_INDICATOR_SYMBOL_LETTER_O = "\U0001f1f4" +REGIONAL_INDICATOR_SYMBOL_LETTER_P = "\U0001f1f5" +REGIONAL_INDICATOR_SYMBOL_LETTER_Q = "\U0001f1f6" +REGIONAL_INDICATOR_SYMBOL_LETTER_R = "\U0001f1f7" +REGIONAL_INDICATOR_SYMBOL_LETTER_S = "\U0001f1f8" +REGIONAL_INDICATOR_SYMBOL_LETTER_T = "\U0001f1f9" +REGIONAL_INDICATOR_SYMBOL_LETTER_U = "\U0001f1fa" +REGIONAL_INDICATOR_SYMBOL_LETTER_V = "\U0001f1fb" +REGIONAL_INDICATOR_SYMBOL_LETTER_W = "\U0001f1fc" +REGIONAL_INDICATOR_SYMBOL_LETTER_X = "\U0001f1fd" +REGIONAL_INDICATOR_SYMBOL_LETTER_Y = "\U0001f1fe" +REGIONAL_INDICATOR_SYMBOL_LETTER_Z = "\U0001f1ff" +TAG_RIGHT_CURLY_BRACKET = "\U000e007d" +DIGIT_FIVE = "5\ufe0f" +TAG_LATIN_CAPITAL_LETTER_U = "\U000e0055" +TAG_LATIN_CAPITAL_LETTER_Q = "\U000e0051" +TAG_LATIN_CAPITAL_LETTER_K = "\U000e004b" +COMBINING_ENCLOSING_KEYCAP = "\u20e3" +TAG_LATIN_CAPITAL_LETTER_C = "\U000e0043" +TAG_ASTERISK = "\U000e002a" +TAG_FULL_STOP = "\U000e002e" +TAG_CIRCUMFLEX_ACCENT = "\U000e005e" +DIGIT_ONE = "1\ufe0f" +TAG_COMMA = "\U000e002c" +DIGIT_ZERO = "0\ufe0f" +TAG_EQUALS_SIGN = "\U000e003d" +TAG_LATIN_CAPITAL_LETTER_O = "\U000e004f" +TAG_COMMERCIAL_AT = "\U000e0040" +DIGIT_EIGHT = "8\ufe0f" +TAG_NUMBER_SIGN = "\U000e0023" +TAG_LATIN_CAPITAL_LETTER_T = "\U000e0054" +TAG_LATIN_CAPITAL_LETTER_N = "\U000e004e" +DIGIT_SIX = "6\ufe0f" +TAG_PERCENT_SIGN = "\U000e0025" +VARIATION_SELECTOR_16 = "\ufe0f" +TAG_LATIN_CAPITAL_LETTER_W = "\U000e0057" +TAG_DOLLAR_SIGN = "\U000e0024" +TAG_LOW_LINE = "\U000e005f" +TAG_DIGIT_EIGHT = "\U000e0038" +TAG_LATIN_CAPITAL_LETTER_M = "\U000e004d" +TAG_LATIN_CAPITAL_LETTER_A = "\U000e0041" +TAG_REVERSE_SOLIDUS = "\U000e005c" +TAG_SOLIDUS = "\U000e002f" +TAG_LATIN_CAPITAL_LETTER_H = "\U000e0048" +TAG_DIGIT_NINE = "\U000e0039" +TAG_LEFT_CURLY_BRACKET = "\U000e007b" +TAG_LATIN_CAPITAL_LETTER_E = "\U000e0045" +TAG_LATIN_SMALL_LETTER_W = "\U000e0077" +TAG_DIGIT_ZERO = "\U000e0030" +TAG_LATIN_CAPITAL_LETTER_B = "\U000e0042" +TAG_LATIN_CAPITAL_LETTER_F = "\U000e0046" +TAG_LATIN_CAPITAL_LETTER_Y = "\U000e0059" +TAG_TILDE = "\U000e007e" +TAG_LATIN_SMALL_LETTER_P = "\U000e0070" +TAG_LATIN_CAPITAL_LETTER_Z = "\U000e005a" +TAG_GREATER_THAN_SIGN = "\U000e003e" +TAG_LATIN_SMALL_LETTER_S = "\U000e0073" +TAG_LATIN_SMALL_LETTER_G = "\U000e0067" +TAG_APOSTROPHE = "\U000e0027" +TAG_RIGHT_PARENTHESIS = "\U000e0029" +TAG_DIGIT_THREE = "\U000e0033" +TAG_LEFT_PARENTHESIS = "\U000e0028" +TAG_DIGIT_SEVEN = "\U000e0037" +TAG_LATIN_SMALL_LETTER_O = "\U000e006f" +TAG_DIGIT_SIX = "\U000e0036" +TAG_DIGIT_TWO = "\U000e0032" +TAG_LATIN_SMALL_LETTER_F = "\U000e0066" +TAG_LATIN_SMALL_LETTER_K = "\U000e006b" +TAG_LATIN_SMALL_LETTER_Y = "\U000e0079" +TAG_SPACE = "\U000e0020" +TAG_LATIN_SMALL_LETTER_I = "\U000e0069" +DIGIT_TWO = "2\ufe0f" +TAG_DIGIT_ONE = "\U000e0031" +TAG_RIGHT_SQUARE_BRACKET = "\U000e005d" +TAG_LATIN_SMALL_LETTER_R = "\U000e0072" +HASH_SIGN = "#\ufe0f" +TAG_SEMICOLON = "\U000e003b" +TAG_LATIN_CAPITAL_LETTER_L = "\U000e004c" +TAG_HYPHEN_MINUS = "\U000e002d" +ASTERISK = "*\ufe0f" +TAG_LATIN_SMALL_LETTER_A = "\U000e0061" +TAG_EXCLAMATION_MARK = "\U000e0021" +TAG_LATIN_CAPITAL_LETTER_V = "\U000e0056" +TAG_LATIN_SMALL_LETTER_C = "\U000e0063" +TAG_GRAVE_ACCENT = "\U000e0060" +ZERO_WIDTH_JOINER = "\u200d" +TAG_LATIN_CAPITAL_LETTER_G = "\U000e0047" +DIGIT_NINE = "9\ufe0f" +TAG_VERTICAL_LINE = "\U000e007c" +TAG_LATIN_SMALL_LETTER_Z = "\U000e007a" +TAG_LATIN_CAPITAL_LETTER_X = "\U000e0058" +TAG_LATIN_SMALL_LETTER_J = "\U000e006a" +TAG_LATIN_CAPITAL_LETTER_P = "\U000e0050" +TAG_AMPERSAND = "\U000e0026" +TAG_LATIN_SMALL_LETTER_L = "\U000e006c" +TAG_LATIN_SMALL_LETTER_X = "\U000e0078" +DIGIT_SEVEN = "7\ufe0f" +TAG_LATIN_CAPITAL_LETTER_J = "\U000e004a" +TAG_LATIN_SMALL_LETTER_T = "\U000e0074" +TAG_QUESTION_MARK = "\U000e003f" +TAG_LATIN_SMALL_LETTER_B = "\U000e0062" +TAG_LEFT_SQUARE_BRACKET = "\U000e005b" +TAG_LATIN_SMALL_LETTER_D = "\U000e0064" +TAG_LATIN_SMALL_LETTER_E = "\U000e0065" +TAG_LATIN_SMALL_LETTER_M = "\U000e006d" +TAG_LESS_THAN_SIGN = "\U000e003c" +TAG_DIGIT_FIVE = "\U000e0035" +TAG_LATIN_CAPITAL_LETTER_D = "\U000e0044" +TAG_LATIN_SMALL_LETTER_N = "\U000e006e" +TAG_PLUS_SIGN = "\U000e002b" +TAG_COLON = "\U000e003a" +DIGIT_THREE = "3\ufe0f" +TAG_LATIN_SMALL_LETTER_Q = "\U000e0071" +TAG_LATIN_CAPITAL_LETTER_R = "\U000e0052" +TAG_LATIN_CAPITAL_LETTER_S = "\U000e0053" +DIGIT_FOUR = "4\ufe0f" +TAG_LATIN_CAPITAL_LETTER_I = "\U000e0049" +TAG_QUOTATION_MARK = "\U000e0022" +CANCEL_TAG = "\U000e007f" +TAG_LATIN_SMALL_LETTER_V = "\U000e0076" +TAG_LATIN_SMALL_LETTER_H = "\U000e0068" +TAG_LATIN_SMALL_LETTER_U = "\U000e0075" +TAG_DIGIT_FOUR = "\U000e0034" diff --git a/pyrogram/enums/__init__.py b/pyrogram/enums/__init__.py new file mode 100644 index 00000000000..d19c7044ff4 --- /dev/null +++ b/pyrogram/enums/__init__.py @@ -0,0 +1,49 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 .chat_action import ChatAction +from .chat_event_action import ChatEventAction +from .chat_member_status import ChatMemberStatus +from .chat_members_filter import ChatMembersFilter +from .chat_type import ChatType +from .message_entity_type import MessageEntityType +from .message_media_type import MessageMediaType +from .message_service_type import MessageServiceType +from .messages_filter import MessagesFilter +from .next_code_type import NextCodeType +from .parse_mode import ParseMode +from .poll_type import PollType +from .sent_code_type import SentCodeType +from .user_status import UserStatus + +__all__ = [ + 'ChatAction', + 'ChatEventAction', + 'ChatMemberStatus', + 'ChatMembersFilter', + 'ChatType', + 'MessageEntityType', + 'MessageMediaType', + 'MessageServiceType', + 'MessagesFilter', + 'NextCodeType', + 'ParseMode', + 'PollType', + 'SentCodeType', + 'UserStatus' +] diff --git a/pyrogram/enums/auto_name.py b/pyrogram/enums/auto_name.py new file mode 100644 index 00000000000..b43fa74180a --- /dev/null +++ b/pyrogram/enums/auto_name.py @@ -0,0 +1,27 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 + + +class AutoName(Enum): + def _generate_next_value_(self, *args): + return self.lower() + + def __repr__(self): + return f"pyrogram.enums.{self}" diff --git a/pyrogram/enums/chat_action.py b/pyrogram/enums/chat_action.py new file mode 100644 index 00000000000..167937e0026 --- /dev/null +++ b/pyrogram/enums/chat_action.py @@ -0,0 +1,72 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 pyrogram import raw +from .auto_name import AutoName + + +class ChatAction(AutoName): + """Chat action enumeration used in :obj:`~pyrogram.types.ChatEvent`.""" + + TYPING = raw.types.SendMessageTypingAction + "Typing text message" + + UPLOAD_PHOTO = raw.types.SendMessageUploadPhotoAction + "Uploading photo" + + RECORD_VIDEO = raw.types.SendMessageRecordVideoAction + "Recording video" + + UPLOAD_VIDEO = raw.types.SendMessageUploadVideoAction + "Uploading video" + + RECORD_AUDIO = raw.types.SendMessageRecordAudioAction + "Recording audio" + + UPLOAD_AUDIO = raw.types.SendMessageUploadAudioAction + "Uploading audio" + + UPLOAD_DOCUMENT = raw.types.SendMessageUploadDocumentAction + "Uploading document" + + FIND_LOCATION = raw.types.SendMessageGeoLocationAction + "Finding location" + + RECORD_VIDEO_NOTE = raw.types.SendMessageRecordRoundAction + "Recording video note" + + UPLOAD_VIDEO_NOTE = raw.types.SendMessageUploadRoundAction + "Uploading video note" + + PLAYING = raw.types.SendMessageGamePlayAction + "Playing game" + + CHOOSE_CONTACT = raw.types.SendMessageChooseContactAction + "Choosing contact" + + SPEAKING = raw.types.SpeakingInGroupCallAction + "Speaking in group call" + + IMPORT_HISTORY = raw.types.SendMessageHistoryImportAction + "Importing history" + + CHOOSE_STICKER = raw.types.SendMessageChooseStickerAction + "Choosing sticker" + + CANCEL = raw.types.SendMessageCancelAction + "Cancel ongoing chat action" diff --git a/pyrogram/enums/chat_event_action.py b/pyrogram/enums/chat_event_action.py new file mode 100644 index 00000000000..7a5954720f1 --- /dev/null +++ b/pyrogram/enums/chat_event_action.py @@ -0,0 +1,127 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 auto + +from .auto_name import AutoName + + +class ChatEventAction(AutoName): + """Chat event action enumeration used in :meth:`~pyrogram.Client.get_chat_event_log`.""" + + DESCRIPTION_CHANGED = auto() + "The chat description has been changed (see ``old_description`` and ``new_description``)" + + HISTORY_TTL_CHANGED = auto() + "The history time-to-live has been changed (see ``old_history_ttl`` and ``new_history_ttl``)" + + LINKED_CHAT_CHANGED = auto() + "The linked chat has been changed (see ``old_linked_chat`` and ``new_linked_chat``)" + + # LOCATION_CHANGED = auto() + "" + + PHOTO_CHANGED = auto() + "The chat photo has been changed (see ``old_photo`` and ``new_photo``)" + + # STICKER_SET_CHANGED = auto() + "" + + TITLE_CHANGED = auto() + "the chat title has been changed (see ``old_title`` and ``new_title``)" + + USERNAME_CHANGED = auto() + "the chat username has been changed (see ``old_username`` and ``new_username``)" + + CHAT_PERMISSIONS_CHANGED = auto() + "the default chat permissions has been changed (see ``old_chat_permissions`` and ``new_chat_permissions``)" + + MESSAGE_DELETED = auto() + "a message has been deleted (see ``deleted_message``)" + + # VOICE_CHAT_DISCARDED = auto() + "" + + MESSAGE_EDITED = auto() + "a message has been edited (see ``old_message`` and ``new_message``)" + + INVITE_LINK_EDITED = auto() + "An invite link has been edited (see ``old_invite_link`` and ``new_invite`` link)" + + INVITE_LINK_REVOKED = auto() + "An invite link has been revoked (see ``revoked_invite_link``)" + + INVITE_LINK_DELETED = auto() + "An invite link has been deleted (see ``deleted_invite_link``)" + + MEMBER_INVITED = auto() + "a member has been invited by someone (see ``invited_member``)" + + MEMBER_JOINED = auto() + "a member joined by themselves. (see ``user``)" + + # MEMBER_JOINED_BY_LINK = auto() + "" + + MEMBER_LEFT = auto() + "a member left by themselves. (see ``user``)" + + # MEMBER_MUTED = auto() + "" + + ADMINISTRATOR_PRIVILEGES_CHANGED = auto() + "a chat member has been promoted/demoted or their administrator privileges has changed (see ``old_administrator_privileges`` and ``new_administrator_privileges``)" + + MEMBER_PERMISSIONS_CHANGED = auto() + "a chat member has been restricted/unrestricted or banned/unbanned, or their permissions has changed (see ``old_member_permissions`` and ``new_member_permissions``)" + + # MEMBER_UNMUTED = auto() + "" + + # MEMBER_VOLUME_CHANGED = auto() + "" + + # VIDEO_CHAT_STARTED = auto() + "" + + POLL_STOPPED = auto() + "a poll has been stopped (see ``stopped_poll``)" + + # VOICE_CHAT_SETTINGS_CHANGED = auto() + "" + + INVITES_ENABLED = auto() + "the chat invitation has been enabled or disabled (see ``invites_enabled``)" + + HISTORY_HIDDEN = auto() + "the chat history has been hidden or unhidden (see ``history_hidden``)" + + SIGNATURES_ENABLED = auto() + "the message signatures have been enabled or disabled (see ``signatures_enabled``)" + + SLOW_MODE_CHANGED = auto() + "the slow mode has been changes (see ``old_slow_mode`` and ``new_slow_mode``)" + + MESSAGE_PINNED = auto() + "a message has been pinned (see ``pinned_message``)" + + MESSAGE_UNPINNED = auto() + "a message has been unpinned (see ``unpinned_message``)" + + UNKNOWN = auto() + "Unknown chat event action" diff --git a/pyrogram/enums/chat_member_status.py b/pyrogram/enums/chat_member_status.py new file mode 100644 index 00000000000..7fa9e8eca8f --- /dev/null +++ b/pyrogram/enums/chat_member_status.py @@ -0,0 +1,43 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 auto + +from .auto_name import AutoName + + +class ChatMemberStatus(AutoName): + """Chat member status enumeration used in :obj:`~pyrogram.types.ChatMember`.""" + + OWNER = auto() + "Chat owner" + + ADMINISTRATOR = auto() + "Chat administrator" + + MEMBER = auto() + "Chat member" + + RESTRICTED = auto() + "Restricted chat member" + + LEFT = auto() + "Left chat member" + + BANNED = auto() + "Banned chat member" diff --git a/pyrogram/enums/chat_members_filter.py b/pyrogram/enums/chat_members_filter.py new file mode 100644 index 00000000000..bf0e7ef3584 --- /dev/null +++ b/pyrogram/enums/chat_members_filter.py @@ -0,0 +1,42 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 pyrogram import raw +from .auto_name import AutoName + + +class ChatMembersFilter(AutoName): + """Chat members filter enumeration used in :meth:`~pyrogram.Client.get_chat_members`""" + + SEARCH = raw.types.ChannelParticipantsSearch + "Search for members" + + BANNED = raw.types.ChannelParticipantsKicked + "Banned members" + + RESTRICTED = raw.types.ChannelParticipantsBanned + "Restricted members" + + BOTS = raw.types.ChannelParticipantsBots + "Bots" + + RECENT = raw.types.ChannelParticipantsRecent + "Recently active members" + + ADMINISTRATORS = raw.types.ChannelParticipantsAdmins + "Administrators" diff --git a/pyrogram/enums/chat_type.py b/pyrogram/enums/chat_type.py new file mode 100644 index 00000000000..5750e16d7ca --- /dev/null +++ b/pyrogram/enums/chat_type.py @@ -0,0 +1,40 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 auto + +from .auto_name import AutoName + + +class ChatType(AutoName): + """Chat type enumeration used in :obj:`~pyrogram.types.Chat`.""" + + PRIVATE = auto() + "Chat is a private chat with a user" + + BOT = auto() + "Chat is a private chat with a bot" + + GROUP = auto() + "Chat is a basic group" + + SUPERGROUP = auto() + "Chat is a supergroup" + + CHANNEL = auto() + "Chat is a channel" diff --git a/pyrogram/enums/message_entity_type.py b/pyrogram/enums/message_entity_type.py new file mode 100644 index 00000000000..4db75f93f75 --- /dev/null +++ b/pyrogram/enums/message_entity_type.py @@ -0,0 +1,84 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 pyrogram import raw +from .auto_name import AutoName + + +class MessageEntityType(AutoName): + """Message entity type enumeration used in :obj:`~pyrogram.types.MessageEntity`.""" + + MENTION = raw.types.MessageEntityMention + "``@username``" + + HASHTAG = raw.types.MessageEntityHashtag + "``#hashtag``" + + CASHTAG = raw.types.MessageEntityCashtag + "``$USD``" + + BOT_COMMAND = raw.types.MessageEntityBotCommand + "``/start@pyrogrambot``" + + URL = raw.types.MessageEntityUrl + "``https://pyrogram.org`` (see ``url``)" + + EMAIL = raw.types.MessageEntityEmail + "``do-not-reply@pyrogram.org``" + + PHONE_NUMBER = raw.types.MessageEntityPhone + "``+1-123-456-7890``" + + BOLD = raw.types.MessageEntityBold + "Bold text" + + ITALIC = raw.types.MessageEntityItalic + "Italic text" + + UNDERLINE = raw.types.MessageEntityUnderline + "Underlined text" + + STRIKETHROUGH = raw.types.MessageEntityStrike + "Strikethrough text" + + SPOILER = raw.types.MessageEntitySpoiler + "Spoiler text" + + CODE = raw.types.MessageEntityCode + "Monowidth string" + + PRE = raw.types.MessageEntityPre + "Monowidth block (see ``language``)" + + BLOCKQUOTE = raw.types.MessageEntityBlockquote + "Blockquote text" + + TEXT_LINK = raw.types.MessageEntityTextUrl + "For clickable text URLs" + + TEXT_MENTION = raw.types.MessageEntityMentionName + "for users without usernames (see ``user``)" + + BANK_CARD = raw.types.MessageEntityBankCard + "Bank card text" + + CUSTOM_EMOJI = raw.types.MessageEntityCustomEmoji + "Custom emoji" + + UNKNOWN = raw.types.MessageEntityUnknown + "Unknown message entity type" diff --git a/pyrogram/enums/message_media_type.py b/pyrogram/enums/message_media_type.py new file mode 100644 index 00000000000..5887811405d --- /dev/null +++ b/pyrogram/enums/message_media_type.py @@ -0,0 +1,70 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 auto + +from .auto_name import AutoName + + +class MessageMediaType(AutoName): + """Message media type enumeration used in :obj:`~pyrogram.types.Message`.""" + + AUDIO = auto() + "Audio media" + + DOCUMENT = auto() + "Document media" + + PHOTO = auto() + "Photo media" + + STICKER = auto() + "Sticker media" + + VIDEO = auto() + "Video media" + + ANIMATION = auto() + "Animation media" + + VOICE = auto() + "Voice media" + + VIDEO_NOTE = auto() + "Video note media" + + CONTACT = auto() + "Contact media" + + LOCATION = auto() + "Location media" + + VENUE = auto() + "Venue media" + + POLL = auto() + "Poll media" + + WEB_PAGE = auto() + "Web page media" + + DICE = auto() + "Dice media" + + GAME = auto() + "Game media" diff --git a/pyrogram/enums/message_service_type.py b/pyrogram/enums/message_service_type.py new file mode 100644 index 00000000000..8a60e29e6e2 --- /dev/null +++ b/pyrogram/enums/message_service_type.py @@ -0,0 +1,73 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 auto + +from .auto_name import AutoName + + +class MessageServiceType(AutoName): + """Message service type enumeration used in :obj:`~pyrogram.types.Message`.""" + + NEW_CHAT_MEMBERS = auto() + "New members join" + + LEFT_CHAT_MEMBERS = auto() + "Left chat members" + + NEW_CHAT_TITLE = auto() + "New chat title" + + NEW_CHAT_PHOTO = auto() + "New chat photo" + + DELETE_CHAT_PHOTO = auto() + "Deleted chat photo" + + GROUP_CHAT_CREATED = auto() + "Group chat created" + + CHANNEL_CHAT_CREATED = auto() + "Channel chat created" + + MIGRATE_TO_CHAT_ID = auto() + "Migrated to chat id" + + MIGRATE_FROM_CHAT_ID = auto() + "Migrated from chat id" + + PINNED_MESSAGE = auto() + "Pinned message" + + GAME_HIGH_SCORE = auto() + "Game high score" + + VIDEO_CHAT_STARTED = auto() + "Video chat started" + + VIDEO_CHAT_ENDED = auto() + "Video chat ended" + + VIDEO_CHAT_SCHEDULED = auto() + "Video chat scheduled" + + VIDEO_CHAT_MEMBERS_INVITED = auto() + "Video chat members invited" + + WEB_APP_DATA = auto() + "Web app data" diff --git a/pyrogram/enums/messages_filter.py b/pyrogram/enums/messages_filter.py new file mode 100644 index 00000000000..67bfac63ee4 --- /dev/null +++ b/pyrogram/enums/messages_filter.py @@ -0,0 +1,75 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 pyrogram import raw +from .auto_name import AutoName + + +class MessagesFilter(AutoName): + """Messages filter enumeration used in :meth:`~pyrogram.Client.search_messages` and :meth:`~pyrogram.Client.search_global`""" + + EMPTY = raw.types.InputMessagesFilterEmpty + "Empty filter (any kind of messages)" + + PHOTO = raw.types.InputMessagesFilterPhotos + "Photo messages" + + VIDEO = raw.types.InputMessagesFilterVideo + "Video messages" + + PHOTO_VIDEO = raw.types.InputMessagesFilterPhotoVideo + "Photo and video messages" + + DOCUMENT = raw.types.InputMessagesFilterDocument + "Document messages" + + URL = raw.types.InputMessagesFilterUrl + "Messages containing URLs" + + ANIMATION = raw.types.InputMessagesFilterGif + "Animation messages" + + VOICE_NOTE = raw.types.InputMessagesFilterVoice + "Voice note messages" + + VIDEO_NOTE = raw.types.InputMessagesFilterRoundVideo + "Video note messages" + + AUDIO_VIDEO_NOTE = raw.types.InputMessagesFilterRoundVideo + "Audio and video note messages" + + AUDIO = raw.types.InputMessagesFilterMusic + "Audio messages (music)" + + CHAT_PHOTO = raw.types.InputMessagesFilterChatPhotos + "Chat photo messages" + + PHONE_CALL = raw.types.InputMessagesFilterPhoneCalls + "Phone call messages" + + MENTION = raw.types.InputMessagesFilterMyMentions + "Messages containing mentions" + + LOCATION = raw.types.InputMessagesFilterGeo + "Location messages" + + CONTACT = raw.types.InputMessagesFilterContacts + "Contact messages" + + PINNED = raw.types.InputMessagesFilterPinned + "Pinned messages" diff --git a/pyrogram/enums/next_code_type.py b/pyrogram/enums/next_code_type.py new file mode 100644 index 00000000000..7b3137a7ad3 --- /dev/null +++ b/pyrogram/enums/next_code_type.py @@ -0,0 +1,39 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 pyrogram import raw +from .auto_name import AutoName + + +class NextCodeType(AutoName): + """Next code type enumeration used in :obj:`~pyrogram.types.SentCode`.""" + + CALL = raw.types.auth.CodeTypeCall + "The code will be sent via a phone call. A synthesized voice will tell the user which verification code to input." + + FLASH_CALL = raw.types.auth.CodeTypeFlashCall + "The code will be sent via a flash phone call, that will be closed immediately." + + MISSED_CALL = raw.types.auth.CodeTypeMissedCall + "Missed call." + + SMS = raw.types.auth.CodeTypeSms + "The code was sent via SMS." + + FRAGMENT_SMS = raw.types.auth.CodeTypeFragmentSms + "The code was sent via Fragment SMS." diff --git a/pyrogram/enums/parse_mode.py b/pyrogram/enums/parse_mode.py new file mode 100644 index 00000000000..26dc165a259 --- /dev/null +++ b/pyrogram/enums/parse_mode.py @@ -0,0 +1,37 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 auto + +from .auto_name import AutoName + + +class ParseMode(AutoName): + """Parse mode enumeration used in various places to set a specific parse mode""" + + DEFAULT = auto() + "Default mode. Markdown and HTML combined" + + MARKDOWN = auto() + "Markdown only mode" + + HTML = auto() + "HTML only mode" + + DISABLED = auto() + "Disabled mode" diff --git a/pyrogram/enums/poll_type.py b/pyrogram/enums/poll_type.py new file mode 100644 index 00000000000..384592dee34 --- /dev/null +++ b/pyrogram/enums/poll_type.py @@ -0,0 +1,31 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 auto + +from .auto_name import AutoName + + +class PollType(AutoName): + """Poll type enumeration used in :obj:`~pyrogram.types.Poll`.""" + + QUIZ = auto() + "Quiz poll" + + REGULAR = auto() + "Regular poll" diff --git a/pyrogram/enums/sent_code_type.py b/pyrogram/enums/sent_code_type.py new file mode 100644 index 00000000000..474ed6b0f33 --- /dev/null +++ b/pyrogram/enums/sent_code_type.py @@ -0,0 +1,45 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 pyrogram import raw +from .auto_name import AutoName + + +class SentCodeType(AutoName): + """Sent code type enumeration used in :obj:`~pyrogram.types.SentCode`.""" + + APP = raw.types.auth.SentCodeTypeApp + "The code was sent through the telegram app." + + CALL = raw.types.auth.SentCodeTypeCall + "The code will be sent via a phone call. A synthesized voice will tell the user which verification code to input." + + FLASH_CALL = raw.types.auth.SentCodeTypeFlashCall + "The code will be sent via a flash phone call, that will be closed immediately." + + MISSED_CALL = raw.types.auth.SentCodeTypeMissedCall + "Missed call." + + SMS = raw.types.auth.SentCodeTypeSms + "The code was sent via SMS." + + FRAGMENT_SMS = raw.types.auth.SentCodeTypeFragmentSms + "The code was sent via Fragment SMS." + + EMAIL_CODE = raw.types.auth.SentCodeTypeEmailCode + "The code was sent via email." diff --git a/pyrogram/enums/user_status.py b/pyrogram/enums/user_status.py new file mode 100644 index 00000000000..c7c25234cb8 --- /dev/null +++ b/pyrogram/enums/user_status.py @@ -0,0 +1,43 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 auto + +from .auto_name import AutoName + + +class UserStatus(AutoName): + """User status enumeration used in :obj:`~pyrogram.types.User`.""" + + ONLINE = auto() + """User is online""" + + OFFLINE = auto() + """User is offline""" + + RECENTLY = auto() + """User was seen recently""" + + LAST_WEEK = auto() + """User was seen last week""" + + LAST_MONTH = auto() + """User was seen last month""" + + LONG_AGO = auto() + """User was seen long ago""" diff --git a/pyrogram/errors/__init__.py b/pyrogram/errors/__init__.py index a34d70785c0..aa3a042c540 100644 --- a/pyrogram/errors/__init__.py +++ b/pyrogram/errors/__init__.py @@ -1,20 +1,65 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2019 Dan Tès +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan # -# This file is part of Pyrogram. +# 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 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. +# 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 . +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . from .exceptions import * from .rpc_error import UnknownError + + +class BadMsgNotification(Exception): + descriptions = { + 16: "The msg_id is too low, the client time has to be synchronized.", + 17: "The msg_id is too high, the client time has to be synchronized.", + 18: "Incorrect two lower order of the msg_id bits, the server expects the client message " + "msg_id to be divisible by 4.", + 19: "The container msg_id is the same as the msg_id of a previously received message.", + 20: "The message is too old, it cannot be verified by the server.", + 32: "The msg_seqno is too low.", + 33: "The msg_seqno is too high.", + 34: "An even msg_seqno was expected, but an odd one was received.", + 35: "An odd msg_seqno was expected, but an even one was received.", + 48: "Incorrect server salt.", + 64: "Invalid container." + } + + def __init__(self, code): + description = self.descriptions.get(code, "Unknown error code") + super().__init__(f"[{code}] {description}") + + +class SecurityError(Exception): + """Generic security error.""" + + @classmethod + def check(cls, cond: bool, msg: str): + """Raises this exception if the condition is false""" + if not cond: + raise cls(f"Check failed: {msg}") + + +class SecurityCheckMismatch(SecurityError): + """Raised when a security check mismatch occurs.""" + + def __init__(self, msg: str = None): + super().__init__("A security check mismatch has occurred." if msg is None else msg) + + +class CDNFileHashMismatch(SecurityError): + """Raised when a CDN file hash mismatch occurs.""" + + def __init__(self, msg: str = None): + super().__init__("A CDN file hash mismatch has occurred." if msg is None else msg) diff --git a/pyrogram/errors/rpc_error.py b/pyrogram/errors/rpc_error.py index c1799f50398..63375f199e9 100644 --- a/pyrogram/errors/rpc_error.py +++ b/pyrogram/errors/rpc_error.py @@ -1,81 +1,103 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2019 Dan Tès +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan # -# This file is part of Pyrogram. +# 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 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. +# 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 . +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . import re +from datetime import datetime from importlib import import_module +from typing import Type, Union -from pyrogram.api.types import RpcError as RawRPCError +from pyrogram import raw +from pyrogram.raw.core import TLObject from .exceptions.all import exceptions class RPCError(Exception): - """This is the base exception class for all Telegram API related errors. - For a finer grained control, see the specific errors below. - """ ID = None CODE = None NAME = None - MESSAGE = None + MESSAGE = "{value}" - def __init__(self, x: int or RawRPCError = None, query_type: type = None): - super().__init__("[{} {}]: {}".format( + def __init__( + self, + value: Union[int, str, raw.types.RpcError] = None, + rpc_name: str = None, + is_unknown: bool = False, + is_signed: bool = False + ): + super().__init__("Telegram says: [{}{} {}] - {} {}".format( + "-" if is_signed else "", self.CODE, self.ID or self.NAME, - str(self) or self.MESSAGE.format(x=x) + self.MESSAGE.format(value=value), + f'(caused by "{rpc_name}")' if rpc_name else "" )) try: - self.x = int(x) + self.value = int(value) except (ValueError, TypeError): - self.x = x + self.value = value - # TODO: Proper log unknown errors - if self.CODE == 520: + if is_unknown: with open("unknown_errors.txt", "a", encoding="utf-8") as f: - f.write("{}\t{}\t{}\n".format(x.error_code, x.error_message, query_type)) + f.write(f"{datetime.now()}\t{value}\t{rpc_name}\n") @staticmethod - def raise_it(rpc_error: RawRPCError, query_type: type): - code = rpc_error.error_code + def raise_it(rpc_error: "raw.types.RpcError", rpc_type: Type[TLObject]): + error_code = rpc_error.error_code + is_signed = error_code < 0 + error_message = rpc_error.error_message + rpc_name = ".".join(rpc_type.QUALNAME.split(".")[1:]) - if code not in exceptions: - raise UnknownError(x=rpc_error, query_type=query_type) + if is_signed: + error_code = -error_code - message = rpc_error.error_message - id = re.sub(r"_\d+", "_X", message) + if error_code not in exceptions: + raise UnknownError( + value=f"[{error_code} {error_message}]", + rpc_name=rpc_name, + is_unknown=True, + is_signed=is_signed + ) - if id not in exceptions[code]: - raise UnknownError(x=rpc_error, query_type=query_type) + error_id = re.sub(r"_\d+", "_X", error_message) - x = re.search(r"_(\d+)", message) - x = x.group(1) if x is not None else x + if error_id not in exceptions[error_code]: + raise getattr( + import_module("pyrogram.errors"), + exceptions[error_code]["_"] + )(value=f"[{error_code} {error_message}]", + rpc_name=rpc_name, + is_unknown=True, + is_signed=is_signed) + + value = re.search(r"_(\d+)", error_message) + value = value.group(1) if value is not None else value raise getattr( import_module("pyrogram.errors"), - exceptions[code][id] - )(x=x) + exceptions[error_code][error_id] + )(value=value, + rpc_name=rpc_name, + is_unknown=False, + is_signed=is_signed) class UnknownError(RPCError): - """This object represents an Unknown Error, that is, an error which - Pyrogram does not know anything about, yet. - """ CODE = 520 """:obj:`int`: Error code""" NAME = "Unknown error" - MESSAGE = "{x}" diff --git a/pyrogram/file_id.py b/pyrogram/file_id.py new file mode 100644 index 00000000000..1a54a49d614 --- /dev/null +++ b/pyrogram/file_id.py @@ -0,0 +1,481 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import base64 +import logging +import struct +from enum import IntEnum +from io import BytesIO +from typing import List + +from pyrogram.raw.core import Bytes, String + +log = logging.getLogger(__name__) + + +def b64_encode(s: bytes) -> str: + """Encode bytes into a URL-safe Base64 string without padding + + Parameters: + s (``bytes``): + Bytes to encode + + Returns: + ``str``: The encoded bytes + """ + return base64.urlsafe_b64encode(s).decode().strip("=") + + +def b64_decode(s: str) -> bytes: + """Decode a URL-safe Base64 string without padding to bytes + + Parameters: + s (``str``): + String to decode + + Returns: + ``bytes``: The decoded string + """ + return base64.urlsafe_b64decode(s + "=" * (-len(s) % 4)) + + +def rle_encode(s: bytes) -> bytes: + """Zero-value RLE encoder + + Parameters: + s (``bytes``): + Bytes to encode + + Returns: + ``bytes``: The encoded bytes + """ + r: List[int] = [] + n: int = 0 + + for b in s: + if not b: + n += 1 + else: + if n: + r.extend((0, n)) + n = 0 + + r.append(b) + + if n: + r.extend((0, n)) + + return bytes(r) + + +def rle_decode(s: bytes) -> bytes: + """Zero-value RLE decoder + + Parameters: + s (``bytes``): + Bytes to decode + + Returns: + ``bytes``: The decoded bytes + """ + r: List[int] = [] + z: bool = False + + for b in s: + if not b: + z = True + continue + + if z: + r.extend((0,) * b) + z = False + else: + r.append(b) + + return bytes(r) + + +class FileType(IntEnum): + """Known file types""" + THUMBNAIL = 0 + CHAT_PHOTO = 1 # ProfilePhoto + PHOTO = 2 + VOICE = 3 # VoiceNote + VIDEO = 4 + DOCUMENT = 5 + ENCRYPTED = 6 + TEMP = 7 + STICKER = 8 + AUDIO = 9 + ANIMATION = 10 + ENCRYPTED_THUMBNAIL = 11 + WALLPAPER = 12 + VIDEO_NOTE = 13 + SECURE_RAW = 14 + SECURE = 15 + BACKGROUND = 16 + DOCUMENT_AS_FILE = 17 + + +class ThumbnailSource(IntEnum): + """Known thumbnail sources""" + LEGACY = 0 + THUMBNAIL = 1 + CHAT_PHOTO_SMALL = 2 # DialogPhotoSmall + CHAT_PHOTO_BIG = 3 # DialogPhotoBig + STICKER_SET_THUMBNAIL = 4 + + +# Photo-like file ids are longer and contain extra info, the rest are all documents +PHOTO_TYPES = {FileType.THUMBNAIL, FileType.CHAT_PHOTO, FileType.PHOTO, FileType.WALLPAPER, + FileType.ENCRYPTED_THUMBNAIL} +DOCUMENT_TYPES = set(FileType) - PHOTO_TYPES + +# Since the file type values are small enough to fit them in few bits, Telegram thought it would be a good idea to +# encode extra information about web url and file reference existence as flag inside the 4 bytes allocated for the field +WEB_LOCATION_FLAG = 1 << 24 +FILE_REFERENCE_FLAG = 1 << 25 + + +class FileId: + MAJOR = 4 + MINOR = 30 + + def __init__( + self, *, + major: int = MAJOR, + minor: int = MINOR, + file_type: FileType, + dc_id: int, + file_reference: bytes = b"", + url: str = None, + media_id: int = None, + access_hash: int = None, + volume_id: int = None, + thumbnail_source: ThumbnailSource = None, + thumbnail_file_type: FileType = None, + thumbnail_size: str = "", + secret: int = None, + local_id: int = None, + chat_id: int = None, + chat_access_hash: int = None, + sticker_set_id: int = None, + sticker_set_access_hash: int = None + ): + self.major = major + self.minor = minor + self.file_type = file_type + self.dc_id = dc_id + self.file_reference = file_reference + self.url = url + self.media_id = media_id + self.access_hash = access_hash + self.volume_id = volume_id + self.thumbnail_source = thumbnail_source + self.thumbnail_file_type = thumbnail_file_type + self.thumbnail_size = thumbnail_size + self.secret = secret + self.local_id = local_id + self.chat_id = chat_id + self.chat_access_hash = chat_access_hash + self.sticker_set_id = sticker_set_id + self.sticker_set_access_hash = sticker_set_access_hash + + @staticmethod + def decode(file_id: str): + decoded = rle_decode(b64_decode(file_id)) + + # region read version + # File id versioning. Major versions lower than 4 don't have a minor version + major = decoded[-1] + + if major < 4: + minor = 0 + buffer = BytesIO(decoded[:-1]) + else: + minor = decoded[-2] + buffer = BytesIO(decoded[:-2]) + # endregion + + file_type, dc_id = struct.unpack("= 4: + buffer.write(struct.pack(" +# +# 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 . + +import inspect +import re +from typing import Callable, Union, List, Pattern + +import pyrogram +from pyrogram import enums +from pyrogram.types import Message, CallbackQuery, InlineQuery, InlineKeyboardMarkup, ReplyKeyboardMarkup, Update + + +class Filter: + async def __call__(self, client: "pyrogram.Client", update: Update): + raise NotImplementedError + + def __invert__(self): + return InvertFilter(self) + + def __and__(self, other): + return AndFilter(self, other) + + def __or__(self, other): + return OrFilter(self, other) + + +class InvertFilter(Filter): + def __init__(self, base): + self.base = base + + async def __call__(self, client: "pyrogram.Client", update: Update): + if inspect.iscoroutinefunction(self.base.__call__): + x = await self.base(client, update) + else: + x = await client.loop.run_in_executor( + client.executor, + self.base, + client, update + ) + + return not x + + +class AndFilter(Filter): + def __init__(self, base, other): + self.base = base + self.other = other + + async def __call__(self, client: "pyrogram.Client", update: Update): + if inspect.iscoroutinefunction(self.base.__call__): + x = await self.base(client, update) + else: + x = await client.loop.run_in_executor( + client.executor, + self.base, + client, update + ) + + # short circuit + if not x: + return False + + if inspect.iscoroutinefunction(self.other.__call__): + y = await self.other(client, update) + else: + y = await client.loop.run_in_executor( + client.executor, + self.other, + client, update + ) + + return x and y + + +class OrFilter(Filter): + def __init__(self, base, other): + self.base = base + self.other = other + + async def __call__(self, client: "pyrogram.Client", update: Update): + if inspect.iscoroutinefunction(self.base.__call__): + x = await self.base(client, update) + else: + x = await client.loop.run_in_executor( + client.executor, + self.base, + client, update + ) + + # short circuit + if x: + return True + + if inspect.iscoroutinefunction(self.other.__call__): + y = await self.other(client, update) + else: + y = await client.loop.run_in_executor( + client.executor, + self.other, + client, update + ) + + return x or y + + +CUSTOM_FILTER_NAME = "CustomFilter" + + +def create(func: Callable, name: str = None, **kwargs) -> Filter: + """Easily create a custom filter. + + Custom filters give you extra control over which updates are allowed or not to be processed by your handlers. + + Parameters: + func (``Callable``): + A function that accepts three positional arguments *(filter, client, update)* and returns a boolean: True if the + update should be handled, False otherwise. + The *filter* argument refers to the filter itself and can be used to access keyword arguments (read below). + The *client* argument refers to the :obj:`~pyrogram.Client` that received the update. + The *update* argument type will vary depending on which `Handler `_ is coming from. + For example, in a :obj:`~pyrogram.handlers.MessageHandler` the *update* argument will be a :obj:`~pyrogram.types.Message`; in a :obj:`~pyrogram.handlers.CallbackQueryHandler` the *update* will be a :obj:`~pyrogram.types.CallbackQuery`. + Your function body can then access the incoming update attributes and decide whether to allow it or not. + + name (``str``, *optional*): + Your filter's name. Can be anything you like. + Defaults to "CustomFilter". + + **kwargs (``any``, *optional*): + Any keyword argument you would like to pass. Useful when creating parameterized custom filters, such as + :meth:`~pyrogram.filters.command` or :meth:`~pyrogram.filters.regex`. + """ + return type( + name or func.__name__ or CUSTOM_FILTER_NAME, + (Filter,), + {"__call__": func, **kwargs} + )() + + +# region all_filter +async def all_filter(_, __, ___): + return True + + +all = create(all_filter) +"""Filter all messages.""" + + +# endregion + +# region me_filter +async def me_filter(_, __, m: Message): + return bool(m.from_user and m.from_user.is_self or getattr(m, "outgoing", False)) + + +me = create(me_filter) +"""Filter messages generated by you yourself.""" + + +# endregion + +# region bot_filter +async def bot_filter(_, __, m: Message): + return bool(m.from_user and m.from_user.is_bot) + + +bot = create(bot_filter) +"""Filter messages coming from bots.""" + + +# endregion + +# region incoming_filter +async def incoming_filter(_, __, m: Message): + return not m.outgoing + + +incoming = create(incoming_filter) +"""Filter incoming messages. Messages sent to your own chat (Saved Messages) are also recognised as incoming.""" + + +# endregion + +# region outgoing_filter +async def outgoing_filter(_, __, m: Message): + return m.outgoing + + +outgoing = create(outgoing_filter) +"""Filter outgoing messages. Messages sent to your own chat (Saved Messages) are not recognized as outgoing.""" + + +# endregion + +# region text_filter +async def text_filter(_, __, m: Message): + return bool(m.text) + + +text = create(text_filter) +"""Filter text messages.""" + + +# endregion + +# region reply_filter +async def reply_filter(_, __, m: Message): + return bool(m.reply_to_message_id) + + +reply = create(reply_filter) +"""Filter messages that are replies to other messages.""" + + +# endregion + +# region forwarded_filter +async def forwarded_filter(_, __, m: Message): + return bool(m.forward_date) + + +forwarded = create(forwarded_filter) +"""Filter messages that are forwarded.""" + + +# endregion + +# region caption_filter +async def caption_filter(_, __, m: Message): + return bool(m.caption) + + +caption = create(caption_filter) +"""Filter media messages that contain captions.""" + + +# endregion + + +# region audio_filter +async def audio_filter(_, __, m: Message): + return bool(m.audio) + + +audio = create(audio_filter) +"""Filter messages that contain :obj:`~pyrogram.types.Audio` objects.""" + + +# endregion + +# region document_filter +async def document_filter(_, __, m: Message): + return bool(m.document) + + +document = create(document_filter) +"""Filter messages that contain :obj:`~pyrogram.types.Document` objects.""" + + +# endregion + +# region photo_filter +async def photo_filter(_, __, m: Message): + return bool(m.photo) + + +photo = create(photo_filter) +"""Filter messages that contain :obj:`~pyrogram.types.Photo` objects.""" + + +# endregion + +# region sticker_filter +async def sticker_filter(_, __, m: Message): + return bool(m.sticker) + + +sticker = create(sticker_filter) +"""Filter messages that contain :obj:`~pyrogram.types.Sticker` objects.""" + + +# endregion + +# region animation_filter +async def animation_filter(_, __, m: Message): + return bool(m.animation) + + +animation = create(animation_filter) +"""Filter messages that contain :obj:`~pyrogram.types.Animation` objects.""" + + +# endregion + +# region game_filter +async def game_filter(_, __, m: Message): + return bool(m.game) + + +game = create(game_filter) +"""Filter messages that contain :obj:`~pyrogram.types.Game` objects.""" + + +# endregion + +# region video_filter +async def video_filter(_, __, m: Message): + return bool(m.video) + + +video = create(video_filter) +"""Filter messages that contain :obj:`~pyrogram.types.Video` objects.""" + + +# endregion + +# region media_group_filter +async def media_group_filter(_, __, m: Message): + return bool(m.media_group_id) + + +media_group = create(media_group_filter) +"""Filter messages containing photos or videos being part of an album.""" + + +# endregion + +# region voice_filter +async def voice_filter(_, __, m: Message): + return bool(m.voice) + + +voice = create(voice_filter) +"""Filter messages that contain :obj:`~pyrogram.types.Voice` note objects.""" + + +# endregion + +# region video_note_filter +async def video_note_filter(_, __, m: Message): + return bool(m.video_note) + + +video_note = create(video_note_filter) +"""Filter messages that contain :obj:`~pyrogram.types.VideoNote` objects.""" + + +# endregion + +# region contact_filter +async def contact_filter(_, __, m: Message): + return bool(m.contact) + + +contact = create(contact_filter) +"""Filter messages that contain :obj:`~pyrogram.types.Contact` objects.""" + + +# endregion + +# region location_filter +async def location_filter(_, __, m: Message): + return bool(m.location) + + +location = create(location_filter) +"""Filter messages that contain :obj:`~pyrogram.types.Location` objects.""" + + +# endregion + +# region venue_filter +async def venue_filter(_, __, m: Message): + return bool(m.venue) + + +venue = create(venue_filter) +"""Filter messages that contain :obj:`~pyrogram.types.Venue` objects.""" + + +# endregion + +# region web_page_filter +async def web_page_filter(_, __, m: Message): + return bool(m.web_page) + + +web_page = create(web_page_filter) +"""Filter messages sent with a webpage preview.""" + + +# endregion + +# region poll_filter +async def poll_filter(_, __, m: Message): + return bool(m.poll) + + +poll = create(poll_filter) +"""Filter messages that contain :obj:`~pyrogram.types.Poll` objects.""" + + +# endregion + +# region dice_filter +async def dice_filter(_, __, m: Message): + return bool(m.dice) + + +dice = create(dice_filter) +"""Filter messages that contain :obj:`~pyrogram.types.Dice` objects.""" + + +# endregion + +# region media_spoiler +async def media_spoiler_filter(_, __, m: Message): + return bool(m.has_media_spoiler) + + +media_spoiler = create(media_spoiler_filter) +"""Filter media messages that contain a spoiler.""" + + +# endregion + +# region private_filter +async def private_filter(_, __, m: Message): + return bool(m.chat and m.chat.type in {enums.ChatType.PRIVATE, enums.ChatType.BOT}) + + +private = create(private_filter) +"""Filter messages sent in private chats.""" + + +# endregion + +# region group_filter +async def group_filter(_, __, m: Message): + return bool(m.chat and m.chat.type in {enums.ChatType.GROUP, enums.ChatType.SUPERGROUP}) + + +group = create(group_filter) +"""Filter messages sent in group or supergroup chats.""" + + +# endregion + +# region channel_filter +async def channel_filter(_, __, m: Message): + return bool(m.chat and m.chat.type == enums.ChatType.CHANNEL) + + +channel = create(channel_filter) +"""Filter messages sent in channels.""" + + +# endregion + +# region new_chat_members_filter +async def new_chat_members_filter(_, __, m: Message): + return bool(m.new_chat_members) + + +new_chat_members = create(new_chat_members_filter) +"""Filter service messages for new chat members.""" + + +# endregion + +# region left_chat_member_filter +async def left_chat_member_filter(_, __, m: Message): + return bool(m.left_chat_member) + + +left_chat_member = create(left_chat_member_filter) +"""Filter service messages for members that left the chat.""" + + +# endregion + +# region new_chat_title_filter +async def new_chat_title_filter(_, __, m: Message): + return bool(m.new_chat_title) + + +new_chat_title = create(new_chat_title_filter) +"""Filter service messages for new chat titles.""" + + +# endregion + +# region new_chat_photo_filter +async def new_chat_photo_filter(_, __, m: Message): + return bool(m.new_chat_photo) + + +new_chat_photo = create(new_chat_photo_filter) +"""Filter service messages for new chat photos.""" + + +# endregion + +# region delete_chat_photo_filter +async def delete_chat_photo_filter(_, __, m: Message): + return bool(m.delete_chat_photo) + + +delete_chat_photo = create(delete_chat_photo_filter) +"""Filter service messages for deleted photos.""" + + +# endregion + +# region group_chat_created_filter +async def group_chat_created_filter(_, __, m: Message): + return bool(m.group_chat_created) + + +group_chat_created = create(group_chat_created_filter) +"""Filter service messages for group chat creations.""" + + +# endregion + +# region supergroup_chat_created_filter +async def supergroup_chat_created_filter(_, __, m: Message): + return bool(m.supergroup_chat_created) + + +supergroup_chat_created = create(supergroup_chat_created_filter) +"""Filter service messages for supergroup chat creations.""" + + +# endregion + +# region channel_chat_created_filter +async def channel_chat_created_filter(_, __, m: Message): + return bool(m.channel_chat_created) + + +channel_chat_created = create(channel_chat_created_filter) +"""Filter service messages for channel chat creations.""" + + +# endregion + +# region migrate_to_chat_id_filter +async def migrate_to_chat_id_filter(_, __, m: Message): + return bool(m.migrate_to_chat_id) + + +migrate_to_chat_id = create(migrate_to_chat_id_filter) +"""Filter service messages that contain migrate_to_chat_id.""" + + +# endregion + +# region migrate_from_chat_id_filter +async def migrate_from_chat_id_filter(_, __, m: Message): + return bool(m.migrate_from_chat_id) + + +migrate_from_chat_id = create(migrate_from_chat_id_filter) +"""Filter service messages that contain migrate_from_chat_id.""" + + +# endregion + +# region pinned_message_filter +async def pinned_message_filter(_, __, m: Message): + return bool(m.pinned_message) + + +pinned_message = create(pinned_message_filter) +"""Filter service messages for pinned messages.""" + + +# endregion + +# region game_high_score_filter +async def game_high_score_filter(_, __, m: Message): + return bool(m.game_high_score) + + +game_high_score = create(game_high_score_filter) +"""Filter service messages for game high scores.""" + + +# endregion + +# region reply_keyboard_filter +async def reply_keyboard_filter(_, __, m: Message): + return isinstance(m.reply_markup, ReplyKeyboardMarkup) + + +reply_keyboard = create(reply_keyboard_filter) +"""Filter messages containing reply keyboard markups""" + + +# endregion + +# region inline_keyboard_filter +async def inline_keyboard_filter(_, __, m: Message): + return isinstance(m.reply_markup, InlineKeyboardMarkup) + + +inline_keyboard = create(inline_keyboard_filter) +"""Filter messages containing inline keyboard markups""" + + +# endregion + +# region mentioned_filter +async def mentioned_filter(_, __, m: Message): + return bool(m.mentioned) + + +mentioned = create(mentioned_filter) +"""Filter messages containing mentions""" + + +# endregion + +# region via_bot_filter +async def via_bot_filter(_, __, m: Message): + return bool(m.via_bot) + + +via_bot = create(via_bot_filter) +"""Filter messages sent via inline bots""" + + +# endregion + +# region video_chat_started_filter +async def video_chat_started_filter(_, __, m: Message): + return bool(m.video_chat_started) + + +video_chat_started = create(video_chat_started_filter) +"""Filter messages for started video chats""" + + +# endregion + +# region video_chat_ended_filter +async def video_chat_ended_filter(_, __, m: Message): + return bool(m.video_chat_ended) + + +video_chat_ended = create(video_chat_ended_filter) +"""Filter messages for ended video chats""" + + +# endregion + +# region video_chat_members_invited_filter +async def video_chat_members_invited_filter(_, __, m: Message): + return bool(m.video_chat_members_invited) + + +video_chat_members_invited = create(video_chat_members_invited_filter) +"""Filter messages for voice chat invited members""" + + +# endregion + +# region service_filter +async def service_filter(_, __, m: Message): + return bool(m.service) + + +service = create(service_filter) +"""Filter service messages. + +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*, +*video_chat_started*, *video_chat_ended*, *video_chat_members_invited*. +""" + + +# endregion + +# region media_filter +async def media_filter(_, __, m: Message): + return bool(m.media) + + +media = create(media_filter) +"""Filter media messages. + +A media message contains any of the following fields set: *audio*, *document*, *photo*, *sticker*, *video*, +*animation*, *voice*, *video_note*, *contact*, *location*, *venue*, *poll*. +""" + + +# endregion + +# region scheduled_filter +async def scheduled_filter(_, __, m: Message): + return bool(m.scheduled) + + +scheduled = create(scheduled_filter) +"""Filter messages that have been scheduled (not yet sent).""" + + +# endregion + +# region from_scheduled_filter +async def from_scheduled_filter(_, __, m: Message): + return bool(m.from_scheduled) + + +from_scheduled = create(from_scheduled_filter) +"""Filter new automatically sent messages that were previously scheduled.""" + + +# endregion + +# region linked_channel_filter +async def linked_channel_filter(_, __, m: Message): + return bool(m.forward_from_chat and not m.from_user) + + +linked_channel = create(linked_channel_filter) +"""Filter messages that are automatically forwarded from the linked channel to the group chat.""" + + +# endregion + + +# region command_filter +def command(commands: Union[str, List[str]], prefixes: Union[str, List[str]] = "/", case_sensitive: bool = False): + """Filter commands, i.e.: text messages starting with "/" or any other custom prefix. + + 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 :obj:`~pyrogram.types.Message`. + + prefixes (``str`` | ``list``, *optional*): + A prefix or a list of prefixes as string the filter should look for. + Defaults to "/" (slash). Examples: ".", "!", ["/", "!", "."], list(".:!"). + Pass None or "" (empty string) to allow commands with no prefix at all. + + case_sensitive (``bool``, *optional*): + Pass True if you want your command(s) to be case sensitive. Defaults to False. + Examples: when True, command="Start" would trigger /Start but not /start. + """ + command_re = re.compile(r"([\"'])(.*?)(?`_ are + stored in the ``matches`` field of the update object itself. + + Parameters: + pattern (``str`` | ``Pattern``): + The regex pattern as string or as pre-compiled pattern. + + flags (``int``, *optional*): + Regex flags. + """ + + async def func(flt, _, update: Update): + if isinstance(update, Message): + value = update.text or update.caption + elif isinstance(update, CallbackQuery): + value = update.data + elif isinstance(update, InlineQuery): + value = update.query + else: + raise ValueError(f"Regex filter doesn't work with {type(update)}") + + if value: + update.matches = list(flt.p.finditer(value)) or None + + return bool(update.matches) + + return create( + func, + "RegexFilter", + p=pattern if isinstance(pattern, Pattern) else re.compile(pattern, flags) + ) + + +# noinspection PyPep8Naming +class user(Filter, set): + """Filter messages coming from one or more users. + + You can use `set bound methods `_ to manipulate the + users container. + + 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. + Defaults to None (no users). + """ + + def __init__(self, users: Union[int, str, List[Union[int, str]]] = None): + users = [] if users is None else users if isinstance(users, list) else [users] + + super().__init__( + "me" if u in ["me", "self"] + else u.lower().strip("@") if isinstance(u, str) + else u for u in users + ) + + async def __call__(self, _, message: Message): + return (message.from_user + and (message.from_user.id in self + or (message.from_user.username + and message.from_user.username.lower() in self) + or ("me" in self + and message.from_user.is_self))) + + +# noinspection PyPep8Naming +class chat(Filter, set): + """Filter messages coming from one or more chats. + + You can use `set bound methods `_ to manipulate the + chats container. + + 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". + Defaults to None (no chats). + """ + + def __init__(self, chats: Union[int, str, List[Union[int, str]]] = None): + chats = [] if chats is None else chats if isinstance(chats, list) else [chats] + + super().__init__( + "me" if c in ["me", "self"] + else c.lower().strip("@") if isinstance(c, str) + else c for c in chats + ) + + async def __call__(self, _, message: Message): + return (message.chat + and (message.chat.id in self + or (message.chat.username + and message.chat.username.lower() in self) + or ("me" in self + and message.from_user + and message.from_user.is_self + and not message.outgoing))) diff --git a/pyrogram/handlers/__init__.py b/pyrogram/handlers/__init__.py new file mode 100644 index 00000000000..1c762958a0c --- /dev/null +++ b/pyrogram/handlers/__init__.py @@ -0,0 +1,30 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 .callback_query_handler import CallbackQueryHandler +from .chat_join_request_handler import ChatJoinRequestHandler +from .chat_member_updated_handler import ChatMemberUpdatedHandler +from .chosen_inline_result_handler import ChosenInlineResultHandler +from .deleted_messages_handler import DeletedMessagesHandler +from .disconnect_handler import DisconnectHandler +from .edited_message_handler import EditedMessageHandler +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 diff --git a/pyrogram/handlers/callback_query_handler.py b/pyrogram/handlers/callback_query_handler.py new file mode 100644 index 00000000000..b924fffa262 --- /dev/null +++ b/pyrogram/handlers/callback_query_handler.py @@ -0,0 +1,49 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Callable + +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:`~pyrogram.Client.add_handler` + + For a nicer way to register this handler, have a look at the + :meth:`~pyrogram.Client.on_callback_query` decorator. + + Parameters: + callback (``Callable``): + Pass a function that will be called when a new CallbackQuery arrives. It takes *(client, callback_query)* + as positional arguments (look at the section below for a detailed description). + + filters (:obj:`Filters`): + Pass one or more filters to allow only a subset of callback queries to be passed + in your callback function. + + Other parameters: + client (:obj:`~pyrogram.Client`): + The Client itself, useful when you want to call other API methods inside the message handler. + + callback_query (:obj:`~pyrogram.types.CallbackQuery`): + The received callback query. + """ + + def __init__(self, callback: Callable, filters=None): + super().__init__(callback, filters) diff --git a/pyrogram/handlers/chat_join_request_handler.py b/pyrogram/handlers/chat_join_request_handler.py new file mode 100644 index 00000000000..54b8b86abb6 --- /dev/null +++ b/pyrogram/handlers/chat_join_request_handler.py @@ -0,0 +1,49 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Callable + +from .handler import Handler + + +class ChatJoinRequestHandler(Handler): + """The ChatJoinRequest handler class. Used to handle join chat requests. + It is intended to be used with :meth:`~pyrogram.Client.add_handler`. + + For a nicer way to register this handler, have a look at the + :meth:`~pyrogram.Client.on_chat_join_request` decorator. + + Parameters: + callback (``Callable``): + Pass a function that will be called when a new ChatJoinRequest event arrives. It takes + *(client, chat_join_request)* as positional arguments (look at the section below for a detailed + description). + + filters (:obj:`Filters`): + Pass one or more filters to allow only a subset of updates to be passed in your callback function. + + Other parameters: + client (:obj:`~pyrogram.Client`): + The Client itself, useful when you want to call other API methods inside the handler. + + chat_join_request (:obj:`~pyrogram.types.ChatJoinRequest`): + The received chat join request. + """ + + def __init__(self, callback: Callable, filters=None): + super().__init__(callback, filters) diff --git a/pyrogram/handlers/chat_member_updated_handler.py b/pyrogram/handlers/chat_member_updated_handler.py new file mode 100644 index 00000000000..a89e7e2b57d --- /dev/null +++ b/pyrogram/handlers/chat_member_updated_handler.py @@ -0,0 +1,49 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Callable + +from .handler import Handler + + +class ChatMemberUpdatedHandler(Handler): + """The ChatMemberUpdated handler class. Used to handle changes in the status of a chat member. + It is intended to be used with :meth:`~pyrogram.Client.add_handler`. + + For a nicer way to register this handler, have a look at the + :meth:`~pyrogram.Client.on_chat_member_updated` decorator. + + Parameters: + callback (``Callable``): + Pass a function that will be called when a new ChatMemberUpdated event arrives. It takes + *(client, chat_member_updated)* as positional arguments (look at the section below for a detailed + description). + + filters (:obj:`Filters`): + Pass one or more filters to allow only a subset of updates to be passed in your callback function. + + Other parameters: + client (:obj:`~pyrogram.Client`): + The Client itself, useful when you want to call other API methods inside the handler. + + chat_member_updated (:obj:`~pyrogram.types.ChatMemberUpdated`): + The received chat member update. + """ + + def __init__(self, callback: Callable, filters=None): + super().__init__(callback, filters) diff --git a/pyrogram/handlers/chosen_inline_result_handler.py b/pyrogram/handlers/chosen_inline_result_handler.py new file mode 100644 index 00000000000..1c04f8f44bc --- /dev/null +++ b/pyrogram/handlers/chosen_inline_result_handler.py @@ -0,0 +1,50 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Callable + +from .handler import Handler + + +class ChosenInlineResultHandler(Handler): + """The ChosenInlineResultHandler handler class. Used to handle chosen inline results coming from inline queries. + It is intended to be used with :meth:`~pyrogram.Client.add_handler` + + For a nicer way to register this handler, have a look at the + :meth:`~pyrogram.Client.on_chosen_inline_result` decorator. + + Parameters: + callback (``Callable``): + Pass a function that will be called when a new chosen inline result arrives. + It takes *(client, chosen_inline_result)* as positional arguments (look at the section below for a + detailed description). + + filters (:obj:`Filters`): + Pass one or more filters to allow only a subset of chosen inline results to be passed + in your callback function. + + Other parameters: + client (:obj:`~pyrogram.Client`): + The Client itself, useful when you want to call other API methods inside the message handler. + + chosen_inline_result (:obj:`~pyrogram.types.ChosenInlineResult`): + The received chosen inline result. + """ + + def __init__(self, callback: Callable, filters=None): + super().__init__(callback, filters) diff --git a/pyrogram/handlers/deleted_messages_handler.py b/pyrogram/handlers/deleted_messages_handler.py new file mode 100644 index 00000000000..ab9f8347051 --- /dev/null +++ b/pyrogram/handlers/deleted_messages_handler.py @@ -0,0 +1,61 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import List, Callable + +import pyrogram +from pyrogram.filters import Filter +from pyrogram.types import Message +from .handler import Handler + + +class DeletedMessagesHandler(Handler): + """The deleted messages handler class. Used to handle deleted messages coming from any chat + (private, group, channel). It is intended to be used with :meth:`~pyrogram.Client.add_handler` + + For a nicer way to register this handler, have a look at the + :meth:`~pyrogram.Client.on_deleted_messages` decorator. + + Parameters: + callback (``Callable``): + Pass a function that will be called when one or more messages have been deleted. + It takes *(client, messages)* as positional arguments (look at the section below for a detailed description). + + filters (:obj:`Filters`): + Pass one or more filters to allow only a subset of messages to be passed + in your callback function. + + Other parameters: + client (:obj:`~pyrogram.Client`): + The Client itself, useful when you want to call other API methods inside the message handler. + + messages (List of :obj:`~pyrogram.types.Message`): + The deleted messages, as list. + """ + + def __init__(self, callback: Callable, filters: Filter = None): + super().__init__(callback, filters) + + async def check(self, client: "pyrogram.Client", messages: List[Message]): + # Every message should be checked, if at least one matches the filter True is returned + # otherwise, or if the list is empty, False is returned + for message in messages: + if await super().check(client, message): + return True + else: + return False diff --git a/pyrogram/handlers/disconnect_handler.py b/pyrogram/handlers/disconnect_handler.py new file mode 100644 index 00000000000..7420afd67a1 --- /dev/null +++ b/pyrogram/handlers/disconnect_handler.py @@ -0,0 +1,43 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Callable + +from .handler import Handler + + +class DisconnectHandler(Handler): + """The Disconnect handler class. Used to handle disconnections. It is intended to be used with + :meth:`~pyrogram.Client.add_handler` + + For a nicer way to register this handler, have a look at the + :meth:`~pyrogram.Client.on_disconnect` decorator. + + Parameters: + callback (``Callable``): + Pass a function that will be called when a disconnection occurs. It takes *(client)* + as positional argument (look at the section below for a detailed description). + + Other parameters: + client (:obj:`~pyrogram.Client`): + The Client itself. Useful, for example, when you want to change the proxy before a new connection + is established. + """ + + def __init__(self, callback: Callable): + super().__init__(callback) diff --git a/pyrogram/handlers/edited_message_handler.py b/pyrogram/handlers/edited_message_handler.py new file mode 100644 index 00000000000..78deaf0fa92 --- /dev/null +++ b/pyrogram/handlers/edited_message_handler.py @@ -0,0 +1,49 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Callable + +from .handler import Handler + + +class EditedMessageHandler(Handler): + """The EditedMessage handler class. Used to handle edited messages. + It is intended to be used with :meth:`~pyrogram.Client.add_handler` + + For a nicer way to register this handler, have a look at the + :meth:`~pyrogram.Client.on_edited_message` decorator. + + Parameters: + callback (``Callable``): + Pass a function that will be called when a new edited message arrives. It takes *(client, message)* + as positional arguments (look at the section below for a detailed description). + + filters (:obj:`Filters`): + Pass one or more filters to allow only a subset of messages to be passed + in your callback function. + + Other parameters: + client (:obj:`~pyrogram.Client`): + The Client itself, useful when you want to call other API methods inside the message handler. + + edited_message (:obj:`~pyrogram.types.Message`): + The received edited message. + """ + + def __init__(self, callback: Callable, filters=None): + super().__init__(callback, filters) diff --git a/pyrogram/handlers/handler.py b/pyrogram/handlers/handler.py new file mode 100644 index 00000000000..c666d042e27 --- /dev/null +++ b/pyrogram/handlers/handler.py @@ -0,0 +1,43 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import inspect +from typing import Callable + +import pyrogram +from pyrogram.filters import Filter +from pyrogram.types import Update + + +class Handler: + def __init__(self, callback: Callable, filters: Filter = None): + self.callback = callback + self.filters = filters + + async def check(self, client: "pyrogram.Client", update: Update): + if callable(self.filters): + if inspect.iscoroutinefunction(self.filters.__call__): + return await self.filters(client, update) + else: + return await client.loop.run_in_executor( + client.executor, + self.filters, + client, update + ) + + return True diff --git a/pyrogram/handlers/inline_query_handler.py b/pyrogram/handlers/inline_query_handler.py new file mode 100644 index 00000000000..f5ea23bc574 --- /dev/null +++ b/pyrogram/handlers/inline_query_handler.py @@ -0,0 +1,49 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Callable + +from .handler import Handler + + +class InlineQueryHandler(Handler): + """The InlineQuery handler class. Used to handle inline queries. + It is intended to be used with :meth:`~pyrogram.Client.add_handler` + + For a nicer way to register this handler, have a look at the + :meth:`~pyrogram.Client.on_inline_query` decorator. + + Parameters: + callback (``Callable``): + Pass a function that will be called when a new InlineQuery arrives. It takes *(client, inline_query)* + as positional arguments (look at the section below for a detailed description). + + filters (:obj:`Filters`): + Pass one or more filters to allow only a subset of inline queries to be passed + in your callback function. + + Other parameters: + client (:obj:`~pyrogram.Client`): + The Client itself, useful when you want to call other API methods inside the inline query handler. + + inline_query (:obj:`~pyrogram.types.InlineQuery`): + The received inline query. + """ + + def __init__(self, callback: Callable, filters=None): + super().__init__(callback, filters) diff --git a/pyrogram/handlers/message_handler.py b/pyrogram/handlers/message_handler.py new file mode 100644 index 00000000000..f5a35b5531f --- /dev/null +++ b/pyrogram/handlers/message_handler.py @@ -0,0 +1,49 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Callable + +from .handler import Handler + + +class MessageHandler(Handler): + """The Message handler class. Used to handle new messages. + It is intended to be used with :meth:`~pyrogram.Client.add_handler` + + For a nicer way to register this handler, have a look at the + :meth:`~pyrogram.Client.on_message` decorator. + + Parameters: + callback (``Callable``): + Pass a function that will be called when a new Message arrives. It takes *(client, message)* + as positional arguments (look at the section below for a detailed description). + + filters (:obj:`Filters`): + Pass one or more filters to allow only a subset of messages to be passed + in your callback function. + + Other parameters: + client (:obj:`~pyrogram.Client`): + The Client itself, useful when you want to call other API methods inside the message handler. + + message (:obj:`~pyrogram.types.Message`): + The received message. + """ + + def __init__(self, callback: Callable, filters=None): + super().__init__(callback, filters) diff --git a/pyrogram/handlers/poll_handler.py b/pyrogram/handlers/poll_handler.py new file mode 100644 index 00000000000..332ca7ea284 --- /dev/null +++ b/pyrogram/handlers/poll_handler.py @@ -0,0 +1,50 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Callable + +from .handler import Handler + + +class PollHandler(Handler): + """The Poll handler class. Used to handle polls updates. + + It is intended to be used with :meth:`~pyrogram.Client.add_handler` + + For a nicer way to register this handler, have a look at the + :meth:`~pyrogram.Client.on_poll` decorator. + + Parameters: + callback (``Callable``): + Pass a function that will be called when a new poll update arrives. It takes *(client, poll)* + as positional arguments (look at the section below for a detailed description). + + filters (:obj:`Filters`): + Pass one or more filters to allow only a subset of polls to be passed + in your callback function. + + Other parameters: + client (:obj:`~pyrogram.Client`): + The Client itself, useful when you want to call other API methods inside the poll handler. + + poll (:obj:`~pyrogram.types.Poll`): + The received poll. + """ + + def __init__(self, callback: Callable, filters=None): + super().__init__(callback, filters) diff --git a/pyrogram/handlers/raw_update_handler.py b/pyrogram/handlers/raw_update_handler.py new file mode 100644 index 00000000000..d957083b573 --- /dev/null +++ b/pyrogram/handlers/raw_update_handler.py @@ -0,0 +1,67 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Callable + +from .handler import Handler + + +class RawUpdateHandler(Handler): + """The Raw Update handler class. Used to handle raw updates. It is intended to be used with + :meth:`~pyrogram.Client.add_handler` + + For a nicer way to register this handler, have a look at the + :meth:`~pyrogram.Client.on_raw_update` decorator. + + Parameters: + callback (``Callable``): + A function that will be called when a new update is received from the server. It takes + *(client, update, users, chats)* as positional arguments (look at the section below for + a detailed description). + + Other Parameters: + client (:obj:`~pyrogram.Client`): + The Client itself, useful when you want to call other API methods inside the update handler. + + update (``Update``): + The received update, which can be one of the many single Updates listed in the + :obj:`~pyrogram.raw.base.Update` base type. + + users (``dict``): + Dictionary of all :obj:`~pyrogram.types.User` mentioned in the update. + You can access extra info about the user (such as *first_name*, *last_name*, etc...) by using + the IDs you find in the *update* argument (e.g.: *users[1768841572]*). + + chats (``dict``): + Dictionary of all :obj:`~pyrogram.types.Chat` and + :obj:`~pyrogram.raw.types.Channel` mentioned in the update. + You can access extra info about the chat (such as *title*, *participants_count*, etc...) + by using the IDs you find in the *update* argument (e.g.: *chats[1701277281]*). + + Note: + The following Empty or Forbidden types may exist inside the *users* and *chats* dictionaries. + They mean you have been blocked by the user or banned from the group/channel. + + - :obj:`~pyrogram.raw.types.UserEmpty` + - :obj:`~pyrogram.raw.types.ChatEmpty` + - :obj:`~pyrogram.raw.types.ChatForbidden` + - :obj:`~pyrogram.raw.types.ChannelForbidden` + """ + + def __init__(self, callback: Callable): + super().__init__(callback) diff --git a/pyrogram/handlers/user_status_handler.py b/pyrogram/handlers/user_status_handler.py new file mode 100644 index 00000000000..f10871e8749 --- /dev/null +++ b/pyrogram/handlers/user_status_handler.py @@ -0,0 +1,47 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Callable + +from .handler import Handler + + +class UserStatusHandler(Handler): + """The UserStatus handler class. Used to handle user status updates (user going online or offline). + It is intended to be used with :meth:`~pyrogram.Client.add_handler`. + + For a nicer way to register this handler, have a look at the :meth:`~pyrogram.Client.on_user_status` decorator. + + Parameters: + callback (``Callable``): + Pass a function that will be called when a new user status update arrives. It takes *(client, user)* + as positional arguments (look at the section below for a detailed description). + + filters (:obj:`Filters`): + Pass one or more filters to allow only a subset of users to be passed in your callback function. + + Other parameters: + client (:obj:`~pyrogram.Client`): + The Client itself, useful when you want to call other API methods inside the user status handler. + + user (:obj:`~pyrogram.types.User`): + The user containing the updated status. + """ + + def __init__(self, callback: Callable, filters=None): + super().__init__(callback, filters) diff --git a/pyrogram/methods/__init__.py b/pyrogram/methods/__init__.py new file mode 100644 index 00000000000..ea71f6b178d --- /dev/null +++ b/pyrogram/methods/__init__.py @@ -0,0 +1,45 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 .advanced import Advanced +from .auth import Auth +from .bots import Bots +from .chats import Chats +from .contacts import Contacts +from .decorators import Decorators +from .invite_links import InviteLinks +from .messages import Messages +from .password import Password +from .users import Users +from .utilities import Utilities + + +class Methods( + Advanced, + Auth, + Bots, + Contacts, + Password, + Chats, + Users, + Messages, + Decorators, + Utilities, + InviteLinks, +): + pass diff --git a/pyrogram/methods/advanced/__init__.py b/pyrogram/methods/advanced/__init__.py new file mode 100644 index 00000000000..bf19658a3cb --- /dev/null +++ b/pyrogram/methods/advanced/__init__.py @@ -0,0 +1,29 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 .invoke import Invoke +from .resolve_peer import ResolvePeer +from .save_file import SaveFile + + +class Advanced( + Invoke, + ResolvePeer, + SaveFile +): + pass diff --git a/pyrogram/methods/advanced/invoke.py b/pyrogram/methods/advanced/invoke.py new file mode 100644 index 00000000000..0af771adc86 --- /dev/null +++ b/pyrogram/methods/advanced/invoke.py @@ -0,0 +1,89 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import logging + +import pyrogram +from pyrogram import raw +from pyrogram.raw.core import TLObject +from pyrogram.session import Session + +log = logging.getLogger(__name__) + + +class Invoke: + async def invoke( + self: "pyrogram.Client", + query: TLObject, + retries: int = Session.MAX_RETRIES, + timeout: float = Session.WAIT_TIMEOUT, + sleep_threshold: float = None + ): + """Invoke raw Telegram functions. + + 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... + + .. 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). + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + query (``RawFunction``): + The API Schema function filled with proper arguments. + + retries (``int``): + Number of retries. + + timeout (``float``): + Timeout in seconds. + + sleep_threshold (``float``): + Sleep threshold in seconds. + + Returns: + ``RawType``: The raw type response generated by the query. + + Raises: + RPCError: In case of a Telegram RPC error. + """ + if not self.is_connected: + raise ConnectionError("Client has not been started yet") + + if self.no_updates: + query = raw.functions.InvokeWithoutUpdates(query=query) + + if self.takeout_id: + query = raw.functions.InvokeWithTakeout(takeout_id=self.takeout_id, query=query) + + r = await self.session.invoke( + query, retries, timeout, + (sleep_threshold + if sleep_threshold is not None + else self.sleep_threshold) + ) + + await self.fetch_peers(getattr(r, "users", [])) + await self.fetch_peers(getattr(r, "chats", [])) + + return r diff --git a/pyrogram/methods/advanced/resolve_peer.py b/pyrogram/methods/advanced/resolve_peer.py new file mode 100644 index 00000000000..ebb8ff44757 --- /dev/null +++ b/pyrogram/methods/advanced/resolve_peer.py @@ -0,0 +1,125 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import logging +import re +from typing import Union + +import pyrogram +from pyrogram import raw +from pyrogram import utils +from pyrogram.errors import PeerIdInvalid + +log = logging.getLogger(__name__) + + +class ResolvePeer: + async def resolve_peer( + self: "pyrogram.Client", + peer_id: Union[int, str] + ) -> Union[raw.base.InputPeer, raw.base.InputUser, raw.base.InputChannel]: + """Get the InputPeer of a known peer id. + Useful whenever an InputPeer type is required. + + .. 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). + + .. include:: /_includes/usable-by/users-bots.rst + + 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: + ``InputPeer``: On success, the resolved peer id is returned in form of an InputPeer object. + + Raises: + KeyError: In case the peer doesn't exist in the internal database. + """ + if not self.is_connected: + raise ConnectionError("Client has not been started yet") + + try: + return await self.storage.get_peer_by_id(peer_id) + except KeyError: + if isinstance(peer_id, str): + if peer_id in ("self", "me"): + return raw.types.InputPeerSelf() + + peer_id = re.sub(r"[@+\s]", "", peer_id.lower()) + + try: + int(peer_id) + except ValueError: + try: + return await self.storage.get_peer_by_username(peer_id) + except KeyError: + await self.invoke( + raw.functions.contacts.ResolveUsername( + username=peer_id + ) + ) + + return await self.storage.get_peer_by_username(peer_id) + else: + try: + return await self.storage.get_peer_by_phone_number(peer_id) + except KeyError: + raise PeerIdInvalid + + peer_type = utils.get_peer_type(peer_id) + + if peer_type == "user": + await self.fetch_peers( + await self.invoke( + raw.functions.users.GetUsers( + id=[ + raw.types.InputUser( + user_id=peer_id, + access_hash=0 + ) + ] + ) + ) + ) + elif peer_type == "chat": + await self.invoke( + raw.functions.messages.GetChats( + id=[-peer_id] + ) + ) + else: + await self.invoke( + raw.functions.channels.GetChannels( + id=[ + raw.types.InputChannel( + channel_id=utils.get_channel_id(peer_id), + access_hash=0 + ) + ] + ) + ) + + try: + return await self.storage.get_peer_by_id(peer_id) + except KeyError: + raise PeerIdInvalid diff --git a/pyrogram/methods/advanced/save_file.py b/pyrogram/methods/advanced/save_file.py new file mode 100644 index 00000000000..453a62af178 --- /dev/null +++ b/pyrogram/methods/advanced/save_file.py @@ -0,0 +1,226 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import asyncio +import functools +import inspect +import io +import logging +import math +import os +from hashlib import md5 +from pathlib import PurePath +from typing import Union, BinaryIO, Callable + +import pyrogram +from pyrogram import StopTransmission +from pyrogram import raw +from pyrogram.session import Session + +log = logging.getLogger(__name__) + + +class SaveFile: + async def save_file( + self: "pyrogram.Client", + path: Union[str, BinaryIO], + 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. + + .. 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). + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + path (``str`` | ``BinaryIO``): + The path of the file you want to upload that exists on your local machine or a binary file-like object + with its attribute ".name" set for in-memory uploads. + + file_id (``int``, *optional*): + In case a file part expired, pass the file_id and the file_part to retry uploading that specific chunk. + + file_part (``int``, *optional*): + In case a file part expired, pass the file_id and the file_part to retry uploading that specific chunk. + + progress (``Callable``, *optional*): + Pass a callback function to view the file transmission progress. + The function must take *(current, total)* as positional arguments (look at Other Parameters below for a + detailed description) and will be called back each time a new file chunk has been successfully + transmitted. + + progress_args (``tuple``, *optional*): + Extra custom arguments for the progress callback function. + You can pass anything you need to be available in the progress callback scope; for example, a Message + object or a Client instance in order to edit the message with the updated progress status. + + Other Parameters: + current (``int``): + The amount of bytes transmitted so far. + + total (``int``): + The total size of the file. + + *args (``tuple``, *optional*): + Extra custom arguments as defined in the ``progress_args`` parameter. + You can either keep ``*args`` or add every single extra argument in your function signature. + + Returns: + ``InputFile``: On success, the uploaded file is returned in form of an InputFile object. + + Raises: + RPCError: In case of a Telegram RPC error. + """ + async with self.save_file_semaphore: + if path is None: + return None + + async def worker(session): + while True: + data = await queue.get() + + if data is None: + return + + try: + await session.invoke(data) + except Exception as e: + log.exception(e) + + part_size = 512 * 1024 + + if isinstance(path, (str, PurePath)): + fp = open(path, "rb") + elif isinstance(path, io.IOBase): + fp = path + else: + raise ValueError("Invalid file. Expected a file path as string or a binary (not text) file pointer") + + file_name = getattr(fp, "name", "file.jpg") + + fp.seek(0, os.SEEK_END) + file_size = fp.tell() + fp.seek(0) + + if file_size == 0: + raise ValueError("File size equals to 0 B") + + file_size_limit_mib = 4000 if self.me.is_premium else 2000 + + if file_size > file_size_limit_mib * 1024 * 1024: + raise ValueError(f"Can't upload files bigger than {file_size_limit_mib} MiB") + + file_total_parts = int(math.ceil(file_size / part_size)) + is_big = file_size > 10 * 1024 * 1024 + workers_count = 4 if is_big else 1 + is_missing_part = file_id is not None + file_id = file_id or self.rnd_id() + md5_sum = md5() if not is_big and not is_missing_part else None + session = Session( + self, await self.storage.dc_id(), await self.storage.auth_key(), + await self.storage.test_mode(), is_media=True + ) + workers = [self.loop.create_task(worker(session)) for _ in range(workers_count)] + queue = asyncio.Queue(1) + + try: + await session.start() + + fp.seek(part_size * file_part) + + while True: + chunk = fp.read(part_size) + + if not chunk: + if not is_big and not is_missing_part: + md5_sum = "".join([hex(i)[2:].zfill(2) for i in md5_sum.digest()]) + break + + if is_big: + rpc = raw.functions.upload.SaveBigFilePart( + file_id=file_id, + file_part=file_part, + file_total_parts=file_total_parts, + bytes=chunk + ) + else: + rpc = raw.functions.upload.SaveFilePart( + file_id=file_id, + file_part=file_part, + bytes=chunk + ) + + await queue.put(rpc) + + if is_missing_part: + return + + if not is_big and not is_missing_part: + md5_sum.update(chunk) + + file_part += 1 + + if progress: + func = functools.partial( + progress, + min(file_part * part_size, file_size), + file_size, + *progress_args + ) + + if inspect.iscoroutinefunction(progress): + await func() + else: + await self.loop.run_in_executor(self.executor, func) + except StopTransmission: + raise + except Exception as e: + log.exception(e) + else: + if is_big: + return raw.types.InputFileBig( + id=file_id, + parts=file_total_parts, + name=file_name, + + ) + else: + return raw.types.InputFile( + id=file_id, + parts=file_total_parts, + name=file_name, + md5_checksum=md5_sum + ) + finally: + for _ in workers: + await queue.put(None) + + await asyncio.gather(*workers) + + await session.stop() + + if isinstance(path, (str, PurePath)): + fp.close() diff --git a/pyrogram/methods/auth/__init__.py b/pyrogram/methods/auth/__init__.py new file mode 100644 index 00000000000..ce585648da4 --- /dev/null +++ b/pyrogram/methods/auth/__init__.py @@ -0,0 +1,53 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 .accept_terms_of_service import AcceptTermsOfService +from .check_password import CheckPassword +from .connect import Connect +from .disconnect import Disconnect +from .get_password_hint import GetPasswordHint +from .initialize import Initialize +from .log_out import LogOut +from .recover_password import RecoverPassword +from .resend_code import ResendCode +from .send_code import SendCode +from .send_recovery_code import SendRecoveryCode +from .sign_in import SignIn +from .sign_in_bot import SignInBot +from .sign_up import SignUp +from .terminate import Terminate + + +class Auth( + AcceptTermsOfService, + CheckPassword, + Connect, + Disconnect, + GetPasswordHint, + Initialize, + LogOut, + RecoverPassword, + ResendCode, + SendCode, + SendRecoveryCode, + SignIn, + SignInBot, + SignUp, + Terminate +): + pass diff --git a/pyrogram/methods/auth/accept_terms_of_service.py b/pyrogram/methods/auth/accept_terms_of_service.py new file mode 100644 index 00000000000..cc1fcf54533 --- /dev/null +++ b/pyrogram/methods/auth/accept_terms_of_service.py @@ -0,0 +1,44 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import pyrogram +from pyrogram import raw + + +class AcceptTermsOfService: + async def accept_terms_of_service( + self: "pyrogram.Client", + terms_of_service_id: str + ) -> bool: + """Accept the given terms of service. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + terms_of_service_id (``str``): + The terms of service identifier. + """ + r = await self.invoke( + raw.functions.help.AcceptTermsOfService( + id=raw.types.DataJSON( + data=terms_of_service_id + ) + ) + ) + + return bool(r) diff --git a/pyrogram/methods/auth/check_password.py b/pyrogram/methods/auth/check_password.py new file mode 100644 index 00000000000..39aa82fcc0f --- /dev/null +++ b/pyrogram/methods/auth/check_password.py @@ -0,0 +1,60 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import logging + +import pyrogram +from pyrogram import raw +from pyrogram import types +from pyrogram.utils import compute_password_check + +log = logging.getLogger(__name__) + + +class CheckPassword: + async def check_password( + self: "pyrogram.Client", + password: str + ) -> "types.User": + """Check your Two-Step Verification password and log in. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + password (``str``): + Your Two-Step Verification password. + + Returns: + :obj:`~pyrogram.types.User`: On success, the authorized user is returned. + + Raises: + BadRequest: In case the password is invalid. + """ + r = await self.invoke( + raw.functions.auth.CheckPassword( + password=compute_password_check( + await self.invoke(raw.functions.account.GetPassword()), + password + ) + ) + ) + + await self.storage.user_id(r.user.id) + await self.storage.is_bot(False) + + return types.User._parse(self, r.user) diff --git a/pyrogram/methods/auth/connect.py b/pyrogram/methods/auth/connect.py new file mode 100644 index 00000000000..612e064bb48 --- /dev/null +++ b/pyrogram/methods/auth/connect.py @@ -0,0 +1,51 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import pyrogram +from pyrogram.session import Session + + +class Connect: + async def connect( + self: "pyrogram.Client", + ) -> bool: + """ + Connect the client to Telegram servers. + + Returns: + ``bool``: On success, in case the passed-in session is authorized, True is returned. Otherwise, in case + the session needs to be authorized, False is returned. + + Raises: + ConnectionError: In case you try to connect an already connected client. + """ + if self.is_connected: + raise ConnectionError("Client is already connected") + + await self.load_session() + + self.session = Session( + self, await self.storage.dc_id(), + await self.storage.auth_key(), await self.storage.test_mode() + ) + + await self.session.start() + + self.is_connected = True + + return bool(await self.storage.user_id()) diff --git a/pyrogram/methods/auth/disconnect.py b/pyrogram/methods/auth/disconnect.py new file mode 100644 index 00000000000..daa07b83533 --- /dev/null +++ b/pyrogram/methods/auth/disconnect.py @@ -0,0 +1,40 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import pyrogram + + +class Disconnect: + async def disconnect( + self: "pyrogram.Client", + ): + """Disconnect the client from Telegram servers. + + Raises: + ConnectionError: In case you try to disconnect an already disconnected client or in case you try to + disconnect a client that needs to be terminated first. + """ + if not self.is_connected: + raise ConnectionError("Client is already disconnected") + + if self.is_initialized: + raise ConnectionError("Can't disconnect an initialized client") + + await self.session.stop() + await self.storage.close() + self.is_connected = False diff --git a/pyrogram/methods/auth/get_password_hint.py b/pyrogram/methods/auth/get_password_hint.py new file mode 100644 index 00000000000..a57f7c80757 --- /dev/null +++ b/pyrogram/methods/auth/get_password_hint.py @@ -0,0 +1,38 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import logging + +import pyrogram +from pyrogram import raw + +log = logging.getLogger(__name__) + + +class GetPasswordHint: + async def get_password_hint( + self: "pyrogram.Client", + ) -> str: + """Get your Two-Step Verification password hint. + + .. include:: /_includes/usable-by/users.rst + + Returns: + ``str``: On success, the password hint as string is returned. + """ + return (await self.invoke(raw.functions.account.GetPassword())).hint diff --git a/pyrogram/methods/auth/initialize.py b/pyrogram/methods/auth/initialize.py new file mode 100644 index 00000000000..7188b66817d --- /dev/null +++ b/pyrogram/methods/auth/initialize.py @@ -0,0 +1,52 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import asyncio +import logging + +import pyrogram + +log = logging.getLogger(__name__) + + +class Initialize: + async def initialize( + self: "pyrogram.Client", + ): + """Initialize the client by starting up workers. + + This method will start updates and download workers. + It will also load plugins and start the internal dispatcher. + + Raises: + ConnectionError: In case you try to initialize a disconnected client or in case you try to initialize an + already initialized client. + """ + if not self.is_connected: + raise ConnectionError("Can't initialize a disconnected client") + + if self.is_initialized: + raise ConnectionError("Client is already initialized") + + self.load_plugins() + + await self.dispatcher.start() + + self.updates_watchdog_task = asyncio.create_task(self.updates_watchdog()) + + self.is_initialized = True diff --git a/pyrogram/methods/auth/log_out.py b/pyrogram/methods/auth/log_out.py new file mode 100644 index 00000000000..84b7db64cdd --- /dev/null +++ b/pyrogram/methods/auth/log_out.py @@ -0,0 +1,51 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import logging + +import pyrogram +from pyrogram import raw + +log = logging.getLogger(__name__) + + +class LogOut: + async def log_out( + self: "pyrogram.Client", + ): + """Log out from Telegram and delete the *\\*.session* file. + + When you log out, the current client is stopped and the storage session deleted. + No more API calls can be made until you start the client and re-authorize again. + + .. include:: /_includes/usable-by/users-bots.rst + + Returns: + ``bool``: On success, True is returned. + + Example: + .. code-block:: python + + # Log out. + app.log_out() + """ + await self.invoke(raw.functions.auth.LogOut()) + await self.stop() + await self.storage.delete() + + return True diff --git a/pyrogram/methods/auth/recover_password.py b/pyrogram/methods/auth/recover_password.py new file mode 100644 index 00000000000..0a34750752f --- /dev/null +++ b/pyrogram/methods/auth/recover_password.py @@ -0,0 +1,57 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import logging + +import pyrogram +from pyrogram import raw +from pyrogram import types + +log = logging.getLogger(__name__) + + +class RecoverPassword: + async def recover_password( + self: "pyrogram.Client", + recovery_code: str + ) -> "types.User": + """Recover your password with a recovery code and log in. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + recovery_code (``str``): + The recovery code sent via email. + + Returns: + :obj:`~pyrogram.types.User`: On success, the authorized user is returned and the Two-Step Verification + password reset. + + Raises: + BadRequest: In case the recovery code is invalid. + """ + r = await self.invoke( + raw.functions.auth.RecoverPassword( + code=recovery_code + ) + ) + + await self.storage.user_id(r.user.id) + await self.storage.is_bot(False) + + return types.User._parse(self, r.user) diff --git a/pyrogram/methods/auth/resend_code.py b/pyrogram/methods/auth/resend_code.py new file mode 100644 index 00000000000..9644ac4f545 --- /dev/null +++ b/pyrogram/methods/auth/resend_code.py @@ -0,0 +1,64 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import logging + +import pyrogram +from pyrogram import raw +from pyrogram import types + +log = logging.getLogger(__name__) + + +class ResendCode: + async def resend_code( + self: "pyrogram.Client", + phone_number: str, + phone_code_hash: str + ) -> "types.SentCode": + """Re-send the confirmation code using a different type. + + The type of the code to be re-sent is specified in the *next_type* attribute of the + :obj:`~pyrogram.types.SentCode` object returned by :meth:`send_code`. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + phone_number (``str``): + Phone number in international format (includes the country prefix). + + phone_code_hash (``str``): + Confirmation code identifier. + + Returns: + :obj:`~pyrogram.types.SentCode`: On success, an object containing information on the re-sent confirmation + code is returned. + + Raises: + BadRequest: In case the arguments are invalid. + """ + phone_number = phone_number.strip(" +") + + r = await self.invoke( + raw.functions.auth.ResendCode( + phone_number=phone_number, + phone_code_hash=phone_code_hash + ) + ) + + return types.SentCode._parse(r) diff --git a/pyrogram/methods/auth/send_code.py b/pyrogram/methods/auth/send_code.py new file mode 100644 index 00000000000..92ffc999961 --- /dev/null +++ b/pyrogram/methods/auth/send_code.py @@ -0,0 +1,79 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import logging + +import pyrogram +from pyrogram import raw +from pyrogram import types +from pyrogram.errors import PhoneMigrate, NetworkMigrate +from pyrogram.session import Session, Auth + +log = logging.getLogger(__name__) + + +class SendCode: + async def send_code( + self: "pyrogram.Client", + phone_number: str + ) -> "types.SentCode": + """Send the confirmation code to the given phone number. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + phone_number (``str``): + Phone number in international format (includes the country prefix). + + Returns: + :obj:`~pyrogram.types.SentCode`: On success, an object containing information on the sent confirmation code + is returned. + + Raises: + BadRequest: In case the phone number is invalid. + """ + phone_number = phone_number.strip(" +") + + while True: + try: + r = await self.invoke( + raw.functions.auth.SendCode( + phone_number=phone_number, + api_id=self.api_id, + api_hash=self.api_hash, + settings=raw.types.CodeSettings() + ) + ) + except (PhoneMigrate, NetworkMigrate) as e: + await self.session.stop() + + await self.storage.dc_id(e.value) + await self.storage.auth_key( + await Auth( + self, await self.storage.dc_id(), + await self.storage.test_mode() + ).create() + ) + self.session = Session( + self, await self.storage.dc_id(), + await self.storage.auth_key(), await self.storage.test_mode() + ) + + await self.session.start() + else: + return types.SentCode._parse(r) diff --git a/pyrogram/methods/auth/send_recovery_code.py b/pyrogram/methods/auth/send_recovery_code.py new file mode 100644 index 00000000000..9a25f3c68ff --- /dev/null +++ b/pyrogram/methods/auth/send_recovery_code.py @@ -0,0 +1,43 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import logging + +import pyrogram +from pyrogram import raw + +log = logging.getLogger(__name__) + + +class SendRecoveryCode: + async def send_recovery_code( + self: "pyrogram.Client", + ) -> str: + """Send a code to your email to recover your password. + + .. include:: /_includes/usable-by/users.rst + + Returns: + ``str``: On success, the hidden email pattern is returned and a recovery code is sent to that email. + + Raises: + BadRequest: In case no recovery email was set up. + """ + return (await self.invoke( + raw.functions.auth.RequestPasswordRecovery() + )).email_pattern diff --git a/pyrogram/methods/auth/sign_in.py b/pyrogram/methods/auth/sign_in.py new file mode 100644 index 00000000000..9d77f1cd37f --- /dev/null +++ b/pyrogram/methods/auth/sign_in.py @@ -0,0 +1,80 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import logging +from typing import Union + +import pyrogram +from pyrogram import raw +from pyrogram import types + +log = logging.getLogger(__name__) + + +class SignIn: + async def sign_in( + self: "pyrogram.Client", + phone_number: str, + phone_code_hash: str, + phone_code: str + ) -> Union["types.User", "types.TermsOfService", bool]: + """Authorize a user in Telegram with a valid confirmation code. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + phone_number (``str``): + Phone number in international format (includes the country prefix). + + phone_code_hash (``str``): + Code identifier taken from the result of :meth:`~pyrogram.Client.send_code`. + + phone_code (``str``): + The valid confirmation code you received (either as Telegram message or as SMS in your phone number). + + Returns: + :obj:`~pyrogram.types.User` | :obj:`~pyrogram.types.TermsOfService` | bool: On success, in case the + authorization completed, the user is returned. In case the phone number needs to be registered first AND the + terms of services accepted (with :meth:`~pyrogram.Client.accept_terms_of_service`), an object containing + them is returned. In case the phone number needs to be registered, but the terms of services don't need to + be accepted, False is returned instead. + + Raises: + BadRequest: In case the arguments are invalid. + SessionPasswordNeeded: In case a password is needed to sign in. + """ + phone_number = phone_number.strip(" +") + + r = await self.invoke( + raw.functions.auth.SignIn( + phone_number=phone_number, + phone_code_hash=phone_code_hash, + phone_code=phone_code + ) + ) + + if isinstance(r, raw.types.auth.AuthorizationSignUpRequired): + if r.terms_of_service: + return types.TermsOfService._parse(terms_of_service=r.terms_of_service) + + return False + else: + await self.storage.user_id(r.user.id) + await self.storage.is_bot(False) + + return types.User._parse(self, r.user) diff --git a/pyrogram/methods/auth/sign_in_bot.py b/pyrogram/methods/auth/sign_in_bot.py new file mode 100644 index 00000000000..09a2f283798 --- /dev/null +++ b/pyrogram/methods/auth/sign_in_bot.py @@ -0,0 +1,79 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import logging + +import pyrogram +from pyrogram import raw +from pyrogram import types +from pyrogram.errors import UserMigrate +from pyrogram.session import Session, Auth + +log = logging.getLogger(__name__) + + +class SignInBot: + async def sign_in_bot( + self: "pyrogram.Client", + bot_token: str + ) -> "types.User": + """Authorize a bot using its bot token generated by BotFather. + + .. include:: /_includes/usable-by/bots.rst + + Parameters: + bot_token (``str``): + The bot token generated by BotFather + + Returns: + :obj:`~pyrogram.types.User`: On success, the bot identity is return in form of a user object. + + Raises: + BadRequest: In case the bot token is invalid. + """ + while True: + try: + r = await self.invoke( + raw.functions.auth.ImportBotAuthorization( + flags=0, + api_id=self.api_id, + api_hash=self.api_hash, + bot_auth_token=bot_token + ) + ) + except UserMigrate as e: + await self.session.stop() + + await self.storage.dc_id(e.value) + await self.storage.auth_key( + await Auth( + self, await self.storage.dc_id(), + await self.storage.test_mode() + ).create() + ) + self.session = Session( + self, await self.storage.dc_id(), + await self.storage.auth_key(), await self.storage.test_mode() + ) + + await self.session.start() + else: + await self.storage.user_id(r.user.id) + await self.storage.is_bot(True) + + return types.User._parse(self, r.user) diff --git a/pyrogram/methods/auth/sign_up.py b/pyrogram/methods/auth/sign_up.py new file mode 100644 index 00000000000..f09a779d8aa --- /dev/null +++ b/pyrogram/methods/auth/sign_up.py @@ -0,0 +1,73 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import logging + +import pyrogram +from pyrogram import raw +from pyrogram import types + +log = logging.getLogger(__name__) + + +class SignUp: + async def sign_up( + self: "pyrogram.Client", + phone_number: str, + phone_code_hash: str, + first_name: str, + last_name: str = "" + ) -> "types.User": + """Register a new user in Telegram. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + phone_number (``str``): + Phone number in international format (includes the country prefix). + + phone_code_hash (``str``): + Code identifier taken from the result of :meth:`~pyrogram.Client.send_code`. + + first_name (``str``): + New user first name. + + last_name (``str``, *optional*): + New user last name. Defaults to "" (empty string, no last name). + + Returns: + :obj:`~pyrogram.types.User`: On success, the new registered user is returned. + + Raises: + BadRequest: In case the arguments are invalid. + """ + phone_number = phone_number.strip(" +") + + r = await self.invoke( + raw.functions.auth.SignUp( + phone_number=phone_number, + first_name=first_name, + last_name=last_name, + phone_code_hash=phone_code_hash + ) + ) + + await self.storage.user_id(r.user.id) + await self.storage.is_bot(False) + + return types.User._parse(self, r.user) diff --git a/pyrogram/methods/auth/terminate.py b/pyrogram/methods/auth/terminate.py new file mode 100644 index 00000000000..d5fd949cbae --- /dev/null +++ b/pyrogram/methods/auth/terminate.py @@ -0,0 +1,61 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import logging + +import pyrogram +from pyrogram import raw + +log = logging.getLogger(__name__) + + +class Terminate: + async def terminate( + self: "pyrogram.Client", + ): + """Terminate the client by shutting down workers. + + This method does the opposite of :meth:`~pyrogram.Client.initialize`. + It will stop the dispatcher and shut down updates and download workers. + + Raises: + ConnectionError: In case you try to terminate a client that is already terminated. + """ + if not self.is_initialized: + raise ConnectionError("Client is already terminated") + + if self.takeout_id: + await self.invoke(raw.functions.account.FinishTakeoutSession()) + log.info("Takeout session %s finished", self.takeout_id) + + await self.storage.save() + await self.dispatcher.stop() + + for media_session in self.media_sessions.values(): + await media_session.stop() + + self.media_sessions.clear() + + self.updates_watchdog_event.set() + + if self.updates_watchdog_task is not None: + await self.updates_watchdog_task + + self.updates_watchdog_event.clear() + + self.is_initialized = False diff --git a/pyrogram/methods/bots/__init__.py b/pyrogram/methods/bots/__init__.py new file mode 100644 index 00000000000..da52fcfb00a --- /dev/null +++ b/pyrogram/methods/bots/__init__.py @@ -0,0 +1,55 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 .answer_callback_query import AnswerCallbackQuery +from .answer_inline_query import AnswerInlineQuery +from .answer_web_app_query import AnswerWebAppQuery +from .delete_bot_commands import DeleteBotCommands +from .get_bot_commands import GetBotCommands +from .get_bot_default_privileges import GetBotDefaultPrivileges +from .get_chat_menu_button import GetChatMenuButton +from .get_game_high_scores import GetGameHighScores +from .get_inline_bot_results import GetInlineBotResults +from .request_callback_answer import RequestCallbackAnswer +from .send_game import SendGame +from .send_inline_bot_result import SendInlineBotResult +from .set_bot_commands import SetBotCommands +from .set_bot_default_privileges import SetBotDefaultPrivileges +from .set_chat_menu_button import SetChatMenuButton +from .set_game_score import SetGameScore + + +class Bots( + AnswerCallbackQuery, + AnswerInlineQuery, + GetInlineBotResults, + RequestCallbackAnswer, + SendInlineBotResult, + SendGame, + SetGameScore, + GetGameHighScores, + SetBotCommands, + GetBotCommands, + DeleteBotCommands, + SetBotDefaultPrivileges, + GetBotDefaultPrivileges, + SetChatMenuButton, + GetChatMenuButton, + AnswerWebAppQuery +): + pass diff --git a/pyrogram/methods/bots/answer_callback_query.py b/pyrogram/methods/bots/answer_callback_query.py new file mode 100644 index 00000000000..a6d8747cd58 --- /dev/null +++ b/pyrogram/methods/bots/answer_callback_query.py @@ -0,0 +1,81 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import pyrogram +from pyrogram import raw + + +class AnswerCallbackQuery: + async def answer_callback_query( + self: "pyrogram.Client", + callback_query_id: str, + text: str = None, + show_alert: bool = None, + url: str = None, + cache_time: int = 0 + ): + """Send answers to callback queries sent from inline keyboards. + The answer will be displayed to the user as a notification at the top of the chat screen or as an alert. + + .. include:: /_includes/usable-by/bots.rst + + Parameters: + callback_query_id (``str``): + Unique identifier for the query to be answered. + + text (``str`` *optional*): + Text of the notification. If not specified, nothing will be shown to the user, 0-200 characters. + + show_alert (``bool``, *optional*): + If true, an alert will be shown by the client instead of a notification at the top of the chat screen. + Defaults to False. + + url (``str``, *optional*): + URL that will be opened by the user's client. + If you have created a Game and accepted the conditions via @Botfather, specify the URL that opens your + game – note that this will only work if the query comes from a callback_game button. + Otherwise, you may use links like t.me/your_bot?start=XXXX that open your bot with a parameter. + + cache_time (``int``, *optional*): + The maximum amount of time in seconds that the result of the callback query may be cached client-side. + Telegram apps will support caching starting in version 3.14. Defaults to 0. + + Returns: + ``bool``: True, on success. + + Example: + .. code-block:: python + + # Answer only (remove the spinning circles) + await app.answer_callback_query(query_id) + + # Answer without alert + await app.answer_callback_query(query_id, text=text) + + # Answer with alert + await app.answer_callback_query(query_id, text=text, show_alert=True) + """ + return await self.invoke( + raw.functions.messages.SetBotCallbackAnswer( + query_id=int(callback_query_id), + cache_time=cache_time, + alert=show_alert or None, + message=text or None, + url=url or None + ) + ) diff --git a/pyrogram/client/methods/bots/answer_inline_query.py b/pyrogram/methods/bots/answer_inline_query.py similarity index 53% rename from pyrogram/client/methods/bots/answer_inline_query.py rename to pyrogram/methods/bots/answer_inline_query.py index 7a1b14c8310..c3a450a015a 100644 --- a/pyrogram/client/methods/bots/answer_inline_query.py +++ b/pyrogram/methods/bots/answer_inline_query.py @@ -1,56 +1,64 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 Dan Tès +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan # -# This file is part of Pyrogram. +# 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 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. +# 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 . +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . -from typing import List +from typing import Iterable -from pyrogram.api import functions, types -from pyrogram.client.ext import BaseClient -from ...types.inline_mode import InlineQueryResult +import pyrogram +from pyrogram import raw +from pyrogram import types -class AnswerInlineQuery(BaseClient): - def answer_inline_query( - self, +class AnswerInlineQuery: + async def answer_inline_query( + self: "pyrogram.Client", inline_query_id: str, - results: List[InlineQueryResult], + results: Iterable["types.InlineQueryResult"], cache_time: int = 300, - is_personal: bool = None, + is_gallery: bool = False, + is_personal: bool = False, next_offset: str = "", switch_pm_text: str = "", switch_pm_parameter: str = "" ): - """Use this method to send answers to an inline query. - No more than 50 results per query are allowed. + """Send answers to an inline query. + + A maximum of 50 results per query is allowed. + + .. include:: /_includes/usable-by/bots.rst Parameters: inline_query_id (``str``): Unique identifier for the answered query. - results (List of :obj:`InlineQueryResult `): + results (List of :obj:`~pyrogram.types.InlineQueryResult`): A list of results for the inline query. cache_time (``int``, *optional*): The maximum amount of time in seconds that the result of the inline query may be cached on the server. Defaults to 300. + is_gallery (``bool``, *optional*): + Pass True, if results should be displayed in gallery mode instead of list mode. + Defaults to False. + is_personal (``bool``, *optional*): Pass True, if results may be cached on the server side only for the user that sent the query. - By default, results may be returned to any user who sends the same query. + By default (False), results may be returned to any user who sends the same query. next_offset (``str``, *optional*): Pass the offset that a client should send in the next query with the same text to receive more results. @@ -73,20 +81,30 @@ def answer_inline_query( where they wanted to use the bot's inline capabilities. Returns: - ``bool``: On success, True is returned. + ``bool``: True, on success. - Raises: - RPCError: In case of a Telegram RPC error. + Example: + .. code-block:: python + + from pyrogram.types import InlineQueryResultArticle, InputTextMessageContent + + await app.answer_inline_query( + inline_query_id, + results=[ + InlineQueryResultArticle( + "Title", + InputTextMessageContent("Message content"))]) """ - return self.send( - functions.messages.SetInlineBotResults( + + return await self.invoke( + raw.functions.messages.SetInlineBotResults( query_id=int(inline_query_id), - results=[r.write() for r in results], + results=[await r.write(self) for r in results], cache_time=cache_time, - gallery=None, + gallery=is_gallery or None, private=is_personal or None, next_offset=next_offset or None, - switch_pm=types.InlineBotSwitchPM( + switch_pm=raw.types.InlineBotSwitchPM( text=switch_pm_text, start_param=switch_pm_parameter ) if switch_pm_text else None diff --git a/pyrogram/methods/bots/answer_web_app_query.py b/pyrogram/methods/bots/answer_web_app_query.py new file mode 100644 index 00000000000..74f56079bb7 --- /dev/null +++ b/pyrogram/methods/bots/answer_web_app_query.py @@ -0,0 +1,53 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class AnswerWebAppQuery: + async def answer_web_app_query( + self: "pyrogram.Client", + web_app_query_id: str, + result: "types.InlineQueryResult" + ) -> "types.SentWebAppMessage": + """Set the result of an interaction with a `Web App `_ and send a + corresponding message on behalf of the user to the chat from which the query originated. + + .. include:: /_includes/usable-by/bots.rst + + Parameters: + web_app_query_id (``str``): + Unique identifier for the answered query. + + result (:obj:`~pyrogram.types.InlineQueryResult`): + A list of results for the inline query. + + Returns: + :obj:`~pyrogram.types.SentWebAppMessage`: On success the sent web app message is returned. + """ + + r = await self.invoke( + raw.functions.messages.SendWebViewResultMessage( + bot_query_id=web_app_query_id, + result=await result.write(self) + ) + ) + + return types.SentWebAppMessage._parse(r) diff --git a/pyrogram/methods/bots/delete_bot_commands.py b/pyrogram/methods/bots/delete_bot_commands.py new file mode 100644 index 00000000000..e8173d32d9d --- /dev/null +++ b/pyrogram/methods/bots/delete_bot_commands.py @@ -0,0 +1,62 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import pyrogram +from pyrogram import raw, types + + +class DeleteBotCommands: + async def delete_bot_commands( + self: "pyrogram.Client", + scope: "types.BotCommandScope" = types.BotCommandScopeDefault(), + language_code: str = "", + ) -> bool: + """Delete the list of the bot's commands for the given scope and user language. + After deletion, higher level commands will be shown to affected users. + + The commands passed will overwrite any command set previously. + This method can be used by the own bot only. + + .. include:: /_includes/usable-by/bots.rst + + Parameters: + scope (:obj:`~pyrogram.types.BotCommandScope`, *optional*): + An object describing the scope of users for which the commands are relevant. + Defaults to :obj:`~pyrogram.types.BotCommandScopeDefault`. + + language_code (``str``, *optional*): + A two-letter ISO 639-1 language code. + If empty, commands will be applied to all users from the given scope, for whose language there are no + dedicated commands. + + Returns: + ``bool``: On success, True is returned. + + Example: + .. code-block:: python + + # Delete commands + await app.delete_bot_commands() + """ + + return await self.invoke( + raw.functions.bots.ResetBotCommands( + scope=await scope.write(self), + lang_code=language_code, + ) + ) diff --git a/pyrogram/methods/bots/get_bot_commands.py b/pyrogram/methods/bots/get_bot_commands.py new file mode 100644 index 00000000000..78deff30fd1 --- /dev/null +++ b/pyrogram/methods/bots/get_bot_commands.py @@ -0,0 +1,67 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import List + +import pyrogram +from pyrogram import raw, types + + +class GetBotCommands: + async def get_bot_commands( + self: "pyrogram.Client", + scope: "types.BotCommandScope" = types.BotCommandScopeDefault(), + language_code: str = "", + ) -> List["types.BotCommand"]: + """Get the current list of the bot's commands for the given scope and user language. + Returns Array of BotCommand on success. If commands aren't set, an empty list is returned. + + The commands passed will overwrite any command set previously. + This method can be used by the own bot only. + + .. include:: /_includes/usable-by/bots.rst + + Parameters: + scope (:obj:`~pyrogram.types.BotCommandScope`, *optional*): + An object describing the scope of users for which the commands are relevant. + Defaults to :obj:`~pyrogram.types.BotCommandScopeDefault`. + + language_code (``str``, *optional*): + A two-letter ISO 639-1 language code. + If empty, commands will be applied to all users from the given scope, for whose language there are no + dedicated commands. + + Returns: + List of :obj:`~pyrogram.types.BotCommand`: On success, the list of bot commands is returned. + + Example: + .. code-block:: python + + # Get commands + commands = await app.get_bot_commands() + print(commands) + """ + + r = await self.invoke( + raw.functions.bots.GetBotCommands( + scope=await scope.write(self), + lang_code=language_code, + ) + ) + + return types.List(types.BotCommand.read(c) for c in r) diff --git a/pyrogram/methods/bots/get_bot_default_privileges.py b/pyrogram/methods/bots/get_bot_default_privileges.py new file mode 100644 index 00000000000..217d9b43842 --- /dev/null +++ b/pyrogram/methods/bots/get_bot_default_privileges.py @@ -0,0 +1,59 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Optional + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class GetBotDefaultPrivileges: + async def get_bot_default_privileges( + self: "pyrogram.Client", + for_channels: bool = None + ) -> Optional["types.ChatPrivileges"]: + """Get the current default privileges of the bot. + + .. include:: /_includes/usable-by/bots.rst + + Parameters: + for_channels (``bool``, *optional*): + Pass True to get default privileges of the bot in channels. Otherwise, default privileges of the bot + for groups and supergroups will be returned. + + Returns: + ``bool``: On success, True is returned. + + Example: + .. code-block:: python + + privileges = await app.get_bot_default_privileges() + """ + + bot_info = await self.invoke( + raw.functions.users.GetFullUser( + id=raw.types.InputUserSelf() + ) + ) + + field = "bot_broadcast_admin_rights" if for_channels else "bot_group_admin_rights" + + admin_rights = getattr(bot_info.full_user, field) + + return types.ChatPrivileges._parse(admin_rights) if admin_rights else None diff --git a/pyrogram/methods/bots/get_chat_menu_button.py b/pyrogram/methods/bots/get_chat_menu_button.py new file mode 100644 index 00000000000..9d143d58190 --- /dev/null +++ b/pyrogram/methods/bots/get_chat_menu_button.py @@ -0,0 +1,66 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class GetChatMenuButton: + async def get_chat_menu_button( + self: "pyrogram.Client", + chat_id: Union[int, str] = None, + ) -> "types.MenuButton": + """Get the current value of the bot's menu button in a private chat, or the default menu button. + + .. include:: /_includes/usable-by/bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + If not specified, default bot's menu button will be returned. + """ + + if chat_id: + r = await self.invoke( + raw.functions.bots.GetBotMenuButton( + user_id=await self.resolve_peer(chat_id), + ) + ) + else: + r = (await self.invoke( + raw.functions.users.GetFullUser( + id=raw.types.InputUserSelf() + ) + )).full_user.bot_info.menu_button + + if isinstance(r, raw.types.BotMenuButtonCommands): + return types.MenuButtonCommands() + + if isinstance(r, raw.types.BotMenuButtonDefault): + return types.MenuButtonDefault() + + if isinstance(r, raw.types.BotMenuButton): + return types.MenuButtonWebApp( + text=r.text, + web_app=types.WebAppInfo( + url=r.url + ) + ) diff --git a/pyrogram/methods/bots/get_game_high_scores.py b/pyrogram/methods/bots/get_game_high_scores.py new file mode 100644 index 00000000000..068ecab8898 --- /dev/null +++ b/pyrogram/methods/bots/get_game_high_scores.py @@ -0,0 +1,72 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union, List + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class GetGameHighScores: + async def get_game_high_scores( + self: "pyrogram.Client", + user_id: Union[int, str], + chat_id: Union[int, str], + message_id: int = None + ) -> List["types.GameHighScore"]: + """Get data for high score tables. + + .. include:: /_includes/usable-by/bots.rst + + Parameters: + user_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + chat_id (``int`` | ``str``, *optional*): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + Required if inline_message_id is not specified. + + message_id (``int``, *optional*): + Identifier of the sent message. + Required if inline_message_id is not specified. + + Returns: + List of :obj:`~pyrogram.types.GameHighScore`: On success. + + Example: + .. code-block:: python + + scores = await app.get_game_high_scores(user_id, chat_id, message_id) + print(scores) + """ + # TODO: inline_message_id + + r = await self.invoke( + raw.functions.messages.GetGameHighScores( + peer=await self.resolve_peer(chat_id), + id=message_id, + user_id=await self.resolve_peer(user_id) + ) + ) + + return types.List(types.GameHighScore._parse(self, score, r.users) for score in r.scores) diff --git a/pyrogram/methods/bots/get_inline_bot_results.py b/pyrogram/methods/bots/get_inline_bot_results.py new file mode 100644 index 00000000000..7cb0aa127dc --- /dev/null +++ b/pyrogram/methods/bots/get_inline_bot_results.py @@ -0,0 +1,92 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw +from pyrogram.errors import UnknownError + + +class GetInlineBotResults: + async def get_inline_bot_results( + self: "pyrogram.Client", + bot: Union[int, str], + query: str = "", + offset: str = "", + latitude: float = None, + longitude: float = None + ): + """Get bot results via inline queries. + You can then send a result using :meth:`~pyrogram.Client.send_inline_bot_result` + + .. include:: /_includes/usable-by/users.rst + + Parameters: + bot (``int`` | ``str``): + Unique identifier of the inline bot you want to get results from. You can specify + a @username (str) or a bot ID (int). + + query (``str``, *optional*): + Text of the query (up to 512 characters). + Defaults to "" (empty string). + + offset (``str``, *optional*): + Offset of the results to be returned. + + latitude (``float``, *optional*): + Latitude of the location. + Useful for location-based results only. + + longitude (``float``, *optional*): + Longitude of the location. + Useful for location-based results only. + + Returns: + :obj:`BotResults `: On Success. + + Raises: + TimeoutError: In case the bot fails to answer within 10 seconds. + + Example: + .. code-block:: python + + results = await app.get_inline_bot_results("pyrogrambot") + print(results) + """ + # TODO: Don't return the raw type + + try: + return await self.invoke( + raw.functions.messages.GetInlineBotResults( + bot=await self.resolve_peer(bot), + peer=raw.types.InputPeerSelf(), + query=query, + offset=offset, + geo_point=raw.types.InputGeoPoint( + lat=latitude, + long=longitude + ) if (latitude is not None and longitude is not None) else None + ) + ) + except UnknownError as e: + # TODO: Add this -503 Timeout error into the Error DB + if e.value.error_code == -503 and e.value.error_message == "Timeout": + raise TimeoutError("The inline bot didn't answer in time") from None + else: + raise e diff --git a/pyrogram/methods/bots/request_callback_answer.py b/pyrogram/methods/bots/request_callback_answer.py new file mode 100644 index 00000000000..22debcbe215 --- /dev/null +++ b/pyrogram/methods/bots/request_callback_answer.py @@ -0,0 +1,77 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw + + +class RequestCallbackAnswer: + async def request_callback_answer( + self: "pyrogram.Client", + chat_id: Union[int, str], + message_id: int, + callback_data: Union[str, bytes], + timeout: int = 10 + ): + """Request a callback answer from bots. + This is the equivalent of clicking an inline button containing callback data. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + message_id (``int``): + The message id the inline keyboard is attached on. + + callback_data (``str`` | ``bytes``): + Callback data associated with the inline button you want to get the answer from. + + timeout (``int``, *optional*): + Timeout in seconds. + + Returns: + The answer containing info useful for clients to display a notification at the top of the chat screen + or as an alert. + + Raises: + TimeoutError: In case the bot fails to answer within 10 seconds. + + Example: + .. code-block:: python + + await app.request_callback_answer(chat_id, message_id, "callback_data") + """ + + # Telegram only wants bytes, but we are allowed to pass strings too. + data = bytes(callback_data, "utf-8") if isinstance(callback_data, str) else callback_data + + return await self.invoke( + raw.functions.messages.GetBotCallbackAnswer( + peer=await self.resolve_peer(chat_id), + msg_id=message_id, + data=data + ), + retries=0, + timeout=timeout + ) diff --git a/pyrogram/methods/bots/send_game.py b/pyrogram/methods/bots/send_game.py new file mode 100644 index 00000000000..60155a6fe6e --- /dev/null +++ b/pyrogram/methods/bots/send_game.py @@ -0,0 +1,100 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class SendGame: + async def send_game( + self: "pyrogram.Client", + chat_id: Union[int, str], + game_short_name: str, + disable_notification: bool = None, + reply_to_message_id: int = None, + protect_content: bool = None, + reply_markup: Union[ + "types.InlineKeyboardMarkup", + "types.ReplyKeyboardMarkup", + "types.ReplyKeyboardRemove", + "types.ForceReply" + ] = None + ) -> "types.Message": + """Send a game. + + .. include:: /_includes/usable-by/bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + game_short_name (``str``): + Short name of the game, serves as the unique identifier for the game. Set up your games via Botfather. + + disable_notification (``bool``, *optional*): + Sends the message silently. + Users will receive a notification with no sound. + + reply_to_message_id (``int``, *optional*): + If the message is a reply, ID of the original message. + + protect_content (``bool``, *optional*): + Protects the contents of the sent message from forwarding and saving. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*): + An object for an inline keyboard. If empty, one ‘Play game_title’ button will be shown automatically. + If not empty, the first button must launch the game. + + Returns: + :obj:`~pyrogram.types.Message`: On success, the sent game message is returned. + + Example: + .. code-block:: python + + await app.send_game(chat_id, "gamename") + """ + r = await self.invoke( + raw.functions.messages.SendMedia( + peer=await self.resolve_peer(chat_id), + media=raw.types.InputMediaGame( + id=raw.types.InputGameShortName( + bot_id=raw.types.InputUserSelf(), + short_name=game_short_name + ), + ), + message="", + silent=disable_notification or None, + reply_to_msg_id=reply_to_message_id, + random_id=self.rnd_id(), + noforwards=protect_content, + reply_markup=await reply_markup.write(self) if reply_markup else None + ) + ) + + for i in r.updates: + if isinstance(i, (raw.types.UpdateNewMessage, raw.types.UpdateNewChannelMessage)): + return await types.Message._parse( + self, i.message, + {i.id: i for i in r.users}, + {i.id: i for i in r.chats} + ) diff --git a/pyrogram/methods/bots/send_inline_bot_result.py b/pyrogram/methods/bots/send_inline_bot_result.py new file mode 100644 index 00000000000..f29d8eb9cdc --- /dev/null +++ b/pyrogram/methods/bots/send_inline_bot_result.py @@ -0,0 +1,75 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw + + +class SendInlineBotResult: + async def send_inline_bot_result( + self: "pyrogram.Client", + chat_id: Union[int, str], + query_id: int, + result_id: str, + disable_notification: bool = None, + reply_to_message_id: int = None + ) -> "raw.base.Updates": + """Send an inline bot result. + Bot results can be retrieved using :meth:`~pyrogram.Client.get_inline_bot_results` + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + query_id (``int``): + Unique identifier for the answered query. + + result_id (``str``): + Unique identifier for the result that was chosen. + + disable_notification (``bool``, *optional*): + Sends the message silently. + Users will receive a notification with no sound. + + reply_to_message_id (``bool``, *optional*): + If the message is a reply, ID of the original message. + + Returns: + :obj:`~pyrogram.raw.base.Updates`: Currently, on success, a raw result is returned. + + Example: + .. code-block:: python + + await app.send_inline_bot_result(chat_id, query_id, result_id) + """ + return await self.invoke( + raw.functions.messages.SendInlineBotResult( + peer=await self.resolve_peer(chat_id), + query_id=query_id, + id=result_id, + random_id=self.rnd_id(), + silent=disable_notification or None, + reply_to_msg_id=reply_to_message_id + ) + ) diff --git a/pyrogram/methods/bots/set_bot_commands.py b/pyrogram/methods/bots/set_bot_commands.py new file mode 100644 index 00000000000..f6f7e6c9922 --- /dev/null +++ b/pyrogram/methods/bots/set_bot_commands.py @@ -0,0 +1,73 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import List + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class SetBotCommands: + async def set_bot_commands( + self: "pyrogram.Client", + commands: List["types.BotCommand"], + scope: "types.BotCommandScope" = types.BotCommandScopeDefault(), + language_code: str = "", + ) -> bool: + """Set the list of the bot's commands. + The commands passed will overwrite any command set previously. + This method can be used by the own bot only. + + .. include:: /_includes/usable-by/bots.rst + + Parameters: + commands (List of :obj:`~pyrogram.types.BotCommand`): + A list of bot commands. + At most 100 commands can be specified. + + scope (:obj:`~pyrogram.types.BotCommandScope`, *optional*): + An object describing the scope of users for which the commands are relevant. + Defaults to :obj:`~pyrogram.types.BotCommandScopeDefault`. + + language_code (``str``, *optional*): + A two-letter ISO 639-1 language code. + If empty, commands will be applied to all users from the given scope, for whose language there are no + dedicated commands. + + Returns: + ``bool``: On success, True is returned. + + Example: + .. code-block:: python + + from pyrogram.types import BotCommand + + # Set new commands + await app.set_bot_commands([ + BotCommand("start", "Start the bot"), + BotCommand("settings", "Bot settings")]) + """ + + return await self.invoke( + raw.functions.bots.SetBotCommands( + commands=[c.write() for c in commands], + scope=await scope.write(self), + lang_code=language_code, + ) + ) diff --git a/pyrogram/methods/bots/set_bot_default_privileges.py b/pyrogram/methods/bots/set_bot_default_privileges.py new file mode 100644 index 00000000000..2890ee1ae11 --- /dev/null +++ b/pyrogram/methods/bots/set_bot_default_privileges.py @@ -0,0 +1,81 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class SetBotDefaultPrivileges: + async def set_bot_default_privileges( + self: "pyrogram.Client", + privileges: "types.ChatPrivileges" = None, + for_channels: bool = None + ) -> bool: + """Change the default privileges requested by the bot when it's added as an administrator to groups or channels. + + These privileges will be suggested to users, but they are are free to modify the list before adding the bot. + + .. include:: /_includes/usable-by/bots.rst + + Parameters: + privileges (:obj:`~pyrogram.types.ChatPrivileges`): + New default privileges. None to clear. + Defaults to None. + + for_channels (``bool``, *optional*): + Pass True to change the default privileges of the bot in channels. Otherwise, the default privileges of + the bot for groups and supergroups will be changed. + + Returns: + ``bool``: On success, True is returned. + + Example: + .. code-block:: python + + from pyrogram.types import ChatPrivileges + + await app.set_bot_default_privileges( + ChatPrivileges( + can_delete_messages=True, + can_restrict_members=True + ) + ) + """ + + function = ( + raw.functions.bots.SetBotBroadcastDefaultAdminRights + if for_channels + else raw.functions.bots.SetBotGroupDefaultAdminRights + ) + + admin_rights = raw.types.ChatAdminRights( + change_info=privileges.can_change_info, + post_messages=privileges.can_post_messages, + edit_messages=privileges.can_edit_messages, + delete_messages=privileges.can_delete_messages, + ban_users=privileges.can_restrict_members, + invite_users=privileges.can_invite_users, + pin_messages=privileges.can_pin_messages, + add_admins=privileges.can_promote_members, + anonymous=privileges.is_anonymous, + manage_call=privileges.can_manage_video_chats, + other=privileges.can_manage_chat + ) if privileges else raw.types.ChatAdminRights() + + return await self.invoke(function(admin_rights=admin_rights)) diff --git a/pyrogram/methods/bots/set_chat_menu_button.py b/pyrogram/methods/bots/set_chat_menu_button.py new file mode 100644 index 00000000000..fa5af85ceb1 --- /dev/null +++ b/pyrogram/methods/bots/set_chat_menu_button.py @@ -0,0 +1,56 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class SetChatMenuButton: + async def set_chat_menu_button( + self: "pyrogram.Client", + chat_id: Union[int, str] = None, + menu_button: "types.MenuButton" = None + ) -> bool: + """Change the bot's menu button in a private chat, or the default menu button. + + .. include:: /_includes/usable-by/bots.rst + + Parameters: + chat_id (``int`` | ``str``, *optional*): + Unique identifier (int) or username (str) of the target chat. + If not specified, default bot's menu button will be changed. + + menu_button (:obj:`~pyrogram.types.MenuButton`, *optional*): + The new bot's menu button. + Defaults to :obj:`~pyrogram.types.MenuButtonDefault`. + """ + + await self.invoke( + raw.functions.bots.SetBotMenuButton( + user_id=await self.resolve_peer(chat_id or "me"), + button=( + (await menu_button.write(self)) if menu_button + else (await types.MenuButtonDefault().write(self)) + ) + ) + ) + + return True diff --git a/pyrogram/client/methods/bots/set_game_score.py b/pyrogram/methods/bots/set_game_score.py similarity index 52% rename from pyrogram/client/methods/bots/set_game_score.py rename to pyrogram/methods/bots/set_game_score.py index f5658542959..b3b58b23621 100644 --- a/pyrogram/client/methods/bots/set_game_score.py +++ b/pyrogram/methods/bots/set_game_score.py @@ -1,40 +1,42 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2019 Dan Tès +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan # -# This file is part of Pyrogram. +# 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 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. +# 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 . +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . from typing import Union import pyrogram -from pyrogram.api import functions, types -from pyrogram.client.ext import BaseClient +from pyrogram import raw +from pyrogram import types -class SetGameScore(BaseClient): - def set_game_score( - self, +class SetGameScore: + async def set_game_score( + self: "pyrogram.Client", user_id: Union[int, str], score: int, force: bool = None, disable_edit_message: bool = None, chat_id: Union[int, str] = None, message_id: int = None - ): + ) -> Union["types.Message", bool]: # inline_message_id: str = None): TODO Add inline_message_id - """Use this method to set the score of the specified user in a game. + """Set the score of the specified user in a game. + + .. include:: /_includes/usable-by/bots.rst Parameters: user_id (``int`` | ``str``): @@ -63,26 +65,33 @@ def set_game_score( Required if inline_message_id is not specified. Returns: - On success, if the message was sent by the bot, returns the edited :obj:`Message`, - otherwise returns True. + :obj:`~pyrogram.types.Message` | ``bool``: On success, if the message was sent by the bot, the edited + message is returned, True otherwise. + + Example: + .. code-block:: python + + # Set new score + await app.set_game_score(user_id, 1000) - Raises: - RPCError: In case of a Telegram RPC error. + # Force set new score + await app.set_game_score(user_id, 25, force=True) """ - r = self.send( - functions.messages.SetGameScore( - peer=self.resolve_peer(chat_id), + r = await self.invoke( + raw.functions.messages.SetGameScore( + peer=await self.resolve_peer(chat_id), score=score, id=message_id, - user_id=self.resolve_peer(user_id), + user_id=await self.resolve_peer(user_id), force=force or None, edit_message=not disable_edit_message or None ) ) for i in r.updates: - if isinstance(i, (types.UpdateEditMessage, types.UpdateEditChannelMessage)): - return pyrogram.Message._parse( + if isinstance(i, (raw.types.UpdateEditMessage, + raw.types.UpdateEditChannelMessage)): + return await types.Message._parse( self, i.message, {i.id: i for i in r.users}, {i.id: i for i in r.chats} diff --git a/pyrogram/methods/chats/__init__.py b/pyrogram/methods/chats/__init__.py new file mode 100644 index 00000000000..a2bdde08a4e --- /dev/null +++ b/pyrogram/methods/chats/__init__.py @@ -0,0 +1,101 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 .add_chat_members import AddChatMembers +from .archive_chats import ArchiveChats +from .ban_chat_member import BanChatMember +from .create_channel import CreateChannel +from .create_group import CreateGroup +from .create_supergroup import CreateSupergroup +from .delete_channel import DeleteChannel +from .delete_chat_photo import DeleteChatPhoto +from .delete_supergroup import DeleteSupergroup +from .delete_user_history import DeleteUserHistory +from .get_chat import GetChat +from .get_chat_event_log import GetChatEventLog +from .get_chat_member import GetChatMember +from .get_chat_members import GetChatMembers +from .get_chat_members_count import GetChatMembersCount +from .get_chat_online_count import GetChatOnlineCount +from .get_dialogs import GetDialogs +from .get_dialogs_count import GetDialogsCount +from .get_nearby_chats import GetNearbyChats +from .get_send_as_chats import GetSendAsChats +from .join_chat import JoinChat +from .leave_chat import LeaveChat +from .mark_chat_unread import MarkChatUnread +from .pin_chat_message import PinChatMessage +from .promote_chat_member import PromoteChatMember +from .restrict_chat_member import RestrictChatMember +from .set_administrator_title import SetAdministratorTitle +from .set_chat_description import SetChatDescription +from .set_chat_permissions import SetChatPermissions +from .set_chat_photo import SetChatPhoto +from .set_chat_protected_content import SetChatProtectedContent +from .set_chat_title import SetChatTitle +from .set_chat_username import SetChatUsername +from .set_send_as_chat import SetSendAsChat +from .set_slow_mode import SetSlowMode +from .unarchive_chats import UnarchiveChats +from .unban_chat_member import UnbanChatMember +from .unpin_all_chat_messages import UnpinAllChatMessages +from .unpin_chat_message import UnpinChatMessage + + +class Chats( + GetChat, + LeaveChat, + JoinChat, + BanChatMember, + UnbanChatMember, + RestrictChatMember, + PromoteChatMember, + GetChatMembers, + GetChatMember, + SetChatPhoto, + DeleteChatPhoto, + SetChatTitle, + SetChatDescription, + PinChatMessage, + UnpinChatMessage, + GetDialogs, + GetChatMembersCount, + SetChatUsername, + SetChatPermissions, + GetDialogsCount, + ArchiveChats, + UnarchiveChats, + CreateGroup, + CreateSupergroup, + CreateChannel, + AddChatMembers, + DeleteChannel, + DeleteSupergroup, + GetNearbyChats, + SetAdministratorTitle, + SetSlowMode, + DeleteUserHistory, + UnpinAllChatMessages, + MarkChatUnread, + GetChatEventLog, + GetChatOnlineCount, + GetSendAsChats, + SetSendAsChat, + SetChatProtectedContent +): + pass diff --git a/pyrogram/methods/chats/add_chat_members.py b/pyrogram/methods/chats/add_chat_members.py new file mode 100644 index 00000000000..b560a6affc9 --- /dev/null +++ b/pyrogram/methods/chats/add_chat_members.py @@ -0,0 +1,90 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union, List + +import pyrogram +from pyrogram import raw + + +class AddChatMembers: + async def add_chat_members( + self: "pyrogram.Client", + chat_id: Union[int, str], + user_ids: Union[Union[int, str], List[Union[int, str]]], + forward_limit: int = 100 + ) -> bool: + """Add new chat members to a group, supergroup or channel + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + The group, supergroup or channel id + + user_ids (``int`` | ``str`` | List of ``int`` or ``str``): + Users to add in the chat + You can pass an ID (int), username (str) or phone number (str). + Multiple users can be added by passing a list of IDs, usernames or phone numbers. + + forward_limit (``int``, *optional*): + How many of the latest messages you want to forward to the new members. Pass 0 to forward none of them. + Only applicable to basic groups (the argument is ignored for supergroups or channels). + Defaults to 100 (max amount). + + Returns: + ``bool``: On success, True is returned. + + Example: + .. code-block:: python + + # Add one member to a group or channel + await app.add_chat_members(chat_id, user_id) + + # Add multiple members to a group or channel + await app.add_chat_members(chat_id, [user_id1, user_id2, user_id3]) + + # Change forward_limit (for basic groups only) + await app.add_chat_members(chat_id, user_id, forward_limit=25) + """ + peer = await self.resolve_peer(chat_id) + + if not isinstance(user_ids, list): + user_ids = [user_ids] + + if isinstance(peer, raw.types.InputPeerChat): + for user_id in user_ids: + await self.invoke( + raw.functions.messages.AddChatUser( + chat_id=peer.chat_id, + user_id=await self.resolve_peer(user_id), + fwd_limit=forward_limit + ) + ) + else: + await self.invoke( + raw.functions.channels.InviteToChannel( + channel=peer, + users=[ + await self.resolve_peer(user_id) + for user_id in user_ids + ] + ) + ) + + return True diff --git a/pyrogram/methods/chats/archive_chats.py b/pyrogram/methods/chats/archive_chats.py new file mode 100644 index 00000000000..661b450cff1 --- /dev/null +++ b/pyrogram/methods/chats/archive_chats.py @@ -0,0 +1,71 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union, List + +import pyrogram +from pyrogram import raw + + +class ArchiveChats: + async def archive_chats( + self: "pyrogram.Client", + chat_ids: Union[int, str, List[Union[int, str]]], + ) -> bool: + """Archive one or more chats. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_ids (``int`` | ``str`` | List[``int``, ``str``]): + Unique identifier (int) or username (str) of the target chat. + You can also pass a list of ids (int) or usernames (str). + + Returns: + ``bool``: On success, True is returned. + + Example: + .. code-block:: python + + # Archive chat + await app.archive_chats(chat_id) + + # Archive multiple chats at once + await app.archive_chats([chat_id1, chat_id2, chat_id3]) + """ + + if not isinstance(chat_ids, list): + chat_ids = [chat_ids] + + folder_peers = [] + + for chat in chat_ids: + folder_peers.append( + raw.types.InputFolderPeer( + peer=await self.resolve_peer(chat), + folder_id=1 + ) + ) + + await self.invoke( + raw.functions.folders.EditPeerFolders( + folder_peers=folder_peers + ) + ) + + return True diff --git a/pyrogram/methods/chats/ban_chat_member.py b/pyrogram/methods/chats/ban_chat_member.py new file mode 100644 index 00000000000..635c48b5da6 --- /dev/null +++ b/pyrogram/methods/chats/ban_chat_member.py @@ -0,0 +1,111 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 datetime import datetime +from typing import Union + +import pyrogram +from pyrogram import raw, utils +from pyrogram import types + + +class BanChatMember: + async def ban_chat_member( + self: "pyrogram.Client", + chat_id: Union[int, str], + user_id: Union[int, str], + until_date: datetime = utils.zero_datetime() + ) -> Union["types.Message", bool]: + """Ban a user from a group, a supergroup or a channel. + In the case of supergroups and channels, the user will not be able to return to the group on their own using + invite links, etc., unless unbanned first. You must be an administrator in the chat for this to work and must + have the appropriate admin rights. + + Note: + In regular groups (non-supergroups), this method will only work if the "All Members Are Admins" setting is + off in the target group. Otherwise members may only be removed by the group's creator or by the member + that added them. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + user_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target user. + For a contact that exists in your Telegram address book you can use his phone number (str). + + until_date (:py:obj:`~datetime.datetime`, *optional*): + Date when the user will be unbanned. + If user is banned for more than 366 days or less than 30 seconds from the current time they are + considered to be banned forever. Defaults to epoch (ban forever). + + Returns: + :obj:`~pyrogram.types.Message` | ``bool``: On success, a service message will be returned (when applicable), + otherwise, in case a message object couldn't be returned, True is returned. + + Example: + .. code-block:: python + + from datetime import datetime, timedelta + + # Ban chat member forever + await app.ban_chat_member(chat_id, user_id) + + # Ban chat member and automatically unban after 24h + await app.ban_chat_member(chat_id, user_id, datetime.now() + timedelta(days=1)) + """ + chat_peer = await self.resolve_peer(chat_id) + user_peer = await self.resolve_peer(user_id) + + if isinstance(chat_peer, raw.types.InputPeerChannel): + r = await self.invoke( + raw.functions.channels.EditBanned( + channel=chat_peer, + participant=user_peer, + banned_rights=raw.types.ChatBannedRights( + until_date=utils.datetime_to_timestamp(until_date), + view_messages=True, + send_messages=True, + send_media=True, + send_stickers=True, + send_gifs=True, + send_games=True, + send_inline=True, + embed_links=True + ) + ) + ) + else: + r = await self.invoke( + raw.functions.messages.DeleteChatUser( + chat_id=abs(chat_id), + user_id=user_peer + ) + ) + + for i in r.updates: + if isinstance(i, (raw.types.UpdateNewMessage, raw.types.UpdateNewChannelMessage)): + return await types.Message._parse( + self, i.message, + {i.id: i for i in r.users}, + {i.id: i for i in r.chats} + ) + else: + return True diff --git a/pyrogram/methods/chats/create_channel.py b/pyrogram/methods/chats/create_channel.py new file mode 100644 index 00000000000..292fa97dd75 --- /dev/null +++ b/pyrogram/methods/chats/create_channel.py @@ -0,0 +1,56 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class CreateChannel: + async def create_channel( + self: "pyrogram.Client", + title: str, + description: str = "" + ) -> "types.Chat": + """Create a new broadcast channel. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + title (``str``): + The channel title. + + description (``str``, *optional*): + The channel description. + + Returns: + :obj:`~pyrogram.types.Chat`: On success, a chat object is returned. + + Example: + .. code-block:: python + + await app.create_channel("Channel Title", "Channel Description") + """ + r = await self.invoke( + raw.functions.channels.CreateChannel( + title=title, + about=description, + broadcast=True + ) + ) + + return types.Chat._parse_chat(self, r.chats[0]) diff --git a/pyrogram/methods/chats/create_group.py b/pyrogram/methods/chats/create_group.py new file mode 100644 index 00000000000..027315de2af --- /dev/null +++ b/pyrogram/methods/chats/create_group.py @@ -0,0 +1,67 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union, List + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class CreateGroup: + async def create_group( + self: "pyrogram.Client", + title: str, + users: Union[Union[int, str], List[Union[int, str]]] + ) -> "types.Chat": + """Create a new basic group. + + .. note:: + + If you want to create a new supergroup, use :meth:`~pyrogram.Client.create_supergroup` instead. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + title (``str``): + The group title. + + users (``int`` | ``str`` | List of ``int`` or ``str``): + Users to create a chat with. + You must pass at least one user using their IDs (int), usernames (str) or phone numbers (str). + Multiple users can be invited by passing a list of IDs, usernames or phone numbers. + + Returns: + :obj:`~pyrogram.types.Chat`: On success, a chat object is returned. + + Example: + .. code-block:: python + + await app.create_group("Group Title", user_id) + """ + if not isinstance(users, list): + users = [users] + + r = await self.invoke( + raw.functions.messages.CreateChat( + title=title, + users=[await self.resolve_peer(u) for u in users] + ) + ) + + return types.Chat._parse_chat(self, r.chats[0]) diff --git a/pyrogram/methods/chats/create_supergroup.py b/pyrogram/methods/chats/create_supergroup.py new file mode 100644 index 00000000000..d1cbbbd8907 --- /dev/null +++ b/pyrogram/methods/chats/create_supergroup.py @@ -0,0 +1,60 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class CreateSupergroup: + async def create_supergroup( + self: "pyrogram.Client", + title: str, + description: str = "" + ) -> "types.Chat": + """Create a new supergroup. + + .. note:: + + If you want to create a new basic group, use :meth:`~pyrogram.Client.create_group` instead. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + title (``str``): + The supergroup title. + + description (``str``, *optional*): + The supergroup description. + + Returns: + :obj:`~pyrogram.types.Chat`: On success, a chat object is returned. + + Example: + .. code-block:: python + + await app.create_supergroup("Supergroup Title", "Supergroup Description") + """ + r = await self.invoke( + raw.functions.channels.CreateChannel( + title=title, + about=description, + megagroup=True + ) + ) + + return types.Chat._parse_chat(self, r.chats[0]) diff --git a/pyrogram/methods/chats/delete_channel.py b/pyrogram/methods/chats/delete_channel.py new file mode 100644 index 00000000000..374d89a6f46 --- /dev/null +++ b/pyrogram/methods/chats/delete_channel.py @@ -0,0 +1,52 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw + + +class DeleteChannel: + async def delete_channel( + self: "pyrogram.Client", + chat_id: Union[int, str] + ) -> bool: + """Delete a channel. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + The id of the channel to be deleted. + + Returns: + ``bool``: On success, True is returned. + + Example: + .. code-block:: python + + await app.delete_channel(channel_id) + """ + await self.invoke( + raw.functions.channels.DeleteChannel( + channel=await self.resolve_peer(chat_id) + ) + ) + + return True diff --git a/pyrogram/methods/chats/delete_chat_photo.py b/pyrogram/methods/chats/delete_chat_photo.py new file mode 100644 index 00000000000..1984bc0cc2b --- /dev/null +++ b/pyrogram/methods/chats/delete_chat_photo.py @@ -0,0 +1,70 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw + + +class DeleteChatPhoto: + async def delete_chat_photo( + self: "pyrogram.Client", + chat_id: Union[int, str] + ) -> bool: + """Delete a chat photo. + + You must be an administrator in the chat for this to work and must have the appropriate admin rights. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + Returns: + ``bool``: True on success. + + Raises: + ValueError: if a chat_id belongs to user. + + Example: + .. code-block:: python + + await app.delete_chat_photo(chat_id) + """ + peer = await self.resolve_peer(chat_id) + + if isinstance(peer, raw.types.InputPeerChat): + await self.invoke( + raw.functions.messages.EditChatPhoto( + chat_id=peer.chat_id, + photo=raw.types.InputChatPhotoEmpty() + ) + ) + elif isinstance(peer, raw.types.InputPeerChannel): + await self.invoke( + raw.functions.channels.EditPhoto( + channel=peer, + photo=raw.types.InputChatPhotoEmpty() + ) + ) + else: + raise ValueError(f'The chat_id "{chat_id}" belongs to a user') + + return True diff --git a/pyrogram/methods/chats/delete_supergroup.py b/pyrogram/methods/chats/delete_supergroup.py new file mode 100644 index 00000000000..ebd60befa85 --- /dev/null +++ b/pyrogram/methods/chats/delete_supergroup.py @@ -0,0 +1,52 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw + + +class DeleteSupergroup: + async def delete_supergroup( + self: "pyrogram.Client", + chat_id: Union[int, str] + ) -> bool: + """Delete a supergroup. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + The id of the supergroup to be deleted. + + Returns: + ``bool``: On success, True is returned. + + Example: + .. code-block:: python + + await app.delete_supergroup(supergroup_id) + """ + await self.invoke( + raw.functions.channels.DeleteChannel( + channel=await self.resolve_peer(chat_id) + ) + ) + + return True diff --git a/pyrogram/methods/chats/delete_user_history.py b/pyrogram/methods/chats/delete_user_history.py new file mode 100644 index 00000000000..7769c7c3c08 --- /dev/null +++ b/pyrogram/methods/chats/delete_user_history.py @@ -0,0 +1,55 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw + + +class DeleteUserHistory: + async def delete_user_history( + self: "pyrogram.Client", + chat_id: Union[int, str], + user_id: Union[int, str], + ) -> bool: + """Delete all messages sent by a certain user in a supergroup. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + user_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the user whose messages will be deleted. + + Returns: + ``bool``: True on success, False otherwise. + """ + + r = await self.invoke( + raw.functions.channels.DeleteParticipantHistory( + channel=await self.resolve_peer(chat_id), + participant=await self.resolve_peer(user_id) + ) + ) + + # Deleting messages you don't have right onto won't raise any error. + # Check for pts_count, which is 0 in case deletes fail. + return bool(r.pts_count) diff --git a/pyrogram/methods/chats/get_chat.py b/pyrogram/methods/chats/get_chat.py new file mode 100644 index 00000000000..e2289935a92 --- /dev/null +++ b/pyrogram/methods/chats/get_chat.py @@ -0,0 +1,87 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw +from pyrogram import types +from pyrogram import utils + + +class GetChat: + async def get_chat( + self: "pyrogram.Client", + chat_id: Union[int, str] + ) -> Union["types.Chat", "types.ChatPreview"]: + """Get up to date information about a chat. + + Information include current name of the user for one-on-one conversations, current username of a user, group or + channel, etc. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + Unique identifier for the target chat in form of a *t.me/joinchat/* link, identifier (int) or username + of the target channel/supergroup (in the format @username). + + Returns: + :obj:`~pyrogram.types.Chat` | :obj:`~pyrogram.types.ChatPreview`: On success, if you've already joined the chat, a chat object is returned, + otherwise, a chat preview object is returned. + + Raises: + ValueError: In case the chat invite link points to a chat you haven't joined yet. + + Example: + .. code-block:: python + + chat = await app.get_chat("pyrogram") + print(chat) + """ + match = self.INVITE_LINK_RE.match(str(chat_id)) + + if match: + r = await self.invoke( + raw.functions.messages.CheckChatInvite( + hash=match.group(1) + ) + ) + + if isinstance(r, raw.types.ChatInvite): + return types.ChatPreview._parse(self, r) + + await self.fetch_peers([r.chat]) + + if isinstance(r.chat, raw.types.Chat): + chat_id = -r.chat.id + + if isinstance(r.chat, raw.types.Channel): + chat_id = utils.get_channel_id(r.chat.id) + + peer = await self.resolve_peer(chat_id) + + if isinstance(peer, raw.types.InputPeerChannel): + r = await self.invoke(raw.functions.channels.GetFullChannel(channel=peer)) + elif isinstance(peer, (raw.types.InputPeerUser, raw.types.InputPeerSelf)): + r = await self.invoke(raw.functions.users.GetFullUser(id=peer)) + else: + r = await self.invoke(raw.functions.messages.GetFullChat(chat_id=peer.chat_id)) + + return await types.Chat._parse_full(self, r) diff --git a/pyrogram/methods/chats/get_chat_event_log.py b/pyrogram/methods/chats/get_chat_event_log.py new file mode 100644 index 00000000000..06fa01c8497 --- /dev/null +++ b/pyrogram/methods/chats/get_chat_event_log.py @@ -0,0 +1,109 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union, List, AsyncGenerator, Optional + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class GetChatEventLog: + async def get_chat_event_log( + self: "pyrogram.Client", + chat_id: Union[int, str], + query: str = "", + offset_id: int = 0, + limit: int = 0, + filters: "types.ChatEventFilter" = None, + user_ids: List[Union[int, str]] = None + ) -> Optional[AsyncGenerator["types.ChatEvent", None]]: + """Get the actions taken by chat members and administrators in the last 48h. + + Only available for supergroups and channels. Requires administrator rights. + Results are returned in reverse chronological order (i.e., newest first). + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + query (``str``, *optional*): + Search query to filter events based on text. + By default, an empty query is applied and all events will be returned. + + offset_id (``int``, *optional*): + Offset event identifier from which to start returning results. + By default, no offset is applied and events will be returned starting from the latest. + + limit (``int``, *optional*): + Maximum amount of events to be returned. + By default, all events will be returned. + + filters (:obj:`~pyrogram.types.ChatEventFilter`, *optional*): + The types of events to return. + By default, all types will be returned. + + user_ids (List of ``int`` | ``str``, *optional*): + User identifiers (int) or usernames (str) by which to filter events. + By default, events relating to all users will be returned. + + Yields: + :obj:`~pyrogram.types.ChatEvent` objects. + + Example: + .. code-block:: python + + async for event in app.get_chat_event_log(chat_id): + print(event) + """ + current = 0 + total = abs(limit) or (1 << 31) + limit = min(100, total) + + while True: + r: raw.base.channels.AdminLogResults = await self.invoke( + raw.functions.channels.GetAdminLog( + channel=await self.resolve_peer(chat_id), + q=query, + min_id=0, + max_id=offset_id, + limit=limit, + events_filter=filters.write() if filters else None, + admins=( + [await self.resolve_peer(i) for i in user_ids] + if user_ids is not None + else user_ids + ) + ) + ) + + if not r.events: + return + + last = r.events[-1] + offset_id = last.id + + for event in r.events: + yield await types.ChatEvent._parse(self, event, r.users, r.chats) + + current += 1 + + if current >= total: + return diff --git a/pyrogram/methods/chats/get_chat_member.py b/pyrogram/methods/chats/get_chat_member.py new file mode 100644 index 00000000000..53d2faad830 --- /dev/null +++ b/pyrogram/methods/chats/get_chat_member.py @@ -0,0 +1,92 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw +from pyrogram import types +from pyrogram.errors import UserNotParticipant + + +class GetChatMember: + async def get_chat_member( + self: "pyrogram.Client", + chat_id: Union[int, str], + user_id: Union[int, str] + ) -> "types.ChatMember": + """Get information about one member of a chat. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + user_id (``int`` | ``str``):: + Unique identifier (int) or username (str) of the target user. + For you yourself you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + Returns: + :obj:`~pyrogram.types.ChatMember`: On success, a chat member is returned. + + Example: + .. code-block:: python + + member = await app.get_chat_member(chat_id, "me") + print(member) + """ + chat = await self.resolve_peer(chat_id) + user = await self.resolve_peer(user_id) + + if isinstance(chat, raw.types.InputPeerChat): + r = await self.invoke( + raw.functions.messages.GetFullChat( + chat_id=chat.chat_id + ) + ) + + members = getattr(r.full_chat.participants, "participants", []) + users = {i.id: i for i in r.users} + + for member in members: + member = types.ChatMember._parse(self, member, users, {}) + + if isinstance(user, raw.types.InputPeerSelf): + if member.user.is_self: + return member + else: + if member.user.id == user.user_id: + return member + else: + raise UserNotParticipant + elif isinstance(chat, raw.types.InputPeerChannel): + r = await self.invoke( + raw.functions.channels.GetParticipant( + channel=chat, + participant=user + ) + ) + + users = {i.id: i for i in r.users} + chats = {i.id: i for i in r.chats} + + return types.ChatMember._parse(self, r.participant, users, chats) + else: + raise ValueError(f'The chat_id "{chat_id}" belongs to a user') diff --git a/pyrogram/methods/chats/get_chat_members.py b/pyrogram/methods/chats/get_chat_members.py new file mode 100644 index 00000000000..49fb0a094d1 --- /dev/null +++ b/pyrogram/methods/chats/get_chat_members.py @@ -0,0 +1,158 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import logging +from typing import Union, Optional, AsyncGenerator + +import pyrogram +from pyrogram import raw, types, enums + +log = logging.getLogger(__name__) + + +async def get_chunk( + client: "pyrogram.Client", + chat_id: Union[int, str], + offset: int, + filter: "enums.ChatMembersFilter", + limit: int, + query: str, +): + is_queryable = filter in [enums.ChatMembersFilter.SEARCH, + enums.ChatMembersFilter.BANNED, + enums.ChatMembersFilter.RESTRICTED] + + filter = filter.value(q=query) if is_queryable else filter.value() + + r = await client.invoke( + raw.functions.channels.GetParticipants( + channel=await client.resolve_peer(chat_id), + filter=filter, + offset=offset, + limit=limit, + hash=0 + ), + sleep_threshold=60 + ) + + members = r.participants + users = {u.id: u for u in r.users} + chats = {c.id: c for c in r.chats} + + return [types.ChatMember._parse(client, member, users, chats) for member in members] + + +class GetChatMembers: + async def get_chat_members( + self: "pyrogram.Client", + chat_id: Union[int, str], + query: str = "", + limit: int = 0, + filter: "enums.ChatMembersFilter" = enums.ChatMembersFilter.SEARCH + ) -> Optional[AsyncGenerator["types.ChatMember", None]]: + """Get the members list of a chat. + + A chat can be either a basic group, a supergroup or a channel. + Requires administrator rights in channels. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + query (``str``, *optional*): + Query string to filter members based on their display names and usernames. + Only applicable to supergroups and channels. Defaults to "" (empty string). + A query string is applicable only for :obj:`~pyrogram.enums.ChatMembersFilter.SEARCH`, + :obj:`~pyrogram.enums.ChatMembersFilter.BANNED` and :obj:`~pyrogram.enums.ChatMembersFilter.RESTRICTED` + filters only. + + limit (``int``, *optional*): + Limits the number of members to be retrieved. + + filter (:obj:`~pyrogram.enums.ChatMembersFilter`, *optional*): + Filter used to select the kind of members you want to retrieve. Only applicable for supergroups + and channels. + + Returns: + ``Generator``: On success, a generator yielding :obj:`~pyrogram.types.ChatMember` objects is returned. + + Example: + .. code-block:: python + + from pyrogram import enums + + # Get members + async for member in app.get_chat_members(chat_id): + print(member) + + # Get administrators + administrators = [] + async for m in app.get_chat_members(chat_id, filter=enums.ChatMembersFilter.ADMINISTRATORS): + administrators.append(m) + + # Get bots + bots = [] + async for m in app.get_chat_members(chat_id, filter=enums.ChatMembersFilter.BOTS): + bots.append(m) + """ + peer = await self.resolve_peer(chat_id) + + if isinstance(peer, raw.types.InputPeerChat): + r = await self.invoke( + raw.functions.messages.GetFullChat( + chat_id=peer.chat_id + ) + ) + + members = getattr(r.full_chat.participants, "participants", []) + users = {i.id: i for i in r.users} + + for member in members: + yield types.ChatMember._parse(self, member, users, {}) + + return + + current = 0 + offset = 0 + total = abs(limit) or (1 << 31) - 1 + limit = min(200, total) + + while True: + members = await get_chunk( + client=self, + chat_id=chat_id, + offset=offset, + filter=filter, + limit=limit, + query=query + ) + + if not members: + return + + offset += len(members) + + for member in members: + yield member + + current += 1 + + if current >= total: + return diff --git a/pyrogram/methods/chats/get_chat_members_count.py b/pyrogram/methods/chats/get_chat_members_count.py new file mode 100644 index 00000000000..26807028335 --- /dev/null +++ b/pyrogram/methods/chats/get_chat_members_count.py @@ -0,0 +1,69 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw + + +class GetChatMembersCount: + async def get_chat_members_count( + self: "pyrogram.Client", + chat_id: Union[int, str] + ) -> int: + """Get the number of members in a chat. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + Returns: + ``int``: On success, the chat members count is returned. + + Raises: + ValueError: In case a chat id belongs to user. + + Example: + .. code-block:: python + + count = await app.get_chat_members_count(chat_id) + print(count) + """ + peer = await self.resolve_peer(chat_id) + + if isinstance(peer, raw.types.InputPeerChat): + r = await self.invoke( + raw.functions.messages.GetChats( + id=[peer.chat_id] + ) + ) + + return r.chats[0].participants_count + elif isinstance(peer, raw.types.InputPeerChannel): + r = await self.invoke( + raw.functions.channels.GetFullChannel( + channel=peer + ) + ) + + return r.full_chat.participants_count + else: + raise ValueError(f'The chat_id "{chat_id}" belongs to a user') diff --git a/pyrogram/methods/chats/get_chat_online_count.py b/pyrogram/methods/chats/get_chat_online_count.py new file mode 100644 index 00000000000..6ecd5727dd2 --- /dev/null +++ b/pyrogram/methods/chats/get_chat_online_count.py @@ -0,0 +1,51 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw + + +class GetChatOnlineCount: + async def get_chat_online_count( + self: "pyrogram.Client", + chat_id: Union[int, str] + ) -> int: + """Get the number of members that are currently online in a chat. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + Returns: + ``int``: On success, the chat members online count is returned. + + Example: + .. code-block:: python + + online = await app.get_chat_online_count(chat_id) + print(online) + """ + return (await self.invoke( + raw.functions.messages.GetOnlines( + peer=await self.resolve_peer(chat_id) + ) + )).onlines diff --git a/pyrogram/methods/chats/get_dialogs.py b/pyrogram/methods/chats/get_dialogs.py new file mode 100644 index 00000000000..3509ccbcca3 --- /dev/null +++ b/pyrogram/methods/chats/get_dialogs.py @@ -0,0 +1,104 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import AsyncGenerator, Optional + +import pyrogram +from pyrogram import types, raw, utils + + +class GetDialogs: + async def get_dialogs( + self: "pyrogram.Client", + limit: int = 0 + ) -> Optional[AsyncGenerator["types.Dialog", None]]: + """Get a user's dialogs sequentially. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + limit (``int``, *optional*): + Limits the number of dialogs to be retrieved. + By default, no limit is applied and all dialogs are returned. + + Returns: + ``Generator``: A generator yielding :obj:`~pyrogram.types.Dialog` objects. + + Example: + .. code-block:: python + + # Iterate through all dialogs + async for dialog in app.get_dialogs(): + print(dialog.chat.first_name or dialog.chat.title) + """ + current = 0 + total = limit or (1 << 31) - 1 + limit = min(100, total) + + offset_date = 0 + offset_id = 0 + offset_peer = raw.types.InputPeerEmpty() + + while True: + r = await self.invoke( + raw.functions.messages.GetDialogs( + offset_date=offset_date, + offset_id=offset_id, + offset_peer=offset_peer, + limit=limit, + hash=0 + ), + sleep_threshold=60 + ) + + users = {i.id: i for i in r.users} + chats = {i.id: i for i in r.chats} + + messages = {} + + for message in r.messages: + if isinstance(message, raw.types.MessageEmpty): + continue + + chat_id = utils.get_peer_id(message.peer_id) + messages[chat_id] = await types.Message._parse(self, message, users, chats) + + dialogs = [] + + for dialog in r.dialogs: + if not isinstance(dialog, raw.types.Dialog): + continue + + dialogs.append(types.Dialog._parse(self, dialog, messages, users, chats)) + + if not dialogs: + return + + last = dialogs[-1] + + offset_id = last.top_message.id + offset_date = utils.datetime_to_timestamp(last.top_message.date) + offset_peer = await self.resolve_peer(last.chat.id) + + for dialog in dialogs: + yield dialog + + current += 1 + + if current >= total: + return diff --git a/pyrogram/methods/chats/get_dialogs_count.py b/pyrogram/methods/chats/get_dialogs_count.py new file mode 100644 index 00000000000..ae22eb5cffa --- /dev/null +++ b/pyrogram/methods/chats/get_dialogs_count.py @@ -0,0 +1,63 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import pyrogram +from pyrogram import raw + + +class GetDialogsCount: + async def get_dialogs_count( + self: "pyrogram.Client", + pinned_only: bool = False + ) -> int: + """Get the total count of your dialogs. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + pinned_only (``bool``, *optional*): + Pass True if you want to count only pinned dialogs. + Defaults to False. + + Returns: + ``int``: On success, the dialogs count is returned. + + Example: + .. code-block:: python + + count = await app.get_dialogs_count() + print(count) + """ + + if pinned_only: + return len((await self.invoke(raw.functions.messages.GetPinnedDialogs(folder_id=0))).dialogs) + else: + r = await self.invoke( + raw.functions.messages.GetDialogs( + offset_date=0, + offset_id=0, + offset_peer=raw.types.InputPeerEmpty(), + limit=1, + hash=0 + ) + ) + + if isinstance(r, raw.types.messages.Dialogs): + return len(r.dialogs) + else: + return r.count diff --git a/pyrogram/methods/chats/get_nearby_chats.py b/pyrogram/methods/chats/get_nearby_chats.py new file mode 100644 index 00000000000..8d1163ac9d2 --- /dev/null +++ b/pyrogram/methods/chats/get_nearby_chats.py @@ -0,0 +1,78 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import List + +import pyrogram +from pyrogram import raw +from pyrogram import types +from pyrogram import utils + + +class GetNearbyChats: + async def get_nearby_chats( + self: "pyrogram.Client", + latitude: float, + longitude: float + ) -> List["types.Chat"]: + """Get nearby chats. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + latitude (``float``): + Latitude of the location. + + longitude (``float``): + Longitude of the location. + + Returns: + List of :obj:`~pyrogram.types.Chat`: On success, a list of nearby chats is returned. + + Example: + .. code-block:: python + + chats = await app.get_nearby_chats(latitude, longitude) + print(chats) + """ + + r = await self.invoke( + raw.functions.contacts.GetLocated( + geo_point=raw.types.InputGeoPoint( + lat=latitude, + long=longitude + ) + ) + ) + + if not r.updates: + return [] + + chats = types.List([types.Chat._parse_chat(self, chat) for chat in r.chats]) + peers = r.updates[0].peers + + for peer in peers: + if isinstance(peer.peer, raw.types.PeerChannel): + chat_id = utils.get_channel_id(peer.peer.channel_id) + + for chat in chats: + if chat.id == chat_id: + chat.distance = peer.distance + break + + return chats diff --git a/pyrogram/methods/chats/get_send_as_chats.py b/pyrogram/methods/chats/get_send_as_chats.py new file mode 100644 index 00000000000..c9a358c3988 --- /dev/null +++ b/pyrogram/methods/chats/get_send_as_chats.py @@ -0,0 +1,65 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import List, Union + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class GetSendAsChats: + async def get_send_as_chats( + self: "pyrogram.Client", + chat_id: Union[int, str] + ) -> List["types.Chat"]: + """Get the list of "send_as" chats available. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + Returns: + List[:obj:`~pyrogram.types.Chat`]: The list of chats. + + Example: + .. code-block:: python + + chats = await app.get_send_as_chats(chat_id) + print(chats) + """ + r = await self.invoke( + raw.functions.channels.GetSendAs( + peer=await self.resolve_peer(chat_id) + ) + ) + + users = {u.id: u for u in r.users} + chats = {c.id: c for c in r.chats} + + send_as_chats = types.List() + + for p in r.peers: + if isinstance(p, raw.types.PeerUser): + send_as_chats.append(types.Chat._parse_chat(self, users[p.user_id])) + else: + send_as_chats.append(types.Chat._parse_chat(self, chats[p.channel_id])) + + return send_as_chats diff --git a/pyrogram/methods/chats/join_chat.py b/pyrogram/methods/chats/join_chat.py new file mode 100644 index 00000000000..2ef82cff318 --- /dev/null +++ b/pyrogram/methods/chats/join_chat.py @@ -0,0 +1,74 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class JoinChat: + async def join_chat( + self: "pyrogram.Client", + chat_id: Union[int, str] + ) -> "types.Chat": + """Join a group chat or channel. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier for the target chat in form of a *t.me/joinchat/* link, a username of the target + channel/supergroup (in the format @username) or a chat id of a linked chat (channel or supergroup). + + Returns: + :obj:`~pyrogram.types.Chat`: On success, a chat object is returned. + + Example: + .. code-block:: python + + # Join chat via invite link + await app.join_chat("https://t.me/+AbCdEf0123456789") + + # Join chat via username + await app.join_chat("pyrogram") + + # Join a linked chat + await app.join_chat(app.get_chat("pyrogram").linked_chat.id) + """ + match = self.INVITE_LINK_RE.match(str(chat_id)) + + if match: + chat = await self.invoke( + raw.functions.messages.ImportChatInvite( + hash=match.group(1) + ) + ) + if isinstance(chat.chats[0], raw.types.Chat): + return types.Chat._parse_chat_chat(self, chat.chats[0]) + elif isinstance(chat.chats[0], raw.types.Channel): + return types.Chat._parse_channel_chat(self, chat.chats[0]) + else: + chat = await self.invoke( + raw.functions.channels.JoinChannel( + channel=await self.resolve_peer(chat_id) + ) + ) + + return types.Chat._parse_channel_chat(self, chat.chats[0]) diff --git a/pyrogram/methods/chats/leave_chat.py b/pyrogram/methods/chats/leave_chat.py new file mode 100644 index 00000000000..b974e387712 --- /dev/null +++ b/pyrogram/methods/chats/leave_chat.py @@ -0,0 +1,77 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw + + +class LeaveChat: + async def leave_chat( + self: "pyrogram.Client", + chat_id: Union[int, str], + delete: bool = False + ): + """Leave a group chat or channel. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier for the target chat or username of the target channel/supergroup + (in the format @username). + + delete (``bool``, *optional*): + Deletes the group chat dialog after leaving (for simple group chats, not supergroups). + Defaults to False. + + Example: + .. code-block:: python + + # Leave chat or channel + await app.leave_chat(chat_id) + + # Leave basic chat and also delete the dialog + await app.leave_chat(chat_id, delete=True) + """ + peer = await self.resolve_peer(chat_id) + + if isinstance(peer, raw.types.InputPeerChannel): + return await self.invoke( + raw.functions.channels.LeaveChannel( + channel=await self.resolve_peer(chat_id) + ) + ) + elif isinstance(peer, raw.types.InputPeerChat): + r = await self.invoke( + raw.functions.messages.DeleteChatUser( + chat_id=peer.chat_id, + user_id=raw.types.InputUserSelf() + ) + ) + + if delete: + await self.invoke( + raw.functions.messages.DeleteHistory( + peer=peer, + max_id=0 + ) + ) + + return r diff --git a/pyrogram/methods/chats/mark_chat_unread.py b/pyrogram/methods/chats/mark_chat_unread.py new file mode 100644 index 00000000000..45aec2f8788 --- /dev/null +++ b/pyrogram/methods/chats/mark_chat_unread.py @@ -0,0 +1,47 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw + + +class MarkChatUnread: + async def mark_chat_unread( + self: "pyrogram.Client", + chat_id: Union[int, str], + ) -> bool: + """Mark a chat as unread. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + Returns: + ``bool``: On success, True is returned. + """ + + return await self.invoke( + raw.functions.messages.MarkDialogUnread( + peer=await self.resolve_peer(chat_id), + unread=True + ) + ) diff --git a/pyrogram/methods/chats/pin_chat_message.py b/pyrogram/methods/chats/pin_chat_message.py new file mode 100644 index 00000000000..8ec06e7b13f --- /dev/null +++ b/pyrogram/methods/chats/pin_chat_message.py @@ -0,0 +1,81 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw, types + + +class PinChatMessage: + async def pin_chat_message( + self: "pyrogram.Client", + chat_id: Union[int, str], + message_id: int, + disable_notification: bool = False, + both_sides: bool = False, + ) -> "types.Message": + """Pin a message in a group, channel or your own chat. + You must be an administrator in the chat for this to work and must have the "can_pin_messages" admin right in + the supergroup or "can_edit_messages" admin right in the channel. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + message_id (``int``): + Identifier of a message to pin. + + disable_notification (``bool``, *optional*): + Pass True, if it is not necessary to send a notification to all chat members about the new pinned + message. Notifications are always disabled in channels. + + both_sides (``bool``, *optional*): + Pass True to pin the message for both sides (you and recipient). + Applicable to private chats only. Defaults to False. + + Returns: + :obj:`~pyrogram.types.Message`: On success, the service message is returned. + + Example: + .. code-block:: python + + # Pin with notification + await app.pin_chat_message(chat_id, message_id) + + # Pin without notification + await app.pin_chat_message(chat_id, message_id, disable_notification=True) + """ + r = await self.invoke( + raw.functions.messages.UpdatePinnedMessage( + peer=await self.resolve_peer(chat_id), + id=message_id, + silent=disable_notification or None, + pm_oneside=not both_sides or None + ) + ) + + users = {u.id: u for u in r.users} + chats = {c.id: c for c in r.chats} + + for i in r.updates: + if isinstance(i, (raw.types.UpdateNewMessage, + raw.types.UpdateNewChannelMessage)): + return await types.Message._parse(self, i.message, users, chats) diff --git a/pyrogram/methods/chats/promote_chat_member.py b/pyrogram/methods/chats/promote_chat_member.py new file mode 100644 index 00000000000..e4539037735 --- /dev/null +++ b/pyrogram/methods/chats/promote_chat_member.py @@ -0,0 +1,101 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw, types, errors + + +class PromoteChatMember: + async def promote_chat_member( + self: "pyrogram.Client", + chat_id: Union[int, str], + user_id: Union[int, str], + privileges: "types.ChatPrivileges" = None, + ) -> bool: + """Promote or demote a user in a supergroup or a channel. + + You must be an administrator in the chat for this to work and must have the appropriate admin rights. + Pass False for all boolean parameters to demote a user. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + user_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target user. + For a contact that exists in your Telegram address book you can use his phone number (str). + + privileges (:obj:`~pyrogram.types.ChatPrivileges`, *optional*): + New user privileges. + + Returns: + ``bool``: True on success. + + Example: + .. code-block:: python + + # Promote chat member to admin + await app.promote_chat_member(chat_id, user_id) + """ + chat_id = await self.resolve_peer(chat_id) + user_id = await self.resolve_peer(user_id) + + # See Chat.promote_member for the reason of this (instead of setting types.ChatPrivileges() as default arg). + if privileges is None: + privileges = types.ChatPrivileges() + + try: + raw_chat_member = (await self.invoke( + raw.functions.channels.GetParticipant( + channel=chat_id, + participant=user_id + ) + )).participant + except errors.RPCError: + raw_chat_member = None + + rank = None + if isinstance(raw_chat_member, raw.types.ChannelParticipantAdmin): + rank = raw_chat_member.rank + + await self.invoke( + raw.functions.channels.EditAdmin( + channel=chat_id, + user_id=user_id, + admin_rights=raw.types.ChatAdminRights( + anonymous=privileges.is_anonymous, + change_info=privileges.can_change_info, + post_messages=privileges.can_post_messages, + edit_messages=privileges.can_edit_messages, + delete_messages=privileges.can_delete_messages, + ban_users=privileges.can_restrict_members, + invite_users=privileges.can_invite_users, + pin_messages=privileges.can_pin_messages, + add_admins=privileges.can_promote_members, + manage_call=privileges.can_manage_video_chats, + other=privileges.can_manage_chat + ), + rank=rank or "" + ) + ) + + return True diff --git a/pyrogram/methods/chats/restrict_chat_member.py b/pyrogram/methods/chats/restrict_chat_member.py new file mode 100644 index 00000000000..6e42b364ee9 --- /dev/null +++ b/pyrogram/methods/chats/restrict_chat_member.py @@ -0,0 +1,99 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 datetime import datetime +from typing import Union + +import pyrogram +from pyrogram import raw, utils +from pyrogram import types + + +class RestrictChatMember: + async def restrict_chat_member( + self: "pyrogram.Client", + chat_id: Union[int, str], + user_id: Union[int, str], + permissions: "types.ChatPermissions", + until_date: datetime = utils.zero_datetime() + ) -> "types.Chat": + """Restrict a user in a supergroup. + + You must be an administrator in the supergroup for this to work and must have the appropriate admin rights. + Pass True for all permissions to lift restrictions from a user. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + user_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target user. + For a contact that exists in your Telegram address book you can use his phone number (str). + + permissions (:obj:`~pyrogram.types.ChatPermissions`): + New user permissions. + + until_date (:py:obj:`~datetime.datetime`, *optional*): + Date when the user will be unbanned. + If user is banned for more than 366 days or less than 30 seconds from the current time they are + considered to be banned forever. Defaults to epoch (ban forever). + + Returns: + :obj:`~pyrogram.types.Chat`: On success, a chat object is returned. + + Example: + .. code-block:: python + + from datetime import datetime, timedelta + from pyrogram.types import ChatPermissions + + # Completely restrict chat member (mute) forever + await app.restrict_chat_member(chat_id, user_id, ChatPermissions()) + + # Chat member muted for 24h + await app.restrict_chat_member(chat_id, user_id, ChatPermissions(), + datetime.now() + timedelta(days=1)) + + # Chat member can only send text messages + await app.restrict_chat_member(chat_id, user_id, + ChatPermissions(can_send_messages=True)) + """ + r = await self.invoke( + raw.functions.channels.EditBanned( + channel=await self.resolve_peer(chat_id), + participant=await self.resolve_peer(user_id), + banned_rights=raw.types.ChatBannedRights( + until_date=utils.datetime_to_timestamp(until_date), + send_messages=not permissions.can_send_messages, + send_media=not permissions.can_send_media_messages, + send_stickers=not permissions.can_send_other_messages, + send_gifs=not permissions.can_send_other_messages, + send_games=not permissions.can_send_other_messages, + send_inline=not permissions.can_send_other_messages, + embed_links=not permissions.can_add_web_page_previews, + send_polls=not permissions.can_send_polls, + change_info=not permissions.can_change_info, + invite_users=not permissions.can_invite_users, + pin_messages=not permissions.can_pin_messages, + ) + ) + ) + + return types.Chat._parse_chat(self, r.chats[0]) diff --git a/pyrogram/methods/chats/set_administrator_title.py b/pyrogram/methods/chats/set_administrator_title.py new file mode 100644 index 00000000000..2c77066ed7d --- /dev/null +++ b/pyrogram/methods/chats/set_administrator_title.py @@ -0,0 +1,85 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw + + +class SetAdministratorTitle: + async def set_administrator_title( + self: "pyrogram.Client", + chat_id: Union[int, str], + user_id: Union[int, str], + title: str, + ) -> bool: + """Set a custom title (rank) to an administrator of a supergroup. + + If you are an administrator of a supergroup (i.e. not the owner), you can only set the title of other + administrators who have been promoted by you. If you are the owner, you can change every administrator's title. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + user_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target user. + For a contact that exists in your Telegram address book you can use his phone number (str). + + title (``str``, *optional*): + A custom title that will be shown to all members instead of "Owner" or "Admin". + Pass None or "" (empty string) to remove the custom title. + + Returns: + ``bool``: True on success. + + Example: + .. code-block:: python + + await app.set_administrator_title(chat_id, user_id, "Admin Title") + """ + chat_id = await self.resolve_peer(chat_id) + user_id = await self.resolve_peer(user_id) + + r = (await self.invoke( + raw.functions.channels.GetParticipant( + channel=chat_id, + participant=user_id + ) + )).participant + + if isinstance(r, raw.types.ChannelParticipantCreator): + admin_rights = raw.types.ChatAdminRights() + elif isinstance(r, raw.types.ChannelParticipantAdmin): + admin_rights = r.admin_rights + else: + raise ValueError("Custom titles can only be applied to owners or administrators of supergroups") + + await self.invoke( + raw.functions.channels.EditAdmin( + channel=chat_id, + user_id=user_id, + admin_rights=admin_rights, + rank=title + ) + ) + + return True diff --git a/pyrogram/methods/chats/set_chat_description.py b/pyrogram/methods/chats/set_chat_description.py new file mode 100644 index 00000000000..563e990c4c9 --- /dev/null +++ b/pyrogram/methods/chats/set_chat_description.py @@ -0,0 +1,66 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw + + +class SetChatDescription: + async def set_chat_description( + self: "pyrogram.Client", + chat_id: Union[int, str], + description: str + ) -> bool: + """Change the description of a supergroup or a channel. + You must be an administrator in the chat for this to work and must have the appropriate admin rights. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + description (``str``): + New chat description, 0-255 characters. + + Returns: + ``bool``: True on success. + + Raises: + ValueError: if a chat_id doesn't belong to a supergroup or a channel. + + Example: + .. code-block:: python + + await app.set_chat_description(chat_id, "New Description") + """ + peer = await self.resolve_peer(chat_id) + + if isinstance(peer, (raw.types.InputPeerChannel, raw.types.InputPeerChat)): + await self.invoke( + raw.functions.messages.EditChatAbout( + peer=peer, + about=description + ) + ) + else: + raise ValueError(f'The chat_id "{chat_id}" belongs to a user') + + return True diff --git a/pyrogram/methods/chats/set_chat_permissions.py b/pyrogram/methods/chats/set_chat_permissions.py new file mode 100644 index 00000000000..d8ec0cf02bf --- /dev/null +++ b/pyrogram/methods/chats/set_chat_permissions.py @@ -0,0 +1,87 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class SetChatPermissions: + async def set_chat_permissions( + self: "pyrogram.Client", + chat_id: Union[int, str], + permissions: "types.ChatPermissions", + ) -> "types.Chat": + """Set default chat permissions for all members. + + You must be an administrator in the group or a supergroup for this to work and must have the + *can_restrict_members* admin rights. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + permissions (:obj:`~pyrogram.types.ChatPermissions`): + New default chat permissions. + + Returns: + :obj:`~pyrogram.types.Chat`: On success, a chat object is returned. + + Example: + .. code-block:: python + + from pyrogram.types import ChatPermissions + + # Completely restrict chat + await app.set_chat_permissions(chat_id, ChatPermissions()) + + # Chat members can only send text messages and media messages + await app.set_chat_permissions( + chat_id, + ChatPermissions( + can_send_messages=True, + can_send_media_messages=True + ) + ) + """ + + r = await self.invoke( + raw.functions.messages.EditChatDefaultBannedRights( + peer=await self.resolve_peer(chat_id), + banned_rights=raw.types.ChatBannedRights( + until_date=0, + send_messages=not permissions.can_send_messages, + send_media=not permissions.can_send_media_messages, + send_stickers=not permissions.can_send_other_messages, + send_gifs=not permissions.can_send_other_messages, + send_games=not permissions.can_send_other_messages, + send_inline=not permissions.can_send_other_messages, + embed_links=not permissions.can_add_web_page_previews, + send_polls=not permissions.can_send_polls, + change_info=not permissions.can_change_info, + invite_users=not permissions.can_invite_users, + pin_messages=not permissions.can_pin_messages, + ) + ) + ) + + return types.Chat._parse_chat(self, r.chats[0]) diff --git a/pyrogram/methods/chats/set_chat_photo.py b/pyrogram/methods/chats/set_chat_photo.py new file mode 100644 index 00000000000..f3db5329913 --- /dev/null +++ b/pyrogram/methods/chats/set_chat_photo.py @@ -0,0 +1,121 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import os +from typing import Union, BinaryIO + +import pyrogram +from pyrogram import raw +from pyrogram import utils +from pyrogram.file_id import FileType + + +class SetChatPhoto: + async def set_chat_photo( + self: "pyrogram.Client", + chat_id: Union[int, str], + *, + photo: Union[str, BinaryIO] = None, + video: Union[str, BinaryIO] = None, + video_start_ts: float = None, + ) -> bool: + """Set a new chat photo or video (H.264/MPEG-4 AVC video, max 5 seconds). + + The ``photo`` and ``video`` arguments are mutually exclusive. + Pass either one as named argument (see examples below). + + You must be an administrator in the chat for this to work and must have the appropriate admin rights. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + photo (``str`` | ``BinaryIO``, *optional*): + New chat photo. You can pass a :obj:`~pyrogram.types.Photo` file_id, a file path to upload a new photo + from your local machine or a binary file-like object with its attribute + ".name" set for in-memory uploads. + + video (``str`` | ``BinaryIO``, *optional*): + New chat video. You can pass a :obj:`~pyrogram.types.Video` file_id, a file path to upload a new video + from your local machine or a binary file-like object with its attribute + ".name" set for in-memory uploads. + + video_start_ts (``float``, *optional*): + The timestamp in seconds of the video frame to use as photo profile preview. + + Returns: + ``bool``: True on success. + + Raises: + ValueError: if a chat_id belongs to user. + + Example: + .. code-block:: python + + # Set chat photo using a local file + await app.set_chat_photo(chat_id, photo="photo.jpg") + + # Set chat photo using an existing Photo file_id + await app.set_chat_photo(chat_id, photo=photo.file_id) + + + # Set chat video using a local file + await app.set_chat_photo(chat_id, video="video.mp4") + + # Set chat photo using an existing Video file_id + await app.set_chat_photo(chat_id, video=video.file_id) + """ + peer = await self.resolve_peer(chat_id) + + if isinstance(photo, str): + if os.path.isfile(photo): + photo = raw.types.InputChatUploadedPhoto( + file=await self.save_file(photo), + video=await self.save_file(video), + video_start_ts=video_start_ts, + ) + else: + photo = utils.get_input_media_from_file_id(photo, FileType.PHOTO) + photo = raw.types.InputChatPhoto(id=photo.id) + else: + photo = raw.types.InputChatUploadedPhoto( + file=await self.save_file(photo), + video=await self.save_file(video), + video_start_ts=video_start_ts, + ) + + if isinstance(peer, raw.types.InputPeerChat): + await self.invoke( + raw.functions.messages.EditChatPhoto( + chat_id=peer.chat_id, + photo=photo, + ) + ) + elif isinstance(peer, raw.types.InputPeerChannel): + await self.invoke( + raw.functions.channels.EditPhoto( + channel=peer, + photo=photo + ) + ) + else: + raise ValueError(f'The chat_id "{chat_id}" belongs to a user') + + return True diff --git a/pyrogram/methods/chats/set_chat_protected_content.py b/pyrogram/methods/chats/set_chat_protected_content.py new file mode 100644 index 00000000000..b6a89c1190d --- /dev/null +++ b/pyrogram/methods/chats/set_chat_protected_content.py @@ -0,0 +1,53 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw + + +class SetChatProtectedContent: + async def set_chat_protected_content( + self: "pyrogram.Client", + chat_id: Union[int, str], + enabled: bool + ) -> bool: + """Set the chat protected content setting. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + enabled (``bool``): + Pass True to enable the protected content setting, False to disable. + + Returns: + ``bool``: On success, True is returned. + """ + + await self.invoke( + raw.functions.messages.ToggleNoForwards( + peer=await self.resolve_peer(chat_id), + enabled=enabled + ) + ) + + return True diff --git a/pyrogram/methods/chats/set_chat_title.py b/pyrogram/methods/chats/set_chat_title.py new file mode 100644 index 00000000000..9a963571c00 --- /dev/null +++ b/pyrogram/methods/chats/set_chat_title.py @@ -0,0 +1,78 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw + + +class SetChatTitle: + async def set_chat_title( + self: "pyrogram.Client", + chat_id: Union[int, str], + title: str + ) -> bool: + """Change the title of a chat. + Titles can't be changed for private chats. + You must be an administrator in the chat for this to work and must have the appropriate admin rights. + + Note: + In regular groups (non-supergroups), this method will only work if the "All Members Are Admins" + setting is off. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + title (``str``): + New chat title, 1-255 characters. + + Returns: + ``bool``: True on success. + + Raises: + ValueError: In case a chat id belongs to user. + + Example: + .. code-block:: python + + await app.set_chat_title(chat_id, "New Title") + """ + peer = await self.resolve_peer(chat_id) + + if isinstance(peer, raw.types.InputPeerChat): + await self.invoke( + raw.functions.messages.EditChatTitle( + chat_id=peer.chat_id, + title=title + ) + ) + elif isinstance(peer, raw.types.InputPeerChannel): + await self.invoke( + raw.functions.channels.EditTitle( + channel=peer, + title=title + ) + ) + else: + raise ValueError(f'The chat_id "{chat_id}" belongs to a user') + + return True diff --git a/pyrogram/methods/chats/set_chat_username.py b/pyrogram/methods/chats/set_chat_username.py new file mode 100644 index 00000000000..e6d64e98c1d --- /dev/null +++ b/pyrogram/methods/chats/set_chat_username.py @@ -0,0 +1,68 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union, Optional + +import pyrogram +from pyrogram import raw + + +class SetChatUsername: + async def set_chat_username( + self: "pyrogram.Client", + chat_id: Union[int, str], + username: Optional[str] + ) -> bool: + """Set a channel or a supergroup username. + + To set your own username (for users only, not bots) you can use :meth:`~pyrogram.Client.set_username`. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``) + Unique identifier (int) or username (str) of the target chat. + + username (``str`` | ``None``): + Username to set. Pass "" (empty string) or None to remove the username. + + Returns: + ``bool``: True on success. + + Raises: + ValueError: In case a chat id belongs to a user or chat. + + Example: + .. code-block:: python + + await app.set_chat_username(chat_id, "new_username") + """ + + peer = await self.resolve_peer(chat_id) + + if isinstance(peer, raw.types.InputPeerChannel): + return bool( + await self.invoke( + raw.functions.channels.UpdateUsername( + channel=peer, + username=username or "" + ) + ) + ) + else: + raise ValueError(f'The chat_id "{chat_id}" belongs to a user or chat') diff --git a/pyrogram/methods/chats/set_send_as_chat.py b/pyrogram/methods/chats/set_send_as_chat.py new file mode 100644 index 00000000000..15201f3beac --- /dev/null +++ b/pyrogram/methods/chats/set_send_as_chat.py @@ -0,0 +1,57 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw + + +class SetSendAsChat: + async def set_send_as_chat( + self: "pyrogram.Client", + chat_id: Union[int, str], + send_as_chat_id: Union[int, str] + ) -> bool: + """Set the default "send_as" chat for a chat. + + Use :meth:`~pyrogram.Client.get_send_as_chats` to get all the "send_as" chats available for use. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + send_as_chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the send_as chat. + + Returns: + ``bool``: On success, true is returned + + Example: + .. code-block:: python + + await app.set_send_as_chat(chat_id, send_as_chat_id) + """ + return await self.invoke( + raw.functions.messages.SaveDefaultSendAs( + peer=await self.resolve_peer(chat_id), + send_as=await self.resolve_peer(send_as_chat_id) + ) + ) diff --git a/pyrogram/methods/chats/set_slow_mode.py b/pyrogram/methods/chats/set_slow_mode.py new file mode 100644 index 00000000000..60e88e6de59 --- /dev/null +++ b/pyrogram/methods/chats/set_slow_mode.py @@ -0,0 +1,63 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union, Optional + +import pyrogram +from pyrogram import raw + + +class SetSlowMode: + async def set_slow_mode( + self: "pyrogram.Client", + chat_id: Union[int, str], + seconds: Optional[int] + ) -> bool: + """Set the slow mode interval for a chat. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + seconds (``int`` | ``None``): + Seconds in which members will be able to send only one message per this interval. + Valid values are: 0 or None (off), 10, 30, 60 (1m), 300 (5m), 900 (15m) or 3600 (1h). + + Returns: + ``bool``: True on success. + + Example: + .. code-block:: python + + # Set slow mode to 60 seconds + await app.set_slow_mode(chat_id, 60) + + # Disable slow mode + await app.set_slow_mode(chat_id, None) + """ + + await self.invoke( + raw.functions.channels.ToggleSlowMode( + channel=await self.resolve_peer(chat_id), + seconds=seconds or 0 + ) + ) + + return True diff --git a/pyrogram/methods/chats/unarchive_chats.py b/pyrogram/methods/chats/unarchive_chats.py new file mode 100644 index 00000000000..4d2cb0e4118 --- /dev/null +++ b/pyrogram/methods/chats/unarchive_chats.py @@ -0,0 +1,71 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union, List + +import pyrogram +from pyrogram import raw + + +class UnarchiveChats: + async def unarchive_chats( + self: "pyrogram.Client", + chat_ids: Union[int, str, List[Union[int, str]]], + ) -> bool: + """Unarchive one or more chats. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_ids (``int`` | ``str`` | List[``int``, ``str``]): + Unique identifier (int) or username (str) of the target chat. + You can also pass a list of ids (int) or usernames (str). + + Returns: + ``bool``: On success, True is returned. + + Example: + .. code-block:: python + + # Unarchive chat + await app.unarchive_chats(chat_id) + + # Unarchive multiple chats at once + await app.unarchive_chats([chat_id1, chat_id2, chat_id3]) + """ + + if not isinstance(chat_ids, list): + chat_ids = [chat_ids] + + folder_peers = [] + + for chat in chat_ids: + folder_peers.append( + raw.types.InputFolderPeer( + peer=await self.resolve_peer(chat), + folder_id=0 + ) + ) + + await self.invoke( + raw.functions.folders.EditPeerFolders( + folder_peers=folder_peers + ) + ) + + return True diff --git a/pyrogram/methods/chats/unban_chat_member.py b/pyrogram/methods/chats/unban_chat_member.py new file mode 100644 index 00000000000..bbe7b454929 --- /dev/null +++ b/pyrogram/methods/chats/unban_chat_member.py @@ -0,0 +1,64 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw + + +class UnbanChatMember: + async def unban_chat_member( + self: "pyrogram.Client", + chat_id: Union[int, str], + user_id: Union[int, str] + ) -> bool: + """Unban a previously banned user in a supergroup or channel. + The user will **not** return to the group or channel automatically, but will be able to join via link, etc. + You must be an administrator for this to work. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + user_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target user. + For a contact that exists in your Telegram address book you can use his phone number (str). + + Returns: + ``bool``: True on success. + + Example: + .. code-block:: python + + # Unban chat member right now + await app.unban_chat_member(chat_id, user_id) + """ + await self.invoke( + raw.functions.channels.EditBanned( + channel=await self.resolve_peer(chat_id), + participant=await self.resolve_peer(user_id), + banned_rights=raw.types.ChatBannedRights( + until_date=0 + ) + ) + ) + + return True diff --git a/pyrogram/methods/chats/unpin_all_chat_messages.py b/pyrogram/methods/chats/unpin_all_chat_messages.py new file mode 100644 index 00000000000..25a53caf625 --- /dev/null +++ b/pyrogram/methods/chats/unpin_all_chat_messages.py @@ -0,0 +1,55 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw + + +class UnpinAllChatMessages: + async def unpin_all_chat_messages( + self: "pyrogram.Client", + chat_id: Union[int, str], + ) -> bool: + """Use this method to clear the list of pinned messages in a chat. + If the chat is not a private chat, the bot must be an administrator in the chat for this to work and must have + the 'can_pin_messages' admin right in a supergroup or 'can_edit_messages' admin right in a channel. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + Returns: + ``bool``: True on success. + + Example: + .. code-block:: python + + # Unpin all chat messages + await app.unpin_all_chat_messages(chat_id) + """ + await self.invoke( + raw.functions.messages.UnpinAllMessages( + peer=await self.resolve_peer(chat_id) + ) + ) + + return True diff --git a/pyrogram/methods/chats/unpin_chat_message.py b/pyrogram/methods/chats/unpin_chat_message.py new file mode 100644 index 00000000000..6c8e036e0d5 --- /dev/null +++ b/pyrogram/methods/chats/unpin_chat_message.py @@ -0,0 +1,61 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw + + +class UnpinChatMessage: + async def unpin_chat_message( + self: "pyrogram.Client", + chat_id: Union[int, str], + message_id: int = 0 + ) -> bool: + """Unpin a message in a group, channel or your own chat. + You must be an administrator in the chat for this to work and must have the "can_pin_messages" admin + right in the supergroup or "can_edit_messages" admin right in the channel. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + message_id (``int``, *optional*): + Identifier of a message to unpin. + If not specified, the most recent pinned message (by sending date) will be unpinned. + + Returns: + ``bool``: True on success. + + Example: + .. code-block:: python + + await app.unpin_chat_message(chat_id, message_id) + """ + await self.invoke( + raw.functions.messages.UpdatePinnedMessage( + peer=await self.resolve_peer(chat_id), + id=message_id, + unpin=True + ) + ) + + return True diff --git a/pyrogram/methods/contacts/__init__.py b/pyrogram/methods/contacts/__init__.py new file mode 100644 index 00000000000..5849ce4389e --- /dev/null +++ b/pyrogram/methods/contacts/__init__.py @@ -0,0 +1,33 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 .add_contact import AddContact +from .delete_contacts import DeleteContacts +from .get_contacts import GetContacts +from .get_contacts_count import GetContactsCount +from .import_contacts import ImportContacts + + +class Contacts( + GetContacts, + DeleteContacts, + ImportContacts, + GetContactsCount, + AddContact +): + pass diff --git a/pyrogram/methods/contacts/add_contact.py b/pyrogram/methods/contacts/add_contact.py new file mode 100644 index 00000000000..9c4faa7e481 --- /dev/null +++ b/pyrogram/methods/contacts/add_contact.py @@ -0,0 +1,78 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class AddContact: + async def add_contact( + self: "pyrogram.Client", + user_id: Union[int, str], + first_name: str, + last_name: str = "", + phone_number: str = "", + share_phone_number: bool = False + ): + """Add an existing Telegram user as contact, even without a phone number. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + user_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target user. + + first_name (``str``): + User's first name. + + last_name (``str``, *optional*): + User's last name. + + phone_number (``str``, *optional*): + User's phone number. + + share_phone_number (``bool``, *optional*): + Whether or not to share the phone number with the user. + Defaults to False. + + Returns: + :obj:`~pyrogram.types.User`: On success the user is returned. + + Example: + .. code-block:: python + + # Add contact by id + await app.add_contact(12345678, "Foo") + + # Add contact by username + await app.add_contact("username", "Bar") + """ + r = await self.invoke( + raw.functions.contacts.AddContact( + id=await self.resolve_peer(user_id), + first_name=first_name, + last_name=last_name, + phone=phone_number, + add_phone_privacy_exception=share_phone_number + ) + ) + + return types.User._parse(self, r.users[0]) diff --git a/pyrogram/methods/contacts/delete_contacts.py b/pyrogram/methods/contacts/delete_contacts.py new file mode 100644 index 00000000000..7f08f29730c --- /dev/null +++ b/pyrogram/methods/contacts/delete_contacts.py @@ -0,0 +1,66 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import List, Union + +import pyrogram +from pyrogram import raw, types + + +class DeleteContacts: + async def delete_contacts( + self: "pyrogram.Client", + user_ids: Union[int, str, List[Union[int, str]]] + ) -> Union["types.User", List["types.User"], None]: + """Delete contacts from your Telegram address book. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + user_ids (``int`` | ``str`` | List of ``int`` or ``str``): + A single user id/username or a list of user identifiers (id or username). + + Returns: + :obj:`~pyrogram.types.User` | List of :obj:`~pyrogram.types.User` | ``None``: In case *user_ids* was an + integer or a string, a single User object is returned. In case *user_ids* was a list, a list of User objects + is returned. In case nothing changed after calling the method (for example, when deleting a non-existent + contact), None is returned. + + Example: + .. code-block:: python + + await app.delete_contacts(user_id) + await app.delete_contacts([user_id1, user_id2, user_id3]) + """ + is_list = isinstance(user_ids, list) + + if not is_list: + user_ids = [user_ids] + + r = await self.invoke( + raw.functions.contacts.DeleteContacts( + id=[await self.resolve_peer(i) for i in user_ids] + ) + ) + + if not r.updates: + return None + + users = types.List([types.User._parse(self, i) for i in r.users]) + + return users if is_list else users[0] diff --git a/pyrogram/methods/contacts/get_contacts.py b/pyrogram/methods/contacts/get_contacts.py new file mode 100644 index 00000000000..763f9a30583 --- /dev/null +++ b/pyrogram/methods/contacts/get_contacts.py @@ -0,0 +1,47 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import logging +from typing import List + +import pyrogram +from pyrogram import raw +from pyrogram import types + +log = logging.getLogger(__name__) + + +class GetContacts: + async def get_contacts( + self: "pyrogram.Client" + ) -> List["types.User"]: + """Get contacts from your Telegram address book. + + .. include:: /_includes/usable-by/users.rst + + Returns: + List of :obj:`~pyrogram.types.User`: On success, a list of users is returned. + + Example: + .. code-block:: python + + contacts = await app.get_contacts() + print(contacts) + """ + contacts = await self.invoke(raw.functions.contacts.GetContacts(hash=0)) + return types.List(types.User._parse(self, user) for user in contacts.users) diff --git a/pyrogram/methods/contacts/get_contacts_count.py b/pyrogram/methods/contacts/get_contacts_count.py new file mode 100644 index 00000000000..56120bac71c --- /dev/null +++ b/pyrogram/methods/contacts/get_contacts_count.py @@ -0,0 +1,41 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import pyrogram +from pyrogram import raw + + +class GetContactsCount: + async def get_contacts_count( + self: "pyrogram.Client" + ) -> int: + """Get the total count of contacts from your Telegram address book. + + .. include:: /_includes/usable-by/users.rst + + Returns: + ``int``: On success, the contacts count is returned. + + Example: + .. code-block:: python + + count = await app.get_contacts_count() + print(count) + """ + + return len((await self.invoke(raw.functions.contacts.GetContacts(hash=0))).contacts) diff --git a/pyrogram/methods/contacts/import_contacts.py b/pyrogram/methods/contacts/import_contacts.py new file mode 100644 index 00000000000..aa8fd4ae37e --- /dev/null +++ b/pyrogram/methods/contacts/import_contacts.py @@ -0,0 +1,58 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import List + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class ImportContacts: + async def import_contacts( + self: "pyrogram.Client", + contacts: List["types.InputPhoneContact"] + ): + """Import contacts to your Telegram address book. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + contacts (List of :obj:`~pyrogram.types.InputPhoneContact`): + The contact list to be added + + Returns: + :obj:`types.contacts.ImportedContacts` + + Example: + .. code-block:: python + + from pyrogram.types import InputPhoneContact + + await app.import_contacts([ + InputPhoneContact("+1-123-456-7890", "Foo"), + InputPhoneContact("+1-456-789-0123", "Bar"), + InputPhoneContact("+1-789-012-3456", "Baz")]) + """ + imported_contacts = await self.invoke( + raw.functions.contacts.ImportContacts( + contacts=contacts + ) + ) + + return imported_contacts diff --git a/pyrogram/methods/decorators/__init__.py b/pyrogram/methods/decorators/__init__.py new file mode 100644 index 00000000000..1fc61185c9e --- /dev/null +++ b/pyrogram/methods/decorators/__init__.py @@ -0,0 +1,47 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 .on_callback_query import OnCallbackQuery +from .on_chat_join_request import OnChatJoinRequest +from .on_chat_member_updated import OnChatMemberUpdated +from .on_chosen_inline_result import OnChosenInlineResult +from .on_deleted_messages import OnDeletedMessages +from .on_disconnect import OnDisconnect +from .on_edited_message import OnEditedMessage +from .on_inline_query import OnInlineQuery +from .on_message import OnMessage +from .on_poll import OnPoll +from .on_raw_update import OnRawUpdate +from .on_user_status import OnUserStatus + + +class Decorators( + OnMessage, + OnEditedMessage, + OnDeletedMessages, + OnCallbackQuery, + OnRawUpdate, + OnDisconnect, + OnUserStatus, + OnInlineQuery, + OnPoll, + OnChosenInlineResult, + OnChatMemberUpdated, + OnChatJoinRequest +): + pass diff --git a/pyrogram/methods/decorators/on_callback_query.py b/pyrogram/methods/decorators/on_callback_query.py new file mode 100644 index 00000000000..07e15a3e78c --- /dev/null +++ b/pyrogram/methods/decorators/on_callback_query.py @@ -0,0 +1,61 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Callable + +import pyrogram +from pyrogram.filters import Filter + + +class OnCallbackQuery: + def on_callback_query( + self=None, + filters=None, + group: int = 0 + ) -> Callable: + """Decorator for handling callback queries. + + This does the same thing as :meth:`~pyrogram.Client.add_handler` using the + :obj:`~pyrogram.handlers.CallbackQueryHandler`. + + Parameters: + filters (:obj:`~pyrogram.filters`, *optional*): + Pass one or more filters to allow only a subset of callback queries to be passed + in your function. + + group (``int``, *optional*): + The group identifier, defaults to 0. + """ + + def decorator(func: Callable) -> Callable: + if isinstance(self, pyrogram.Client): + self.add_handler(pyrogram.handlers.CallbackQueryHandler(func, filters), group) + elif isinstance(self, Filter) or self is None: + if not hasattr(func, "handlers"): + func.handlers = [] + + func.handlers.append( + ( + pyrogram.handlers.CallbackQueryHandler(func, self), + group if filters is None else filters + ) + ) + + return func + + return decorator diff --git a/pyrogram/methods/decorators/on_chat_join_request.py b/pyrogram/methods/decorators/on_chat_join_request.py new file mode 100644 index 00000000000..57fb709cb55 --- /dev/null +++ b/pyrogram/methods/decorators/on_chat_join_request.py @@ -0,0 +1,60 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Callable + +import pyrogram +from pyrogram.filters import Filter + + +class OnChatJoinRequest: + def on_chat_join_request( + self=None, + filters=None, + group: int = 0 + ) -> Callable: + """Decorator for handling chat join requests. + + This does the same thing as :meth:`~pyrogram.Client.add_handler` using the + :obj:`~pyrogram.handlers.ChatJoinRequestHandler`. + + Parameters: + filters (:obj:`~pyrogram.filters`, *optional*): + Pass one or more filters to allow only a subset of updates to be passed in your function. + + group (``int``, *optional*): + The group identifier, defaults to 0. + """ + + def decorator(func: Callable) -> Callable: + if isinstance(self, pyrogram.Client): + self.add_handler(pyrogram.handlers.ChatJoinRequestHandler(func, filters), group) + elif isinstance(self, Filter) or self is None: + if not hasattr(func, "handlers"): + func.handlers = [] + + func.handlers.append( + ( + pyrogram.handlers.ChatJoinRequestHandler(func, self), + group if filters is None else filters + ) + ) + + return func + + return decorator diff --git a/pyrogram/methods/decorators/on_chat_member_updated.py b/pyrogram/methods/decorators/on_chat_member_updated.py new file mode 100644 index 00000000000..c2f0e888a80 --- /dev/null +++ b/pyrogram/methods/decorators/on_chat_member_updated.py @@ -0,0 +1,60 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Callable + +import pyrogram +from pyrogram.filters import Filter + + +class OnChatMemberUpdated: + def on_chat_member_updated( + self=None, + filters=None, + group: int = 0 + ) -> Callable: + """Decorator for handling event changes on chat members. + + This does the same thing as :meth:`~pyrogram.Client.add_handler` using the + :obj:`~pyrogram.handlers.ChatMemberUpdatedHandler`. + + Parameters: + filters (:obj:`~pyrogram.filters`, *optional*): + Pass one or more filters to allow only a subset of updates to be passed in your function. + + group (``int``, *optional*): + The group identifier, defaults to 0. + """ + + def decorator(func: Callable) -> Callable: + if isinstance(self, pyrogram.Client): + self.add_handler(pyrogram.handlers.ChatMemberUpdatedHandler(func, filters), group) + elif isinstance(self, Filter) or self is None: + if not hasattr(func, "handlers"): + func.handlers = [] + + func.handlers.append( + ( + pyrogram.handlers.ChatMemberUpdatedHandler(func, self), + group if filters is None else filters + ) + ) + + return func + + return decorator diff --git a/pyrogram/methods/decorators/on_chosen_inline_result.py b/pyrogram/methods/decorators/on_chosen_inline_result.py new file mode 100644 index 00000000000..090f6c04251 --- /dev/null +++ b/pyrogram/methods/decorators/on_chosen_inline_result.py @@ -0,0 +1,61 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Callable + +import pyrogram +from pyrogram.filters import Filter + + +class OnChosenInlineResult: + def on_chosen_inline_result( + self=None, + filters=None, + group: int = 0 + ) -> Callable: + """Decorator for handling chosen inline results. + + This does the same thing as :meth:`~pyrogram.Client.add_handler` using the + :obj:`~pyrogram.handlers.ChosenInlineResultHandler`. + + Parameters: + filters (:obj:`~pyrogram.filters`, *optional*): + Pass one or more filters to allow only a subset of chosen inline results to be passed + in your function. + + group (``int``, *optional*): + The group identifier, defaults to 0. + """ + + def decorator(func: Callable) -> Callable: + if isinstance(self, pyrogram.Client): + self.add_handler(pyrogram.handlers.ChosenInlineResultHandler(func, filters), group) + elif isinstance(self, Filter) or self is None: + if not hasattr(func, "handlers"): + func.handlers = [] + + func.handlers.append( + ( + pyrogram.handlers.ChosenInlineResultHandler(func, self), + group if filters is None else filters + ) + ) + + return func + + return decorator diff --git a/pyrogram/methods/decorators/on_deleted_messages.py b/pyrogram/methods/decorators/on_deleted_messages.py new file mode 100644 index 00000000000..9565c11329f --- /dev/null +++ b/pyrogram/methods/decorators/on_deleted_messages.py @@ -0,0 +1,61 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Callable + +import pyrogram +from pyrogram.filters import Filter + + +class OnDeletedMessages: + def on_deleted_messages( + self=None, + filters=None, + group: int = 0 + ) -> Callable: + """Decorator for handling deleted messages. + + This does the same thing as :meth:`~pyrogram.Client.add_handler` using the + :obj:`~pyrogram.handlers.DeletedMessagesHandler`. + + Parameters: + filters (:obj:`~pyrogram.filters`, *optional*): + Pass one or more filters to allow only a subset of messages to be passed + in your function. + + group (``int``, *optional*): + The group identifier, defaults to 0. + """ + + def decorator(func: Callable) -> Callable: + if isinstance(self, pyrogram.Client): + self.add_handler(pyrogram.handlers.DeletedMessagesHandler(func, filters), group) + elif isinstance(self, Filter) or self is None: + if not hasattr(func, "handlers"): + func.handlers = [] + + func.handlers.append( + ( + pyrogram.handlers.DeletedMessagesHandler(func, self), + group if filters is None else filters + ) + ) + + return func + + return decorator diff --git a/pyrogram/methods/decorators/on_disconnect.py b/pyrogram/methods/decorators/on_disconnect.py new file mode 100644 index 00000000000..26aa62f8fb2 --- /dev/null +++ b/pyrogram/methods/decorators/on_disconnect.py @@ -0,0 +1,43 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Callable + +import pyrogram + + +class OnDisconnect: + def on_disconnect(self=None) -> Callable: + """Decorator for handling disconnections. + + This does the same thing as :meth:`~pyrogram.Client.add_handler` using the + :obj:`~pyrogram.handlers.DisconnectHandler`. + """ + + def decorator(func: Callable) -> Callable: + if isinstance(self, pyrogram.Client): + self.add_handler(pyrogram.handlers.DisconnectHandler(func)) + else: + if not hasattr(func, "handlers"): + func.handlers = [] + + func.handlers.append((pyrogram.handlers.DisconnectHandler(func), 0)) + + return func + + return decorator diff --git a/pyrogram/methods/decorators/on_edited_message.py b/pyrogram/methods/decorators/on_edited_message.py new file mode 100644 index 00000000000..a8c86bb6d22 --- /dev/null +++ b/pyrogram/methods/decorators/on_edited_message.py @@ -0,0 +1,61 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Callable + +import pyrogram +from pyrogram.filters import Filter + + +class OnEditedMessage: + def on_edited_message( + self=None, + filters=None, + group: int = 0 + ) -> Callable: + """Decorator for handling edited messages. + + This does the same thing as :meth:`~pyrogram.Client.add_handler` using the + :obj:`~pyrogram.handlers.EditedMessageHandler`. + + Parameters: + filters (:obj:`~pyrogram.filters`, *optional*): + Pass one or more filters to allow only a subset of messages to be passed + in your function. + + group (``int``, *optional*): + The group identifier, defaults to 0. + """ + + def decorator(func: Callable) -> Callable: + if isinstance(self, pyrogram.Client): + self.add_handler(pyrogram.handlers.EditedMessageHandler(func, filters), group) + elif isinstance(self, Filter) or self is None: + if not hasattr(func, "handlers"): + func.handlers = [] + + func.handlers.append( + ( + pyrogram.handlers.EditedMessageHandler(func, self), + group if filters is None else filters + ) + ) + + return func + + return decorator diff --git a/pyrogram/methods/decorators/on_inline_query.py b/pyrogram/methods/decorators/on_inline_query.py new file mode 100644 index 00000000000..6b53a464def --- /dev/null +++ b/pyrogram/methods/decorators/on_inline_query.py @@ -0,0 +1,61 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Callable + +import pyrogram +from pyrogram.filters import Filter + + +class OnInlineQuery: + def on_inline_query( + self=None, + filters=None, + group: int = 0 + ) -> Callable: + """Decorator for handling inline queries. + + This does the same thing as :meth:`~pyrogram.Client.add_handler` using the + :obj:`~pyrogram.handlers.InlineQueryHandler`. + + Parameters: + filters (:obj:`~pyrogram.filters`, *optional*): + Pass one or more filters to allow only a subset of inline queries to be passed + in your function. + + group (``int``, *optional*): + The group identifier, defaults to 0. + """ + + def decorator(func: Callable) -> Callable: + if isinstance(self, pyrogram.Client): + self.add_handler(pyrogram.handlers.InlineQueryHandler(func, filters), group) + elif isinstance(self, Filter) or self is None: + if not hasattr(func, "handlers"): + func.handlers = [] + + func.handlers.append( + ( + pyrogram.handlers.InlineQueryHandler(func, self), + group if filters is None else filters + ) + ) + + return func + + return decorator diff --git a/pyrogram/methods/decorators/on_message.py b/pyrogram/methods/decorators/on_message.py new file mode 100644 index 00000000000..220c12bbcc1 --- /dev/null +++ b/pyrogram/methods/decorators/on_message.py @@ -0,0 +1,61 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Callable + +import pyrogram +from pyrogram.filters import Filter + + +class OnMessage: + def on_message( + self=None, + filters=None, + group: int = 0 + ) -> Callable: + """Decorator for handling new messages. + + This does the same thing as :meth:`~pyrogram.Client.add_handler` using the + :obj:`~pyrogram.handlers.MessageHandler`. + + Parameters: + filters (:obj:`~pyrogram.filters`, *optional*): + Pass one or more filters to allow only a subset of messages to be passed + in your function. + + group (``int``, *optional*): + The group identifier, defaults to 0. + """ + + def decorator(func: Callable) -> Callable: + if isinstance(self, pyrogram.Client): + self.add_handler(pyrogram.handlers.MessageHandler(func, filters), group) + elif isinstance(self, Filter) or self is None: + if not hasattr(func, "handlers"): + func.handlers = [] + + func.handlers.append( + ( + pyrogram.handlers.MessageHandler(func, self), + group if filters is None else filters + ) + ) + + return func + + return decorator diff --git a/pyrogram/methods/decorators/on_poll.py b/pyrogram/methods/decorators/on_poll.py new file mode 100644 index 00000000000..6990c456b6a --- /dev/null +++ b/pyrogram/methods/decorators/on_poll.py @@ -0,0 +1,61 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Callable + +import pyrogram +from pyrogram.filters import Filter + + +class OnPoll: + def on_poll( + self=None, + filters=None, + group: int = 0 + ) -> Callable: + """Decorator for handling poll updates. + + This does the same thing as :meth:`~pyrogram.Client.add_handler` using the + :obj:`~pyrogram.handlers.PollHandler`. + + Parameters: + filters (:obj:`~pyrogram.filters`, *optional*): + Pass one or more filters to allow only a subset of polls to be passed + in your function. + + group (``int``, *optional*): + The group identifier, defaults to 0. + """ + + def decorator(func: Callable) -> Callable: + if isinstance(self, pyrogram.Client): + self.add_handler(pyrogram.handlers.PollHandler(func, filters), group) + elif isinstance(self, Filter) or self is None: + if not hasattr(func, "handlers"): + func.handlers = [] + + func.handlers.append( + ( + pyrogram.handlers.PollHandler(func, self), + group if filters is None else filters + ) + ) + + return func + + return decorator diff --git a/pyrogram/methods/decorators/on_raw_update.py b/pyrogram/methods/decorators/on_raw_update.py new file mode 100644 index 00000000000..644bc8a5b9f --- /dev/null +++ b/pyrogram/methods/decorators/on_raw_update.py @@ -0,0 +1,55 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Callable + +import pyrogram + + +class OnRawUpdate: + def on_raw_update( + self=None, + group: int = 0 + ) -> Callable: + """Decorator for handling raw updates. + + This does the same thing as :meth:`~pyrogram.Client.add_handler` using the + :obj:`~pyrogram.handlers.RawUpdateHandler`. + + Parameters: + group (``int``, *optional*): + The group identifier, defaults to 0. + """ + + def decorator(func: Callable) -> Callable: + if isinstance(self, pyrogram.Client): + self.add_handler(pyrogram.handlers.RawUpdateHandler(func), group) + else: + if not hasattr(func, "handlers"): + func.handlers = [] + + func.handlers.append( + ( + pyrogram.handlers.RawUpdateHandler(func), + group + ) + ) + + return func + + return decorator diff --git a/pyrogram/methods/decorators/on_user_status.py b/pyrogram/methods/decorators/on_user_status.py new file mode 100644 index 00000000000..a4328c37f10 --- /dev/null +++ b/pyrogram/methods/decorators/on_user_status.py @@ -0,0 +1,59 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Callable + +import pyrogram +from pyrogram.filters import Filter + + +class OnUserStatus: + def on_user_status( + self=None, + filters=None, + group: int = 0 + ) -> Callable: + """Decorator for handling user status updates. + This does the same thing as :meth:`~pyrogram.Client.add_handler` using the + :obj:`~pyrogram.handlers.UserStatusHandler`. + + Parameters: + filters (:obj:`~pyrogram.filters`, *optional*): + Pass one or more filters to allow only a subset of UserStatus updated to be passed in your function. + + group (``int``, *optional*): + The group identifier, defaults to 0. + """ + + def decorator(func: Callable) -> Callable: + if isinstance(self, pyrogram.Client): + self.add_handler(pyrogram.handlers.UserStatusHandler(func, filters), group) + elif isinstance(self, Filter) or self is None: + if not hasattr(func, "handlers"): + func.handlers = [] + + func.handlers.append( + ( + pyrogram.handlers.UserStatusHandler(func, self), + group if filters is None else filters + ) + ) + + return func + + return decorator diff --git a/pyrogram/methods/invite_links/__init__.py b/pyrogram/methods/invite_links/__init__.py new file mode 100644 index 00000000000..67c1d14958d --- /dev/null +++ b/pyrogram/methods/invite_links/__init__.py @@ -0,0 +1,58 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 .approve_all_chat_join_requests import ApproveAllChatJoinRequests +from .approve_chat_join_request import ApproveChatJoinRequest +from .create_chat_invite_link import CreateChatInviteLink +from .decline_all_chat_join_requests import DeclineAllChatJoinRequests +from .decline_chat_join_request import DeclineChatJoinRequest +from .delete_chat_admin_invite_links import DeleteChatAdminInviteLinks +from .delete_chat_invite_link import DeleteChatInviteLink +from .edit_chat_invite_link import EditChatInviteLink +from .export_chat_invite_link import ExportChatInviteLink +from .get_chat_admin_invite_links import GetChatAdminInviteLinks +from .get_chat_admin_invite_links_count import GetChatAdminInviteLinksCount +from .get_chat_admins_with_invite_links import GetChatAdminsWithInviteLinks +from .get_chat_invite_link import GetChatInviteLink +from .get_chat_invite_link_joiners import GetChatInviteLinkJoiners +from .get_chat_invite_link_joiners_count import GetChatInviteLinkJoinersCount +from .get_chat_join_requests import GetChatJoinRequests +from .revoke_chat_invite_link import RevokeChatInviteLink + + +class InviteLinks( + RevokeChatInviteLink, + DeleteChatInviteLink, + EditChatInviteLink, + CreateChatInviteLink, + GetChatInviteLinkJoiners, + GetChatInviteLinkJoinersCount, + GetChatAdminInviteLinks, + ExportChatInviteLink, + DeleteChatAdminInviteLinks, + GetChatAdminInviteLinksCount, + GetChatAdminsWithInviteLinks, + GetChatInviteLink, + ApproveChatJoinRequest, + DeclineChatJoinRequest, + ApproveAllChatJoinRequests, + DeclineAllChatJoinRequests, + GetChatJoinRequests +): + pass diff --git a/pyrogram/methods/invite_links/approve_all_chat_join_requests.py b/pyrogram/methods/invite_links/approve_all_chat_join_requests.py new file mode 100644 index 00000000000..623fd87fccc --- /dev/null +++ b/pyrogram/methods/invite_links/approve_all_chat_join_requests.py @@ -0,0 +1,55 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw + + +class ApproveAllChatJoinRequests: + async def approve_all_chat_join_requests( + self: "pyrogram.Client", + chat_id: Union[int, str], + invite_link: str = None + ) -> bool: + """Approve all pending join requests in a chat. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier for the target chat or username of the target channel/supergroup + (in the format @username). + + invite_link (``str``, *optional*): + Pass an invite link to approve only its join requests. + By default, all join requests are approved. + + Returns: + ``bool``: True on success. + """ + await self.invoke( + raw.functions.messages.HideAllChatJoinRequests( + peer=await self.resolve_peer(chat_id), + approved=True, + link=invite_link + ) + ) + + return True diff --git a/pyrogram/methods/invite_links/approve_chat_join_request.py b/pyrogram/methods/invite_links/approve_chat_join_request.py new file mode 100644 index 00000000000..2fc4e6d3099 --- /dev/null +++ b/pyrogram/methods/invite_links/approve_chat_join_request.py @@ -0,0 +1,57 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw + + +class ApproveChatJoinRequest: + async def approve_chat_join_request( + self: "pyrogram.Client", + chat_id: Union[int, str], + user_id: int, + ) -> bool: + """Approve a chat join request. + + You must be an administrator in the chat for this to work and must have the *can_invite_users* administrator + right. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier for the target chat or username of the target channel/supergroup + (in the format @username). + + user_id (``int``): + Unique identifier of the target user. + + Returns: + ``bool``: True on success. + """ + await self.invoke( + raw.functions.messages.HideChatJoinRequest( + peer=await self.resolve_peer(chat_id), + user_id=await self.resolve_peer(user_id), + approved=True + ) + ) + + return True diff --git a/pyrogram/methods/invite_links/create_chat_invite_link.py b/pyrogram/methods/invite_links/create_chat_invite_link.py new file mode 100644 index 00000000000..ccf8d6505a6 --- /dev/null +++ b/pyrogram/methods/invite_links/create_chat_invite_link.py @@ -0,0 +1,87 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 datetime import datetime +from typing import Union + +import pyrogram +from pyrogram import raw, utils +from pyrogram import types + + +class CreateChatInviteLink: + async def create_chat_invite_link( + self: "pyrogram.Client", + chat_id: Union[int, str], + name: str = None, + expire_date: datetime = None, + member_limit: int = None, + creates_join_request: bool = None + ) -> "types.ChatInviteLink": + """Create an additional invite link for a chat. + + You must be an administrator in the chat for this to work and must have the appropriate admin rights. + + The link can be revoked using the method :meth:`~pyrogram.Client.revoke_chat_invite_link`. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier for the target chat or username of the target channel/supergroup + (in the format @username). + + name (``str``, *optional*): + Invite link name. + + expire_date (:py:obj:`~datetime.datetime`, *optional*): + Point in time when the link will expire. + Defaults to None (no expiration date). + + member_limit (``int``, *optional*): + Maximum number of users that can be members of the chat simultaneously after joining the chat via + this invite link; 1-99999. + Defaults to None (no member limit). + + creates_join_request (``bool``, *optional*): + True, if users joining the chat via the link need to be approved by chat administrators. + If True, member_limit can't be specified. + + Returns: + :obj:`~pyrogram.types.ChatInviteLink`: On success, the new invite link is returned. + + Example: + .. code-block:: python + + # Create a new link without limits + link = await app.create_chat_invite_link(chat_id) + + # Create a new link for up to 3 new users + link = await app.create_chat_invite_link(chat_id, member_limit=3) + """ + r = await self.invoke( + raw.functions.messages.ExportChatInvite( + peer=await self.resolve_peer(chat_id), + expire_date=utils.datetime_to_timestamp(expire_date), + usage_limit=member_limit, + title=name, + request_needed=creates_join_request + ) + ) + + return types.ChatInviteLink._parse(self, r) diff --git a/pyrogram/methods/invite_links/decline_all_chat_join_requests.py b/pyrogram/methods/invite_links/decline_all_chat_join_requests.py new file mode 100644 index 00000000000..9cf50955073 --- /dev/null +++ b/pyrogram/methods/invite_links/decline_all_chat_join_requests.py @@ -0,0 +1,55 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw + + +class DeclineAllChatJoinRequests: + async def decline_all_chat_join_requests( + self: "pyrogram.Client", + chat_id: Union[int, str], + invite_link: str = None + ) -> bool: + """Decline all pending join requests in a chat. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier for the target chat or username of the target channel/supergroup + (in the format @username). + + invite_link (``str``, *optional*): + Pass an invite link to decline only its join requests. + By default, all join requests are declined. + + Returns: + ``bool``: True on success. + """ + await self.invoke( + raw.functions.messages.HideAllChatJoinRequests( + peer=await self.resolve_peer(chat_id), + approved=False, + link=invite_link + ) + ) + + return True diff --git a/pyrogram/methods/invite_links/decline_chat_join_request.py b/pyrogram/methods/invite_links/decline_chat_join_request.py new file mode 100644 index 00000000000..9a35b8ee4b9 --- /dev/null +++ b/pyrogram/methods/invite_links/decline_chat_join_request.py @@ -0,0 +1,57 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw + + +class DeclineChatJoinRequest: + async def decline_chat_join_request( + self: "pyrogram.Client", + chat_id: Union[int, str], + user_id: int, + ) -> bool: + """Decline a chat join request. + + You must be an administrator in the chat for this to work and must have the *can_invite_users* administrator + right. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier for the target chat or username of the target channel/supergroup + (in the format @username). + + user_id (``int``): + Unique identifier of the target user. + + Returns: + ``bool``: True on success. + """ + await self.invoke( + raw.functions.messages.HideChatJoinRequest( + peer=await self.resolve_peer(chat_id), + user_id=await self.resolve_peer(user_id), + approved=False + ) + ) + + return True diff --git a/pyrogram/methods/invite_links/delete_chat_admin_invite_links.py b/pyrogram/methods/invite_links/delete_chat_admin_invite_links.py new file mode 100644 index 00000000000..32ef1de5e72 --- /dev/null +++ b/pyrogram/methods/invite_links/delete_chat_admin_invite_links.py @@ -0,0 +1,54 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw + + +class DeleteChatAdminInviteLinks: + async def delete_chat_admin_invite_links( + self: "pyrogram.Client", + chat_id: Union[int, str], + admin_id: Union[int, str], + ) -> bool: + """Delete all revoked invite links of an administrator. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier for the target chat or username of the target channel/supergroup + (in the format @username). + + admin_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target user. + For you yourself you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + Returns: + ``bool``: On success ``True`` is returned. + """ + + return await self.invoke( + raw.functions.messages.DeleteRevokedExportedChatInvites( + peer=await self.resolve_peer(chat_id), + admin_id=await self.resolve_peer(admin_id), + ) + ) diff --git a/pyrogram/methods/invite_links/delete_chat_invite_link.py b/pyrogram/methods/invite_links/delete_chat_invite_link.py new file mode 100644 index 00000000000..82db623abed --- /dev/null +++ b/pyrogram/methods/invite_links/delete_chat_invite_link.py @@ -0,0 +1,52 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw + + +class DeleteChatInviteLink: + async def delete_chat_invite_link( + self: "pyrogram.Client", + chat_id: Union[int, str], + invite_link: str, + ) -> bool: + """Delete an already revoked invite link. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier for the target chat or username of the target channel/supergroup + (in the format @username). + + invite_link (``str``): + The revoked invite link to delete. + + Returns: + ``bool``: On success ``True`` is returned. + """ + + return await self.invoke( + raw.functions.messages.DeleteExportedChatInvite( + peer=await self.resolve_peer(chat_id), + link=invite_link, + ) + ) diff --git a/pyrogram/methods/invite_links/edit_chat_invite_link.py b/pyrogram/methods/invite_links/edit_chat_invite_link.py new file mode 100644 index 00000000000..553c25dd176 --- /dev/null +++ b/pyrogram/methods/invite_links/edit_chat_invite_link.py @@ -0,0 +1,92 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 datetime import datetime +from typing import Union + +import pyrogram +from pyrogram import raw, utils +from pyrogram import types + + +class EditChatInviteLink: + async def edit_chat_invite_link( + self: "pyrogram.Client", + chat_id: Union[int, str], + invite_link: str, + name: str = None, + expire_date: datetime = None, + member_limit: int = None, + creates_join_request: bool = None + ) -> "types.ChatInviteLink": + """Edit a non-primary invite link. + + You must be an administrator in the chat for this to work and must have the appropriate admin rights. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier for the target chat or username of the target channel/supergroup + (in the format @username). + + invite_link (``str``): + The invite link to edit + + name (``str``, *optional*): + Invite link name. + + expire_date (:py:obj:`~datetime.datetime`, *optional*): + Point in time when the link will expire. + Defaults to None (no change), pass None to set no expiration date. + + member_limit (``int``, *optional*): + Maximum number of users that can be members of the chat simultaneously after joining the chat via this + invite link; 1-99999. + Defaults to None (no change), pass 0 to set no member limit. + + creates_join_request (``bool``, *optional*): + True, if users joining the chat via the link need to be approved by chat administrators. + If True, member_limit can't be specified. + + Returns: + :obj:`~pyrogram.types.ChatInviteLink`: On success, the new invite link is returned + + Example: + .. code-block:: python + + # Edit the member limit of a link + link = await app.edit_chat_invite_link(chat_id, invite_link, member_limit=5) + + # Set no expiration date of a link + link = await app.edit_chat_invite_link(chat_id, invite_link, expire_date=0) + """ + r = await self.invoke( + raw.functions.messages.EditExportedChatInvite( + peer=await self.resolve_peer(chat_id), + link=invite_link, + expire_date=utils.datetime_to_timestamp(expire_date), + usage_limit=member_limit, + title=name, + request_needed=creates_join_request + ) + ) + + users = {i.id: i for i in r.users} + + return types.ChatInviteLink._parse(self, r.invite, users) diff --git a/pyrogram/methods/invite_links/export_chat_invite_link.py b/pyrogram/methods/invite_links/export_chat_invite_link.py new file mode 100644 index 00000000000..f06caf0f9a1 --- /dev/null +++ b/pyrogram/methods/invite_links/export_chat_invite_link.py @@ -0,0 +1,65 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class ExportChatInviteLink: + async def export_chat_invite_link( + self: "pyrogram.Client", + chat_id: Union[int, str], + ) -> "types.ChatInviteLink": + """Generate a new primary invite link for a chat; any previously generated primary link is revoked. + + You must be an administrator in the chat for this to work and must have the appropriate admin rights. + + .. note :: + Each administrator in a chat generates their own invite links. Bots can't use invite links generated by + other administrators. If you want your bot to work with invite links, it will need to generate its own link + using this method – after this the link will become available to the bot via the + :meth:`~pyrogram.Client.get_chat` method. If your bot needs to generate a new invite link replacing its + previous one, use this method again. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier for the target chat or username of the target channel/supergroup + (in the format @username). + + Returns: + ``str``: On success, the new invite link as string is returned. + + Example: + .. code-block:: python + + # Generate a new primary link + link = await app.export_chat_invite_link(chat_id) + """ + r = await self.invoke( + raw.functions.messages.ExportChatInvite( + peer=await self.resolve_peer(chat_id), + legacy_revoke_permanent=True + ) + ) + + return r.link diff --git a/pyrogram/methods/invite_links/get_chat_admin_invite_links.py b/pyrogram/methods/invite_links/get_chat_admin_invite_links.py new file mode 100644 index 00000000000..62acca10e8c --- /dev/null +++ b/pyrogram/methods/invite_links/get_chat_admin_invite_links.py @@ -0,0 +1,100 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union, Optional, AsyncGenerator + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class GetChatAdminInviteLinks: + async def get_chat_admin_invite_links( + self: "pyrogram.Client", + chat_id: Union[int, str], + admin_id: Union[int, str], + revoked: bool = False, + limit: int = 0, + ) -> Optional[AsyncGenerator["types.ChatInviteLink", None]]: + """Get the invite links created by an administrator in a chat. + + .. note:: + + As an administrator you can only get your own links you have exported. + As the chat or channel owner you can get everyones links. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier for the target chat or username of the target channel/supergroup + (in the format @username). + + admin_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target user. + For you yourself you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + revoked (``bool``, *optional*): + True, if you want to get revoked links instead. + Defaults to False (get active links only). + + limit (``int``, *optional*): + Limits the number of invite links to be retrieved. + By default, no limit is applied and all invite links are returned. + + Returns: + ``Generator``: A generator yielding :obj:`~pyrogram.types.ChatInviteLink` objects. + + Yields: + :obj:`~pyrogram.types.ChatInviteLink` objects. + """ + current = 0 + total = abs(limit) or (1 << 31) - 1 + limit = min(100, total) + + offset_date = None + offset_link = None + + while True: + r = await self.invoke( + raw.functions.messages.GetExportedChatInvites( + peer=await self.resolve_peer(chat_id), + admin_id=await self.resolve_peer(admin_id), + limit=limit, + revoked=revoked, + offset_date=offset_date, + offset_link=offset_link + ) + ) + + if not r.invites: + break + + users = {i.id: i for i in r.users} + + offset_date = r.invites[-1].date + offset_link = r.invites[-1].link + + for i in r.invites: + yield types.ChatInviteLink._parse(self, i, users) + + current += 1 + + if current >= total: + return diff --git a/pyrogram/methods/invite_links/get_chat_admin_invite_links_count.py b/pyrogram/methods/invite_links/get_chat_admin_invite_links_count.py new file mode 100644 index 00000000000..528876ed4ef --- /dev/null +++ b/pyrogram/methods/invite_links/get_chat_admin_invite_links_count.py @@ -0,0 +1,62 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw + + +class GetChatAdminInviteLinksCount: + async def get_chat_admin_invite_links_count( + self: "pyrogram.Client", + chat_id: Union[int, str], + admin_id: Union[int, str], + revoked: bool = False, + ) -> int: + """Get the count of the invite links created by an administrator in a chat. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier for the target chat or username of the target channel/supergroup + (in the format @username). + + admin_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target user. + For you yourself you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + revoked (``bool``, *optional*): + True, if you want to get revoked links instead. + Defaults to False (get active links only). + + Returns: + ``int``: On success, the invite links count is returned. + """ + r = await self.invoke( + raw.functions.messages.GetExportedChatInvites( + peer=await self.resolve_peer(chat_id), + admin_id=await self.resolve_peer(admin_id), + limit=1, + revoked=revoked + ) + ) + + return r.count diff --git a/pyrogram/methods/invite_links/get_chat_admins_with_invite_links.py b/pyrogram/methods/invite_links/get_chat_admins_with_invite_links.py new file mode 100644 index 00000000000..f283e534647 --- /dev/null +++ b/pyrogram/methods/invite_links/get_chat_admins_with_invite_links.py @@ -0,0 +1,56 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw, types + + +class GetChatAdminsWithInviteLinks: + async def get_chat_admins_with_invite_links( + self: "pyrogram.Client", + chat_id: Union[int, str], + ): + """Get the list of the administrators that have exported invite links in a chat. + + You must be the owner of a chat for this to work. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier for the target chat or username of the target channel/supergroup + (in the format @username). + + Returns: + List of :obj:`~pyrogram.types.ChatAdminWithInviteLink`: On success, the list of admins that have exported + invite links is returned. + """ + r = await self.invoke( + raw.functions.messages.GetAdminsWithInvites( + peer=await self.resolve_peer(chat_id) + ) + ) + + users = {i.id: i for i in r.users} + + return types.List( + types.ChatAdminWithInviteLinks._parse(self, admin, users) + for admin in r.admins + ) diff --git a/pyrogram/methods/invite_links/get_chat_invite_link.py b/pyrogram/methods/invite_links/get_chat_invite_link.py new file mode 100644 index 00000000000..8ad575f34a3 --- /dev/null +++ b/pyrogram/methods/invite_links/get_chat_invite_link.py @@ -0,0 +1,56 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class GetChatInviteLink: + async def get_chat_invite_link( + self: "pyrogram.Client", + chat_id: Union[int, str], + invite_link: str, + ) -> "types.ChatInviteLink": + """Get detailed information about a chat invite link. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier for the target chat or username of the target channel/supergroup + (in the format @username). + + invite_link (str): + The invite link. + + Returns: + :obj:`~pyrogram.types.ChatInviteLink`: On success, the invite link is returned. + """ + r = await self.invoke( + raw.functions.messages.GetExportedChatInvite( + peer=await self.resolve_peer(chat_id), + link=invite_link + ) + ) + + users = {i.id: i for i in r.users} + + return types.ChatInviteLink._parse(self, r.invite, users) diff --git a/pyrogram/methods/invite_links/get_chat_invite_link_joiners.py b/pyrogram/methods/invite_links/get_chat_invite_link_joiners.py new file mode 100644 index 00000000000..c1fc43a71f8 --- /dev/null +++ b/pyrogram/methods/invite_links/get_chat_invite_link_joiners.py @@ -0,0 +1,87 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union, Optional, AsyncGenerator + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class GetChatInviteLinkJoiners: + async def get_chat_invite_link_joiners( + self: "pyrogram.Client", + chat_id: Union[int, str], + invite_link: str, + limit: int = 0 + ) -> Optional[AsyncGenerator["types.ChatJoiner", None]]: + """Get the members who joined the chat with the invite link. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier for the target chat or username of the target channel/supergroup + (in the format @username). + + invite_link (str): + The invite link. + + limit (``int``, *optional*): + Limits the number of invite links to be retrieved. + By default, no limit is applied and all invite links are returned. + + Returns: + ``Generator``: A generator yielding :obj:`~pyrogram.types.ChatJoiner` objects. + + Yields: + :obj:`~pyrogram.types.ChatJoiner` objects. + """ + current = 0 + total = abs(limit) or (1 << 31) - 1 + limit = min(100, total) + + offset_date = 0 + offset_user = raw.types.InputUserEmpty() + + while True: + r = await self.invoke( + raw.functions.messages.GetChatInviteImporters( + peer=await self.resolve_peer(chat_id), + link=invite_link, + limit=limit, + offset_date=offset_date, + offset_user=offset_user + ) + ) + + if not r.importers: + break + + users = {i.id: i for i in r.users} + + offset_date = r.importers[-1].date + offset_user = await self.resolve_peer(r.importers[-1].user_id) + + for i in r.importers: + yield types.ChatJoiner._parse(self, i, users) + + current += 1 + + if current >= total: + return diff --git a/pyrogram/methods/invite_links/get_chat_invite_link_joiners_count.py b/pyrogram/methods/invite_links/get_chat_invite_link_joiners_count.py new file mode 100644 index 00000000000..c591be1927c --- /dev/null +++ b/pyrogram/methods/invite_links/get_chat_invite_link_joiners_count.py @@ -0,0 +1,56 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw + + +class GetChatInviteLinkJoinersCount: + async def get_chat_invite_link_joiners_count( + self: "pyrogram.Client", + chat_id: Union[int, str], + invite_link: str + ) -> int: + """Get the count of the members who joined the chat with the invite link. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier for the target chat or username of the target channel/supergroup + (in the format @username). + + invite_link (str): + The invite link. + + Returns: + ``int``: On success, the joined chat members count is returned. + """ + r = await self.invoke( + raw.functions.messages.GetChatInviteImporters( + peer=await self.resolve_peer(chat_id), + link=invite_link, + limit=1, + offset_date=0, + offset_user=raw.types.InputUserEmpty() + ) + ) + + return r.count diff --git a/pyrogram/methods/invite_links/get_chat_join_requests.py b/pyrogram/methods/invite_links/get_chat_join_requests.py new file mode 100644 index 00000000000..a75498e2f63 --- /dev/null +++ b/pyrogram/methods/invite_links/get_chat_join_requests.py @@ -0,0 +1,88 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union, Optional, AsyncGenerator + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class GetChatJoinRequests: + async def get_chat_join_requests( + self: "pyrogram.Client", + chat_id: Union[int, str], + limit: int = 0, + query: str = "" + ) -> Optional[AsyncGenerator["types.ChatJoiner", None]]: + """Get the pending join requests of a chat. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier for the target chat or username of the target channel/supergroup + (in the format @username). + + limit (``int``, *optional*): + Limits the number of invite links to be retrieved. + By default, no limit is applied and all invite links are returned. + + query (``str``, *optional*): + Query to search for a user. + + Returns: + ``Generator``: A generator yielding :obj:`~pyrogram.types.ChatJoiner` objects. + + Yields: + :obj:`~pyrogram.types.ChatJoiner` objects. + """ + current = 0 + total = abs(limit) or (1 << 31) - 1 + limit = min(100, total) + + offset_date = 0 + offset_user = raw.types.InputUserEmpty() + + while True: + r = await self.invoke( + raw.functions.messages.GetChatInviteImporters( + peer=await self.resolve_peer(chat_id), + limit=limit, + offset_date=offset_date, + offset_user=offset_user, + requested=True, + q=query + ) + ) + + if not r.importers: + break + + users = {i.id: i for i in r.users} + + offset_date = r.importers[-1].date + offset_user = await self.resolve_peer(r.importers[-1].user_id) + + for i in r.importers: + yield types.ChatJoiner._parse(self, i, users) + + current += 1 + + if current >= total: + return diff --git a/pyrogram/methods/invite_links/revoke_chat_invite_link.py b/pyrogram/methods/invite_links/revoke_chat_invite_link.py new file mode 100644 index 00000000000..ff55a04e039 --- /dev/null +++ b/pyrogram/methods/invite_links/revoke_chat_invite_link.py @@ -0,0 +1,68 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class RevokeChatInviteLink: + async def revoke_chat_invite_link( + self: "pyrogram.Client", + chat_id: Union[int, str], + invite_link: str, + ) -> "types.ChatInviteLink": + """Revoke a previously created invite link. + + If the primary link is revoked, a new link is automatically generated. + + You must be an administrator in the chat for this to work and must have the appropriate admin rights. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier for the target chat or username of the target channel/supergroup + (in the format @username). + + invite_link (``str``): + The invite link to revoke. + + Returns: + :obj:`~pyrogram.types.ChatInviteLink`: On success, the invite link object is returned. + """ + + r = await self.invoke( + raw.functions.messages.EditExportedChatInvite( + peer=await self.resolve_peer(chat_id), + link=invite_link, + revoked=True + ) + ) + + users = {i.id: i for i in r.users} + + chat_invite = ( + r.new_invite + if isinstance(r, raw.types.messages.ExportedChatInviteReplaced) + else r.invite + ) + + return types.ChatInviteLink._parse(self, chat_invite, users) diff --git a/pyrogram/methods/messages/__init__.py b/pyrogram/methods/messages/__init__.py new file mode 100644 index 00000000000..7a5d2d49be2 --- /dev/null +++ b/pyrogram/methods/messages/__init__.py @@ -0,0 +1,119 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 .copy_media_group import CopyMediaGroup +from .copy_message import CopyMessage +from .delete_messages import DeleteMessages +from .download_media import DownloadMedia +from .edit_inline_caption import EditInlineCaption +from .edit_inline_media import EditInlineMedia +from .edit_inline_reply_markup import EditInlineReplyMarkup +from .edit_inline_text import EditInlineText +from .edit_message_caption import EditMessageCaption +from .edit_message_media import EditMessageMedia +from .edit_message_reply_markup import EditMessageReplyMarkup +from .edit_message_text import EditMessageText +from .forward_messages import ForwardMessages +from .get_chat_history import GetChatHistory +from .get_chat_history_count import GetChatHistoryCount +from .get_custom_emoji_stickers import GetCustomEmojiStickers +from .get_discussion_message import GetDiscussionMessage +from .get_discussion_replies import GetDiscussionReplies +from .get_discussion_replies_count import GetDiscussionRepliesCount +from .get_media_group import GetMediaGroup +from .get_messages import GetMessages +from .read_chat_history import ReadChatHistory +from .retract_vote import RetractVote +from .search_global import SearchGlobal +from .search_global_count import SearchGlobalCount +from .search_messages import SearchMessages +from .search_messages_count import SearchMessagesCount +from .send_animation import SendAnimation +from .send_audio import SendAudio +from .send_cached_media import SendCachedMedia +from .send_chat_action import SendChatAction +from .send_contact import SendContact +from .send_dice import SendDice +from .send_document import SendDocument +from .send_location import SendLocation +from .send_media_group import SendMediaGroup +from .send_message import SendMessage +from .send_photo import SendPhoto +from .send_poll import SendPoll +from .send_reaction import SendReaction +from .send_sticker import SendSticker +from .send_venue import SendVenue +from .send_video import SendVideo +from .send_video_note import SendVideoNote +from .send_voice import SendVoice +from .stop_poll import StopPoll +from .stream_media import StreamMedia +from .vote_poll import VotePoll + + +class Messages( + DeleteMessages, + EditMessageCaption, + EditMessageReplyMarkup, + EditMessageMedia, + EditMessageText, + ForwardMessages, + GetMediaGroup, + GetMessages, + SendAudio, + SendChatAction, + SendContact, + SendDocument, + SendAnimation, + SendLocation, + SendMediaGroup, + SendMessage, + SendPhoto, + SendSticker, + SendVenue, + SendVideo, + SendVideoNote, + SendVoice, + SendPoll, + VotePoll, + StopPoll, + RetractVote, + DownloadMedia, + GetChatHistory, + SendCachedMedia, + GetChatHistoryCount, + ReadChatHistory, + EditInlineText, + EditInlineCaption, + EditInlineMedia, + EditInlineReplyMarkup, + SendDice, + SearchMessages, + SearchGlobal, + CopyMessage, + CopyMediaGroup, + SearchMessagesCount, + SearchGlobalCount, + GetDiscussionMessage, + SendReaction, + GetDiscussionReplies, + GetDiscussionRepliesCount, + StreamMedia, + GetCustomEmojiStickers +): + pass diff --git a/pyrogram/methods/messages/copy_media_group.py b/pyrogram/methods/messages/copy_media_group.py new file mode 100644 index 00000000000..52911ea0bc9 --- /dev/null +++ b/pyrogram/methods/messages/copy_media_group.py @@ -0,0 +1,140 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 datetime import datetime +from typing import Union, List + +import pyrogram +from pyrogram import types, utils, raw + + +class CopyMediaGroup: + async def copy_media_group( + self: "pyrogram.Client", + chat_id: Union[int, str], + from_chat_id: Union[int, str], + message_id: int, + captions: Union[List[str], str] = None, + disable_notification: bool = None, + reply_to_message_id: int = None, + schedule_date: datetime = None, + ) -> List["types.Message"]: + """Copy a media group by providing one of the message ids. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + from_chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the source chat where the original media group was sent. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + message_id (``int``): + Message identifier in the chat specified in *from_chat_id*. + + captions (``str`` | List of ``str`` , *optional*): + New caption for media, 0-1024 characters after entities parsing for each media. + If not specified, the original caption is kept. + Pass "" (empty string) to remove the caption. + + If a ``string`` is passed, it becomes a caption only for the first media. + If a list of ``string`` passed, each element becomes caption for each media element. + You can pass ``None`` in list to keep the original caption (see examples below). + + disable_notification (``bool``, *optional*): + Sends the message silently. + Users will receive a notification with no sound. + + reply_to_message_id (``int``, *optional*): + If the message is a reply, ID of the original message. + + schedule_date (:py:obj:`~datetime.datetime`, *optional*): + Date when the message will be automatically sent. + + Returns: + List of :obj:`~pyrogram.types.Message`: On success, a list of copied messages is returned. + + Example: + .. code-block:: python + + # Copy a media group + await app.copy_media_group(to_chat, from_chat, 123) + + await app.copy_media_group(to_chat, from_chat, 123, captions="single caption") + + await app.copy_media_group(to_chat, from_chat, 123, + captions=["caption 1", None, ""]) + """ + + media_group = await self.get_media_group(from_chat_id, message_id) + multi_media = [] + + for i, message in enumerate(media_group): + if message.photo: + file_id = message.photo.file_id + elif message.audio: + file_id = message.audio.file_id + elif message.document: + file_id = message.document.file_id + elif message.video: + file_id = message.video.file_id + else: + raise ValueError("Message with this type can't be copied.") + + media = utils.get_input_media_from_file_id(file_id=file_id) + multi_media.append( + raw.types.InputSingleMedia( + media=media, + random_id=self.rnd_id(), + **await self.parser.parse( + captions[i] if isinstance(captions, list) and i < len(captions) and captions[i] else + captions if isinstance(captions, str) and i == 0 else + message.caption if message.caption and message.caption != "None" and not type( + captions) is str else "") + ) + ) + + r = await self.invoke( + raw.functions.messages.SendMultiMedia( + peer=await self.resolve_peer(chat_id), + multi_media=multi_media, + silent=disable_notification or None, + reply_to_msg_id=reply_to_message_id, + schedule_date=utils.datetime_to_timestamp(schedule_date) + ), + sleep_threshold=60 + ) + + return await utils.parse_messages( + self, + raw.types.messages.Messages( + messages=[m.message for m in filter( + lambda u: isinstance(u, (raw.types.UpdateNewMessage, + raw.types.UpdateNewChannelMessage, + raw.types.UpdateNewScheduledMessage)), + r.updates + )], + users=r.users, + chats=r.chats + ) + ) diff --git a/pyrogram/methods/messages/copy_message.py b/pyrogram/methods/messages/copy_message.py new file mode 100644 index 00000000000..0b6624b28de --- /dev/null +++ b/pyrogram/methods/messages/copy_message.py @@ -0,0 +1,121 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import logging +from datetime import datetime +from typing import Union, List, Optional + +import pyrogram +from pyrogram import types, enums + +log = logging.getLogger(__name__) + + +class CopyMessage: + async def copy_message( + self: "pyrogram.Client", + chat_id: Union[int, str], + from_chat_id: Union[int, str], + message_id: int, + caption: str = None, + parse_mode: Optional["enums.ParseMode"] = None, + caption_entities: List["types.MessageEntity"] = None, + disable_notification: bool = None, + reply_to_message_id: int = None, + schedule_date: datetime = None, + protect_content: bool = None, + reply_markup: Union[ + "types.InlineKeyboardMarkup", + "types.ReplyKeyboardMarkup", + "types.ReplyKeyboardRemove", + "types.ForceReply" + ] = None + ) -> "types.Message": + """Copy messages of any kind. + + The method is analogous to the method :meth:`~Client.forward_messages`, but the copied message doesn't have a + link to the original message. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + from_chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the source chat where the original message was sent. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + message_id (``int``): + Message identifier in the chat specified in *from_chat_id*. + + caption (``string``, *optional*): + New caption for media, 0-1024 characters after entities parsing. + If not specified, the original caption is kept. + Pass "" (empty string) to remove the caption. + + parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*): + By default, texts are parsed using both Markdown and HTML styles. + You can combine both syntaxes together. + + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the new caption, which can be specified instead of *parse_mode*. + + disable_notification (``bool``, *optional*): + Sends the message silently. + Users will receive a notification with no sound. + + reply_to_message_id (``int``, *optional*): + If the message is a reply, ID of the original message. + + schedule_date (:py:obj:`~datetime.datetime`, *optional*): + Date when the message will be automatically sent. + + protect_content (``bool``, *optional*): + Protects the contents of the sent message from forwarding and saving. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*): + Additional interface options. An object for an inline keyboard, custom reply keyboard, + instructions to remove reply keyboard or to force a reply from the user. + + Returns: + :obj:`~pyrogram.types.Message`: On success, the copied message is returned. + + Example: + .. code-block:: python + + # Copy a message + await app.copy_message(to_chat, from_chat, 123) + + """ + message: types.Message = await self.get_messages(from_chat_id, message_id) + + return await message.copy( + chat_id=chat_id, + caption=caption, + parse_mode=parse_mode, + caption_entities=caption_entities, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + schedule_date=schedule_date, + protect_content=protect_content, + reply_markup=reply_markup + ) diff --git a/pyrogram/methods/messages/delete_messages.py b/pyrogram/methods/messages/delete_messages.py new file mode 100644 index 00000000000..07c3a7b85bf --- /dev/null +++ b/pyrogram/methods/messages/delete_messages.py @@ -0,0 +1,84 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union, Iterable + +import pyrogram +from pyrogram import raw + + +class DeleteMessages: + async def delete_messages( + self: "pyrogram.Client", + chat_id: Union[int, str], + message_ids: Union[int, Iterable[int]], + revoke: bool = True + ) -> int: + """Delete messages, including service messages. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + message_ids (``int`` | Iterable of ``int``): + An iterable of message identifiers to delete (integers) or a single message id. + + revoke (``bool``, *optional*): + Deletes messages on both parts. + This is only for private cloud chats and normal groups, messages on + channels and supergroups are always revoked (i.e.: deleted for everyone). + Defaults to True. + + Returns: + ``int``: Amount of affected messages + + Example: + .. code-block:: python + + # Delete one message + await app.delete_messages(chat_id, message_id) + + # Delete multiple messages at once + await app.delete_messages(chat_id, list_of_message_ids) + + # Delete messages only on your side (without revoking) + await app.delete_messages(chat_id, message_id, revoke=False) + """ + peer = await self.resolve_peer(chat_id) + message_ids = list(message_ids) if not isinstance(message_ids, int) else [message_ids] + + if isinstance(peer, raw.types.InputPeerChannel): + r = await self.invoke( + raw.functions.channels.DeleteMessages( + channel=peer, + id=message_ids + ) + ) + else: + r = await self.invoke( + raw.functions.messages.DeleteMessages( + id=message_ids, + revoke=revoke + ) + ) + + return r.pts_count diff --git a/pyrogram/methods/messages/download_media.py b/pyrogram/methods/messages/download_media.py new file mode 100644 index 00000000000..4f44ff254f2 --- /dev/null +++ b/pyrogram/methods/messages/download_media.py @@ -0,0 +1,187 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import asyncio +import os +from datetime import datetime +from typing import Union, Optional, Callable, BinaryIO + +import pyrogram +from pyrogram import types +from pyrogram.file_id import FileId, FileType, PHOTO_TYPES + +DEFAULT_DOWNLOAD_DIR = "downloads/" + + +class DownloadMedia: + async def download_media( + self: "pyrogram.Client", + message: Union["types.Message", str], + file_name: str = DEFAULT_DOWNLOAD_DIR, + in_memory: bool = False, + block: bool = True, + progress: Callable = None, + progress_args: tuple = () + ) -> Optional[Union[str, BinaryIO]]: + """Download the media from a message. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + message (:obj:`~pyrogram.types.Message` | ``str``): + Pass a Message containing the media, the media itself (message.audio, message.video, ...) or a file id + as string. + + file_name (``str``, *optional*): + A custom *file_name* to be used instead of the one provided by Telegram. + By default, all files are downloaded in the *downloads* folder in your working directory. + You can also specify a path for downloading files in a custom location: paths that end with "/" + are considered directories. All non-existent folders will be created automatically. + + in_memory (``bool``, *optional*): + Pass True to download the media in-memory. + A binary file-like object with its attribute ".name" set will be returned. + Defaults to False. + + block (``bool``, *optional*): + Blocks the code execution until the file has been downloaded. + Defaults to True. + + progress (``Callable``, *optional*): + Pass a callback function to view the file transmission progress. + The function must take *(current, total)* as positional arguments (look at Other Parameters below for a + detailed description) and will be called back each time a new file chunk has been successfully + transmitted. + + progress_args (``tuple``, *optional*): + Extra custom arguments for the progress callback function. + You can pass anything you need to be available in the progress callback scope; for example, a Message + object or a Client instance in order to edit the message with the updated progress status. + + Other Parameters: + current (``int``): + The amount of bytes transmitted so far. + + total (``int``): + The total size of the file. + + *args (``tuple``, *optional*): + Extra custom arguments as defined in the ``progress_args`` parameter. + You can either keep ``*args`` or add every single extra argument in your function signature. + + Returns: + ``str`` | ``None`` | ``BinaryIO``: On success, the absolute path of the downloaded file is returned, + otherwise, in case the download failed or was deliberately stopped with + :meth:`~pyrogram.Client.stop_transmission`, None is returned. + Otherwise, in case ``in_memory=True``, a binary file-like object with its attribute ".name" set is returned. + + Raises: + ValueError: if the message doesn't contain any downloadable media + + Example: + Download media to file + + .. code-block:: python + + # Download from Message + await app.download_media(message) + + # Download from file id + await app.download_media(message.photo.file_id) + + # Keep track of the progress while downloading + async def progress(current, total): + print(f"{current * 100 / total:.1f}%") + + await app.download_media(message, progress=progress) + + Download media in-memory + + .. code-block:: python + + file = await app.download_media(message, in_memory=True) + + file_name = file.name + file_bytes = bytes(file.getbuffer()) + """ + available_media = ("audio", "document", "photo", "sticker", "animation", "video", "voice", "video_note", + "new_chat_photo") + + if isinstance(message, types.Message): + for kind in available_media: + media = getattr(message, kind, None) + + if media is not None: + break + else: + raise ValueError("This message doesn't contain any downloadable media") + else: + media = message + + if isinstance(media, str): + file_id_str = media + else: + file_id_str = media.file_id + + file_id_obj = FileId.decode(file_id_str) + + file_type = file_id_obj.file_type + media_file_name = getattr(media, "file_name", "") + file_size = getattr(media, "file_size", 0) + mime_type = getattr(media, "mime_type", "") + date = getattr(media, "date", None) + + directory, file_name = os.path.split(file_name) + file_name = file_name or media_file_name or "" + + if not os.path.isabs(file_name): + directory = self.PARENT_DIR / (directory or DEFAULT_DOWNLOAD_DIR) + + if not file_name: + guessed_extension = self.guess_extension(mime_type) + + if file_type in PHOTO_TYPES: + extension = ".jpg" + elif file_type == FileType.VOICE: + extension = guessed_extension or ".ogg" + elif file_type in (FileType.VIDEO, FileType.ANIMATION, FileType.VIDEO_NOTE): + extension = guessed_extension or ".mp4" + elif file_type == FileType.DOCUMENT: + extension = guessed_extension or ".zip" + elif file_type == FileType.STICKER: + extension = guessed_extension or ".webp" + elif file_type == FileType.AUDIO: + extension = guessed_extension or ".mp3" + else: + extension = ".unknown" + + file_name = "{}_{}_{}{}".format( + FileType(file_id_obj.file_type).name.lower(), + (date or datetime.now()).strftime("%Y-%m-%d_%H-%M-%S"), + self.rnd_id(), + extension + ) + + downloader = self.handle_download( + (file_id_obj, directory, file_name, in_memory, file_size, progress, progress_args) + ) + + if block: + return await downloader + else: + asyncio.get_event_loop().create_task(downloader) diff --git a/pyrogram/methods/messages/edit_inline_caption.py b/pyrogram/methods/messages/edit_inline_caption.py new file mode 100644 index 00000000000..69c73334168 --- /dev/null +++ b/pyrogram/methods/messages/edit_inline_caption.py @@ -0,0 +1,65 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Optional + +import pyrogram +from pyrogram import types, enums + + +class EditInlineCaption: + async def edit_inline_caption( + self: "pyrogram.Client", + inline_message_id: str, + caption: str, + parse_mode: Optional["enums.ParseMode"] = None, + reply_markup: "types.InlineKeyboardMarkup" = None + ) -> bool: + """Edit the caption of inline media messages. + + .. include:: /_includes/usable-by/bots.rst + + Parameters: + inline_message_id (``str``): + Identifier of the inline message. + + caption (``str``): + New caption of the media message. + + parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*): + By default, texts are parsed using both Markdown and HTML styles. + You can combine both syntaxes together. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*): + An InlineKeyboardMarkup object. + + Returns: + ``bool``: On success, True is returned. + + Example: + .. code-block:: python + + # Bots only + await app.edit_inline_caption(inline_message_id, "new media caption") + """ + return await self.edit_inline_text( + inline_message_id=inline_message_id, + text=caption, + parse_mode=parse_mode, + reply_markup=reply_markup + ) diff --git a/pyrogram/methods/messages/edit_inline_media.py b/pyrogram/methods/messages/edit_inline_media.py new file mode 100644 index 00000000000..7ab424a4f2f --- /dev/null +++ b/pyrogram/methods/messages/edit_inline_media.py @@ -0,0 +1,244 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import asyncio +import io +import os +import re + +import pyrogram +from pyrogram import raw +from pyrogram import types +from pyrogram import utils +from pyrogram.errors import RPCError, MediaEmpty +from pyrogram.file_id import FileType +from .inline_session import get_session + + +class EditInlineMedia: + MAX_RETRIES = 3 + + async def edit_inline_media( + self: "pyrogram.Client", + inline_message_id: str, + media: "types.InputMedia", + reply_markup: "types.InlineKeyboardMarkup" = None + ) -> bool: + """Edit inline animation, audio, document, photo or video messages. + + When the inline message is edited, a new file can't be uploaded. Use a previously uploaded file via its file_id + or specify a URL. + + .. include:: /_includes/usable-by/bots.rst + + Parameters: + inline_message_id (``str``): + Required if *chat_id* and *message_id* are not specified. + Identifier of the inline message. + + media (:obj:`~pyrogram.types.InputMedia`): + One of the InputMedia objects describing an animation, audio, document, photo or video. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*): + An InlineKeyboardMarkup object. + + Returns: + ``bool``: On success, True is returned. + + Example: + .. code-block:: python + + from pyrogram.types import InputMediaPhoto, InputMediaVideo, InputMediaAudio + + # Bots only + + # Replace the current media with a local photo + await app.edit_inline_media(inline_message_id, InputMediaPhoto("new_photo.jpg")) + + # Replace the current media with a local video + await app.edit_inline_media(inline_message_id, InputMediaVideo("new_video.mp4")) + + # Replace the current media with a local audio + await app.edit_inline_media(inline_message_id, InputMediaAudio("new_audio.mp3")) + """ + caption = media.caption + parse_mode = media.parse_mode + + is_bytes_io = isinstance(media.media, io.BytesIO) + is_uploaded_file = is_bytes_io or os.path.isfile(media.media) + + is_external_url = not is_uploaded_file and re.match("^https?://", media.media) + + if is_bytes_io and not hasattr(media.media, "name"): + media.media.name = "media" + + if is_uploaded_file: + filename_attribute = [ + raw.types.DocumentAttributeFilename( + file_name=media.media.name if is_bytes_io else os.path.basename(media.media) + ) + ] + else: + filename_attribute = [] + + if isinstance(media, types.InputMediaPhoto): + if is_uploaded_file: + media = raw.types.InputMediaUploadedPhoto( + file=await self.save_file(media.media), + spoiler=media.has_spoiler + ) + elif is_external_url: + media = raw.types.InputMediaPhotoExternal( + url=media.media, + spoiler=media.has_spoiler + ) + else: + media = utils.get_input_media_from_file_id(media.media, FileType.PHOTO) + elif isinstance(media, types.InputMediaVideo): + if is_uploaded_file: + media = raw.types.InputMediaUploadedDocument( + mime_type=(None if is_bytes_io else self.guess_mime_type(media.media)) or "video/mp4", + thumb=await self.save_file(media.thumb), + file=await self.save_file(media.media), + spoiler=media.has_spoiler, + attributes=[ + raw.types.DocumentAttributeVideo( + supports_streaming=media.supports_streaming or None, + duration=media.duration, + w=media.width, + h=media.height + ) + ] + filename_attribute + ) + elif is_external_url: + media = raw.types.InputMediaDocumentExternal( + url=media.media, + spoiler=media.has_spoiler + ) + else: + media = utils.get_input_media_from_file_id(media.media, FileType.VIDEO) + elif isinstance(media, types.InputMediaAudio): + if is_uploaded_file: + media = raw.types.InputMediaUploadedDocument( + mime_type=(None if is_bytes_io else self.guess_mime_type(media.media)) or "audio/mpeg", + thumb=await self.save_file(media.thumb), + file=await self.save_file(media.media), + attributes=[ + raw.types.DocumentAttributeAudio( + duration=media.duration, + performer=media.performer, + title=media.title + ) + ] + filename_attribute + ) + elif is_external_url: + media = raw.types.InputMediaDocumentExternal( + url=media.media + ) + else: + media = utils.get_input_media_from_file_id(media.media, FileType.AUDIO) + elif isinstance(media, types.InputMediaAnimation): + if is_uploaded_file: + media = raw.types.InputMediaUploadedDocument( + mime_type=(None if is_bytes_io else self.guess_mime_type(media.media)) or "video/mp4", + thumb=await self.save_file(media.thumb), + file=await self.save_file(media.media), + spoiler=media.has_spoiler, + attributes=[ + raw.types.DocumentAttributeVideo( + supports_streaming=True, + duration=media.duration, + w=media.width, + h=media.height + ), + raw.types.DocumentAttributeAnimated() + ] + filename_attribute, + nosound_video=True + ) + elif is_external_url: + media = raw.types.InputMediaDocumentExternal( + url=media.media, + spoiler=media.has_spoiler + ) + else: + media = utils.get_input_media_from_file_id(media.media, FileType.ANIMATION) + elif isinstance(media, types.InputMediaDocument): + if is_uploaded_file: + media = raw.types.InputMediaUploadedDocument( + mime_type=(None if is_bytes_io else self.guess_mime_type(media.media)) or "application/zip", + thumb=await self.save_file(media.thumb), + file=await self.save_file(media.media), + attributes=filename_attribute, + force_file=True + ) + elif is_external_url: + media = raw.types.InputMediaDocumentExternal( + url=media.media + ) + else: + media = utils.get_input_media_from_file_id(media.media, FileType.DOCUMENT) + + unpacked = utils.unpack_inline_message_id(inline_message_id) + dc_id = unpacked.dc_id + + session = await get_session(self, dc_id) + + if is_uploaded_file: + uploaded_media = await self.invoke( + raw.functions.messages.UploadMedia( + peer=raw.types.InputPeerSelf(), + media=media + ) + ) + + actual_media = raw.types.InputMediaPhoto( + id=raw.types.InputPhoto( + id=uploaded_media.photo.id, + access_hash=uploaded_media.photo.access_hash, + file_reference=uploaded_media.photo.file_reference + ), + spoiler=getattr(media, "has_spoiler", None) + ) if isinstance(media, types.InputMediaPhoto) else raw.types.InputMediaDocument( + id=raw.types.InputDocument( + id=uploaded_media.document.id, + access_hash=uploaded_media.document.access_hash, + file_reference=uploaded_media.document.file_reference + ), + spoiler=getattr(media, "has_spoiler", None) + ) + else: + actual_media = media + + for i in range(self.MAX_RETRIES): + try: + return await session.invoke( + raw.functions.messages.EditInlineBotMessage( + id=unpacked, + media=actual_media, + reply_markup=await reply_markup.write(self) if reply_markup else None, + **await self.parser.parse(caption, parse_mode) + ), + sleep_threshold=self.sleep_threshold + ) + except RPCError as e: + if i == self.MAX_RETRIES - 1: + raise + + if isinstance(e, MediaEmpty): + # Must wait due to a server race condition + await asyncio.sleep(1) diff --git a/pyrogram/methods/messages/edit_inline_reply_markup.py b/pyrogram/methods/messages/edit_inline_reply_markup.py new file mode 100644 index 00000000000..e2ef40e1602 --- /dev/null +++ b/pyrogram/methods/messages/edit_inline_reply_markup.py @@ -0,0 +1,69 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import pyrogram +from pyrogram import raw +from pyrogram import types +from pyrogram import utils +from .inline_session import get_session + + +class EditInlineReplyMarkup: + async def edit_inline_reply_markup( + self: "pyrogram.Client", + inline_message_id: str, + reply_markup: "types.InlineKeyboardMarkup" = None + ) -> bool: + """Edit only the reply markup of inline messages sent via the bot (for inline bots). + + .. include:: /_includes/usable-by/bots.rst + + Parameters: + inline_message_id (``str``): + Identifier of the inline message. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*): + An InlineKeyboardMarkup object. + + Returns: + ``bool``: On success, True is returned. + + Example: + .. code-block:: python + + from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton + + # Bots only + await app.edit_inline_reply_markup( + inline_message_id, + InlineKeyboardMarkup([[ + InlineKeyboardButton("New button", callback_data="new_data")]])) + """ + + unpacked = utils.unpack_inline_message_id(inline_message_id) + dc_id = unpacked.dc_id + + session = await get_session(self, dc_id) + + return await session.invoke( + raw.functions.messages.EditInlineBotMessage( + id=unpacked, + reply_markup=await reply_markup.write(self) if reply_markup else None, + ), + sleep_threshold=self.sleep_threshold + ) diff --git a/pyrogram/methods/messages/edit_inline_text.py b/pyrogram/methods/messages/edit_inline_text.py new file mode 100644 index 00000000000..354c55a3392 --- /dev/null +++ b/pyrogram/methods/messages/edit_inline_text.py @@ -0,0 +1,88 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Optional + +import pyrogram +from pyrogram import raw, enums +from pyrogram import types +from pyrogram import utils +from .inline_session import get_session + + +class EditInlineText: + async def edit_inline_text( + self: "pyrogram.Client", + inline_message_id: str, + text: str, + parse_mode: Optional["enums.ParseMode"] = None, + disable_web_page_preview: bool = None, + reply_markup: "types.InlineKeyboardMarkup" = None + ) -> bool: + """Edit the text of inline messages. + + .. include:: /_includes/usable-by/bots.rst + + Parameters: + inline_message_id (``str``): + Identifier of the inline message. + + text (``str``): + New text of the message. + + parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*): + By default, texts are parsed using both Markdown and HTML styles. + You can combine both syntaxes together. + + disable_web_page_preview (``bool``, *optional*): + Disables link previews for links in this message. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*): + An InlineKeyboardMarkup object. + + Returns: + ``bool``: On success, True is returned. + + Example: + .. code-block:: python + + # Bots only + + # Simple edit text + await app.edit_inline_text(inline_message_id, "new text") + + # Take the same text message, remove the web page preview only + await app.edit_inline_text( + inline_message_id, message.text, + disable_web_page_preview=True) + """ + + unpacked = utils.unpack_inline_message_id(inline_message_id) + dc_id = unpacked.dc_id + + session = await get_session(self, dc_id) + + return await session.invoke( + raw.functions.messages.EditInlineBotMessage( + id=unpacked, + no_webpage=disable_web_page_preview or None, + reply_markup=await reply_markup.write(self) if reply_markup else None, + **await self.parser.parse(text, parse_mode) + ), + sleep_threshold=self.sleep_threshold + ) diff --git a/pyrogram/methods/messages/edit_message_caption.py b/pyrogram/methods/messages/edit_message_caption.py new file mode 100644 index 00000000000..2e4a45a694a --- /dev/null +++ b/pyrogram/methods/messages/edit_message_caption.py @@ -0,0 +1,76 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union, List, Optional + +import pyrogram +from pyrogram import types, enums + + +class EditMessageCaption: + async def edit_message_caption( + self: "pyrogram.Client", + chat_id: Union[int, str], + message_id: int, + caption: str, + parse_mode: Optional["enums.ParseMode"] = None, + caption_entities: List["types.MessageEntity"] = None, + reply_markup: "types.InlineKeyboardMarkup" = None + ) -> "types.Message": + """Edit the caption of media messages. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + message_id (``int``): + Message identifier in the chat specified in chat_id. + + caption (``str``): + New caption of the media message. + + parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*): + By default, texts are parsed using both Markdown and HTML styles. + You can combine both syntaxes together. + + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*): + An InlineKeyboardMarkup object. + + Returns: + :obj:`~pyrogram.types.Message`: On success, the edited message is returned. + + Example: + .. code-block:: python + + await app.edit_message_caption(chat_id, message_id, "new media caption") + """ + return await self.edit_message_text( + chat_id=chat_id, + message_id=message_id, + text=caption, + parse_mode=parse_mode, + entities=caption_entities, + reply_markup=reply_markup + ) diff --git a/pyrogram/methods/messages/edit_message_media.py b/pyrogram/methods/messages/edit_message_media.py new file mode 100644 index 00000000000..5a34f13875e --- /dev/null +++ b/pyrogram/methods/messages/edit_message_media.py @@ -0,0 +1,287 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import io +import os +import re +from typing import Union + +import pyrogram +from pyrogram import raw +from pyrogram import types +from pyrogram import utils +from pyrogram.file_id import FileType + + +class EditMessageMedia: + async def edit_message_media( + self: "pyrogram.Client", + chat_id: Union[int, str], + message_id: int, + media: "types.InputMedia", + reply_markup: "types.InlineKeyboardMarkup" = None, + file_name: str = None + ) -> "types.Message": + """Edit animation, audio, document, photo or video messages. + + If a message is a part of a message album, then it can be edited only to a photo or a video. Otherwise, the + message type can be changed arbitrarily. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + message_id (``int``): + Message identifier in the chat specified in chat_id. + + media (:obj:`~pyrogram.types.InputMedia`): + One of the InputMedia objects describing an animation, audio, document, photo or video. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*): + An InlineKeyboardMarkup object. + + file_name (``str``, *optional*): + File name of the media to be sent. Not applicable to photos. + Defaults to file's path basename. + + Returns: + :obj:`~pyrogram.types.Message`: On success, the edited message is returned. + + Example: + .. code-block:: python + + from pyrogram.types import InputMediaPhoto, InputMediaVideo, InputMediaAudio + + # Replace the current media with a local photo + await app.edit_message_media(chat_id, message_id, + InputMediaPhoto("new_photo.jpg")) + + # Replace the current media with a local video + await app.edit_message_media(chat_id, message_id, + InputMediaVideo("new_video.mp4")) + + # Replace the current media with a local audio + await app.edit_message_media(chat_id, message_id, + InputMediaAudio("new_audio.mp3")) + """ + caption = media.caption + parse_mode = media.parse_mode + + message, entities = None, None + + if caption is not None: + message, entities = (await self.parser.parse(caption, parse_mode)).values() + + if isinstance(media, types.InputMediaPhoto): + if isinstance(media.media, io.BytesIO) or os.path.isfile(media.media): + uploaded_media = await self.invoke( + raw.functions.messages.UploadMedia( + peer=await self.resolve_peer(chat_id), + media=raw.types.InputMediaUploadedPhoto( + file=await self.save_file(media.media), + spoiler=media.has_spoiler + ) + ) + ) + + media = raw.types.InputMediaPhoto( + id=raw.types.InputPhoto( + id=uploaded_media.photo.id, + access_hash=uploaded_media.photo.access_hash, + file_reference=uploaded_media.photo.file_reference + ), + spoiler=media.has_spoiler + ) + elif re.match("^https?://", media.media): + media = raw.types.InputMediaPhotoExternal( + url=media.media, + spoiler=media.has_spoiler + ) + else: + media = utils.get_input_media_from_file_id(media.media, FileType.PHOTO) + elif isinstance(media, types.InputMediaVideo): + if isinstance(media.media, io.BytesIO) or os.path.isfile(media.media): + uploaded_media = await self.invoke( + raw.functions.messages.UploadMedia( + peer=await self.resolve_peer(chat_id), + media=raw.types.InputMediaUploadedDocument( + mime_type=self.guess_mime_type(media.media) or "video/mp4", + thumb=await self.save_file(media.thumb), + spoiler=media.has_spoiler, + file=await self.save_file(media.media), + attributes=[ + raw.types.DocumentAttributeVideo( + supports_streaming=media.supports_streaming or None, + duration=media.duration, + w=media.width, + h=media.height + ), + raw.types.DocumentAttributeFilename( + file_name=file_name or os.path.basename(media.media) + ) + ] + ) + ) + ) + + media = raw.types.InputMediaDocument( + id=raw.types.InputDocument( + id=uploaded_media.document.id, + access_hash=uploaded_media.document.access_hash, + file_reference=uploaded_media.document.file_reference + ), + spoiler=media.has_spoiler + ) + elif re.match("^https?://", media.media): + media = raw.types.InputMediaDocumentExternal( + url=media.media, + spoiler=media.has_spoiler + ) + else: + media = utils.get_input_media_from_file_id(media.media, FileType.VIDEO) + elif isinstance(media, types.InputMediaAudio): + if isinstance(media.media, io.BytesIO) or os.path.isfile(media.media): + media = await self.invoke( + raw.functions.messages.UploadMedia( + peer=await self.resolve_peer(chat_id), + media=raw.types.InputMediaUploadedDocument( + mime_type=self.guess_mime_type(media.media) or "audio/mpeg", + thumb=await self.save_file(media.thumb), + file=await self.save_file(media.media), + attributes=[ + raw.types.DocumentAttributeAudio( + duration=media.duration, + performer=media.performer, + title=media.title + ), + raw.types.DocumentAttributeFilename( + file_name=file_name or os.path.basename(media.media) + ) + ] + ) + ) + ) + + media = raw.types.InputMediaDocument( + id=raw.types.InputDocument( + id=media.document.id, + access_hash=media.document.access_hash, + file_reference=media.document.file_reference + ) + ) + elif re.match("^https?://", media.media): + media = raw.types.InputMediaDocumentExternal( + url=media.media + ) + else: + media = utils.get_input_media_from_file_id(media.media, FileType.AUDIO) + elif isinstance(media, types.InputMediaAnimation): + if isinstance(media.media, io.BytesIO) or os.path.isfile(media.media): + uploaded_media = await self.invoke( + raw.functions.messages.UploadMedia( + peer=await self.resolve_peer(chat_id), + media=raw.types.InputMediaUploadedDocument( + mime_type=self.guess_mime_type(media.media) or "video/mp4", + thumb=await self.save_file(media.thumb), + spoiler=media.has_spoiler, + file=await self.save_file(media.media), + attributes=[ + raw.types.DocumentAttributeVideo( + supports_streaming=True, + duration=media.duration, + w=media.width, + h=media.height + ), + raw.types.DocumentAttributeFilename( + file_name=file_name or os.path.basename(media.media) + ), + raw.types.DocumentAttributeAnimated() + ] + ) + ) + ) + + media = raw.types.InputMediaDocument( + id=raw.types.InputDocument( + id=uploaded_media.document.id, + access_hash=uploaded_media.document.access_hash, + file_reference=uploaded_media.document.file_reference + ), + spoiler=media.has_spoiler + ) + elif re.match("^https?://", media.media): + media = raw.types.InputMediaDocumentExternal( + url=media.media, + spoiler=media.has_spoiler + ) + else: + media = utils.get_input_media_from_file_id(media.media, FileType.ANIMATION) + elif isinstance(media, types.InputMediaDocument): + if isinstance(media.media, io.BytesIO) or os.path.isfile(media.media): + media = await self.invoke( + raw.functions.messages.UploadMedia( + peer=await self.resolve_peer(chat_id), + media=raw.types.InputMediaUploadedDocument( + mime_type=self.guess_mime_type(media.media) or "application/zip", + thumb=await self.save_file(media.thumb), + file=await self.save_file(media.media), + attributes=[ + raw.types.DocumentAttributeFilename( + file_name=file_name or os.path.basename(media.media) + ) + ] + ) + ) + ) + + media = raw.types.InputMediaDocument( + id=raw.types.InputDocument( + id=media.document.id, + access_hash=media.document.access_hash, + file_reference=media.document.file_reference + ) + ) + elif re.match("^https?://", media.media): + media = raw.types.InputMediaDocumentExternal( + url=media.media + ) + else: + media = utils.get_input_media_from_file_id(media.media, FileType.DOCUMENT) + + r = await self.invoke( + raw.functions.messages.EditMessage( + peer=await self.resolve_peer(chat_id), + id=message_id, + media=media, + reply_markup=await reply_markup.write(self) if reply_markup else None, + message=message, + entities=entities + ) + ) + + for i in r.updates: + if isinstance(i, (raw.types.UpdateEditMessage, raw.types.UpdateEditChannelMessage)): + return await types.Message._parse( + self, i.message, + {i.id: i for i in r.users}, + {i.id: i for i in r.chats} + ) diff --git a/pyrogram/methods/messages/edit_message_reply_markup.py b/pyrogram/methods/messages/edit_message_reply_markup.py new file mode 100644 index 00000000000..1cd75c1a8b5 --- /dev/null +++ b/pyrogram/methods/messages/edit_message_reply_markup.py @@ -0,0 +1,77 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class EditMessageReplyMarkup: + async def edit_message_reply_markup( + self: "pyrogram.Client", + chat_id: Union[int, str], + message_id: int, + reply_markup: "types.InlineKeyboardMarkup" = None, + ) -> "types.Message": + """Edit only the reply markup of messages sent by the bot. + + .. include:: /_includes/usable-by/bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + message_id (``int``): + Message identifier in the chat specified in chat_id. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*): + An InlineKeyboardMarkup object. + + Returns: + :obj:`~pyrogram.types.Message`: On success, the edited message is returned. + + Example: + .. code-block:: python + + from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton + + # Bots only + await app.edit_message_reply_markup( + chat_id, message_id, + InlineKeyboardMarkup([[ + InlineKeyboardButton("New button", callback_data="new_data")]])) + """ + r = await self.invoke( + raw.functions.messages.EditMessage( + peer=await self.resolve_peer(chat_id), + id=message_id, + reply_markup=await reply_markup.write(self) if reply_markup else None, + ) + ) + + for i in r.updates: + if isinstance(i, (raw.types.UpdateEditMessage, raw.types.UpdateEditChannelMessage)): + return await types.Message._parse( + self, i.message, + {i.id: i for i in r.users}, + {i.id: i for i in r.chats} + ) diff --git a/pyrogram/methods/messages/edit_message_text.py b/pyrogram/methods/messages/edit_message_text.py new file mode 100644 index 00000000000..540b6aa63ce --- /dev/null +++ b/pyrogram/methods/messages/edit_message_text.py @@ -0,0 +1,98 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union, List, Optional + +import pyrogram +from pyrogram import raw, enums +from pyrogram import types +from pyrogram import utils + + +class EditMessageText: + async def edit_message_text( + self: "pyrogram.Client", + chat_id: Union[int, str], + message_id: int, + text: str, + parse_mode: Optional["enums.ParseMode"] = None, + entities: List["types.MessageEntity"] = None, + disable_web_page_preview: bool = None, + reply_markup: "types.InlineKeyboardMarkup" = None + ) -> "types.Message": + """Edit the text of messages. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + message_id (``int``): + Message identifier in the chat specified in chat_id. + + text (``str``): + New text of the message. + + parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*): + By default, texts are parsed using both Markdown and HTML styles. + You can combine both syntaxes together. + + entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in message text, which can be specified instead of *parse_mode*. + + disable_web_page_preview (``bool``, *optional*): + Disables link previews for links in this message. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*): + An InlineKeyboardMarkup object. + + Returns: + :obj:`~pyrogram.types.Message`: On success, the edited message is returned. + + Example: + .. code-block:: python + + # Simple edit text + await app.edit_message_text(chat_id, message_id, "new text") + + # Take the same text message, remove the web page preview only + await app.edit_message_text( + chat_id, message_id, message.text, + disable_web_page_preview=True) + """ + + r = await self.invoke( + raw.functions.messages.EditMessage( + peer=await self.resolve_peer(chat_id), + id=message_id, + no_webpage=disable_web_page_preview or None, + reply_markup=await reply_markup.write(self) if reply_markup else None, + **await utils.parse_text_entities(self, text, parse_mode, entities) + ) + ) + + for i in r.updates: + if isinstance(i, (raw.types.UpdateEditMessage, raw.types.UpdateEditChannelMessage)): + return await types.Message._parse( + self, i.message, + {i.id: i for i in r.users}, + {i.id: i for i in r.chats} + ) diff --git a/pyrogram/methods/messages/forward_messages.py b/pyrogram/methods/messages/forward_messages.py new file mode 100644 index 00000000000..8635e17101c --- /dev/null +++ b/pyrogram/methods/messages/forward_messages.py @@ -0,0 +1,110 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 datetime import datetime +from typing import Union, List, Iterable + +import pyrogram +from pyrogram import raw, utils +from pyrogram import types + + +class ForwardMessages: + async def forward_messages( + self: "pyrogram.Client", + chat_id: Union[int, str], + from_chat_id: Union[int, str], + message_ids: Union[int, Iterable[int]], + disable_notification: bool = None, + schedule_date: datetime = None, + protect_content: bool = None + ) -> Union["types.Message", List["types.Message"]]: + """Forward messages of any kind. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + from_chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the source chat where the original message was sent. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + message_ids (``int`` | Iterable of ``int``): + An iterable of message identifiers in the chat specified in *from_chat_id* or a single message id. + + disable_notification (``bool``, *optional*): + Sends the message silently. + Users will receive a notification with no sound. + + schedule_date (:py:obj:`~datetime.datetime`, *optional*): + Date when the message will be automatically sent. + + protect_content (``bool``, *optional*): + Protects the contents of the sent message from forwarding and saving. + + Returns: + :obj:`~pyrogram.types.Message` | List of :obj:`~pyrogram.types.Message`: In case *message_ids* was not + a list, a single message is returned, otherwise a list of messages is returned. + + Example: + .. code-block:: python + + # Forward a single message + await app.forward_messages(to_chat, from_chat, 123) + + # Forward multiple messages at once + await app.forward_messages(to_chat, from_chat, [1, 2, 3]) + """ + + is_iterable = not isinstance(message_ids, int) + message_ids = list(message_ids) if is_iterable else [message_ids] + + r = await self.invoke( + raw.functions.messages.ForwardMessages( + to_peer=await self.resolve_peer(chat_id), + from_peer=await self.resolve_peer(from_chat_id), + id=message_ids, + silent=disable_notification or None, + random_id=[self.rnd_id() for _ in message_ids], + schedule_date=utils.datetime_to_timestamp(schedule_date), + noforwards=protect_content + ) + ) + + forwarded_messages = [] + + users = {i.id: i for i in r.users} + chats = {i.id: i for i in r.chats} + + for i in r.updates: + if isinstance(i, (raw.types.UpdateNewMessage, + raw.types.UpdateNewChannelMessage, + raw.types.UpdateNewScheduledMessage)): + forwarded_messages.append( + await types.Message._parse( + self, i.message, + users, chats + ) + ) + + return types.List(forwarded_messages) if is_iterable else forwarded_messages[0] diff --git a/pyrogram/methods/messages/get_chat_history.py b/pyrogram/methods/messages/get_chat_history.py new file mode 100644 index 00000000000..b384c6ba8f5 --- /dev/null +++ b/pyrogram/methods/messages/get_chat_history.py @@ -0,0 +1,121 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 datetime import datetime +from typing import Union, Optional, AsyncGenerator + +import pyrogram +from pyrogram import types, raw, utils + + +async def get_chunk( + *, + client: "pyrogram.Client", + chat_id: Union[int, str], + limit: int = 0, + offset: int = 0, + from_message_id: int = 0, + from_date: datetime = utils.zero_datetime() +): + messages = await client.invoke( + raw.functions.messages.GetHistory( + peer=await client.resolve_peer(chat_id), + offset_id=from_message_id, + offset_date=utils.datetime_to_timestamp(from_date), + add_offset=offset, + limit=limit, + max_id=0, + min_id=0, + hash=0 + ), + sleep_threshold=60 + ) + + return await utils.parse_messages(client, messages, replies=0) + + +class GetChatHistory: + async def get_chat_history( + self: "pyrogram.Client", + chat_id: Union[int, str], + limit: int = 0, + offset: int = 0, + offset_id: int = 0, + offset_date: datetime = utils.zero_datetime() + ) -> Optional[AsyncGenerator["types.Message", None]]: + """Get messages from a chat history. + + The messages are returned in reverse chronological order. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + limit (``int``, *optional*): + Limits the number of messages to be retrieved. + By default, no limit is applied and all messages are returned. + + offset (``int``, *optional*): + Sequential number of the first message to be returned.. + Negative values are also accepted and become useful in case you set offset_id or offset_date. + + offset_id (``int``, *optional*): + Identifier of the first message to be returned. + + offset_date (:py:obj:`~datetime.datetime`, *optional*): + Pass a date as offset to retrieve only older messages starting from that date. + + Returns: + ``Generator``: A generator yielding :obj:`~pyrogram.types.Message` objects. + + Example: + .. code-block:: python + + async for message in app.get_chat_history(chat_id): + print(message.text) + """ + current = 0 + total = limit or (1 << 31) - 1 + limit = min(100, total) + + while True: + messages = await get_chunk( + client=self, + chat_id=chat_id, + limit=limit, + offset=offset, + from_message_id=offset_id, + from_date=offset_date + ) + + if not messages: + return + + offset_id = messages[-1].id + + for message in messages: + yield message + + current += 1 + + if current >= total: + return diff --git a/pyrogram/methods/messages/get_chat_history_count.py b/pyrogram/methods/messages/get_chat_history_count.py new file mode 100644 index 00000000000..1926224b357 --- /dev/null +++ b/pyrogram/methods/messages/get_chat_history_count.py @@ -0,0 +1,72 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import logging +from typing import Union + +import pyrogram +from pyrogram import raw + +log = logging.getLogger(__name__) + + +class GetChatHistoryCount: + async def get_chat_history_count( + self: "pyrogram.Client", + chat_id: Union[int, str] + ) -> int: + """Get the total count of messages in a chat. + + .. note:: + + Due to Telegram latest internal changes, the server can't reliably find anymore the total count of messages + a **private** or a **basic group** chat has with a single method call. To overcome this limitation, Pyrogram + has to iterate over all the messages. Channels and supergroups are not affected by this limitation. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + Returns: + ``int``: On success, the chat history count is returned. + + Example: + .. code-block:: python + + await app.get_history_count(chat_id) + """ + + r = await self.invoke( + raw.functions.messages.GetHistory( + peer=await self.resolve_peer(chat_id), + offset_id=0, + offset_date=0, + add_offset=0, + limit=1, + max_id=0, + min_id=0, + hash=0 + ) + ) + + if isinstance(r, raw.types.messages.Messages): + return len(r.messages) + else: + return r.count diff --git a/pyrogram/methods/messages/get_custom_emoji_stickers.py b/pyrogram/methods/messages/get_custom_emoji_stickers.py new file mode 100644 index 00000000000..7a71f0587c0 --- /dev/null +++ b/pyrogram/methods/messages/get_custom_emoji_stickers.py @@ -0,0 +1,55 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import List + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class GetCustomEmojiStickers: + async def get_custom_emoji_stickers( + self: "pyrogram.Client", + custom_emoji_ids: List[int], + ) -> List["types.Sticker"]: + """Get information about custom emoji stickers by their identifiers. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + custom_emoji_ids (List of ``int``): + List of custom emoji identifiers. + At most 200 custom emoji identifiers can be specified. + + Returns: + List of :obj:`~pyrogram.types.Sticker`: On success, a list of sticker objects is returned. + """ + result = await self.invoke( + raw.functions.messages.GetCustomEmojiDocuments( + document_id=custom_emoji_ids + ) + ) + + stickers = [] + for item in result: + attributes = {type(i): i for i in item.attributes} + sticker = await types.Sticker._parse(self, item, attributes) + stickers.append(sticker) + + return pyrogram.types.List(stickers) diff --git a/pyrogram/methods/messages/get_discussion_message.py b/pyrogram/methods/messages/get_discussion_message.py new file mode 100644 index 00000000000..58a6b7044de --- /dev/null +++ b/pyrogram/methods/messages/get_discussion_message.py @@ -0,0 +1,65 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class GetDiscussionMessage: + async def get_discussion_message( + self: "pyrogram.Client", + chat_id: Union[int, str], + message_id: int, + ) -> "types.Message": + """Get the first discussion message of a channel post or a discussion thread in a group. + + Reply to the returned message to leave a comment on the linked channel post or to continue + the discussion thread. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + message_id (``int``): + Message id. + + Example: + .. code-block:: python + + # Get the discussion message + m = await app.get_discussion_message(channel_id, message_id) + + # Comment to the post by replying + await m.reply("comment") + """ + r = await self.invoke( + raw.functions.messages.GetDiscussionMessage( + peer=await self.resolve_peer(chat_id), + msg_id=message_id + ) + ) + + users = {u.id: u for u in r.users} + chats = {c.id: c for c in r.chats} + + return await types.Message._parse(self, r.messages[0], users, chats) diff --git a/pyrogram/methods/messages/get_discussion_replies.py b/pyrogram/methods/messages/get_discussion_replies.py new file mode 100644 index 00000000000..dd23751bb12 --- /dev/null +++ b/pyrogram/methods/messages/get_discussion_replies.py @@ -0,0 +1,86 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union, Optional, AsyncGenerator + +import pyrogram +from pyrogram import types, raw + + +class GetDiscussionReplies: + async def get_discussion_replies( + self: "pyrogram.Client", + chat_id: Union[int, str], + message_id: int, + limit: int = 0, + ) -> Optional[AsyncGenerator["types.Message", None]]: + """Get the message replies of a discussion thread. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + message_id (``int``): + Message id. + + limit (``int``, *optional*): + Limits the number of messages to be retrieved. + By default, no limit is applied and all messages are returned. + + Example: + .. code-block:: python + + async for message in app.get_discussion_replies(chat_id, message_id): + print(message) + """ + + current = 0 + total = limit or (1 << 31) - 1 + limit = min(100, total) + + while True: + r = await self.invoke( + raw.functions.messages.GetReplies( + peer=await self.resolve_peer(chat_id), + msg_id=message_id, + offset_id=0, + offset_date=0, + add_offset=current, + limit=limit, + max_id=0, + min_id=0, + hash=0 + ) + ) + + users = {u.id: u for u in r.users} + chats = {c.id: c for c in r.chats} + messages = r.messages + + if not messages: + return + + for message in messages: + yield await types.Message._parse(self, message, users, chats, replies=0) + + current += 1 + + if current >= total: + return diff --git a/pyrogram/methods/messages/get_discussion_replies_count.py b/pyrogram/methods/messages/get_discussion_replies_count.py new file mode 100644 index 00000000000..bbb90ac3a24 --- /dev/null +++ b/pyrogram/methods/messages/get_discussion_replies_count.py @@ -0,0 +1,62 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw + + +class GetDiscussionRepliesCount: + async def get_discussion_replies_count( + self: "pyrogram.Client", + chat_id: Union[int, str], + message_id: int, + ) -> int: + """Get the total count of replies in a discussion thread. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + message_id (``int``): + Message id. + + Example: + .. code-block:: python + + count = await app.get_discussion_replies_count(chat_id, message_id) + """ + + r = await self.invoke( + raw.functions.messages.GetReplies( + peer=await self.resolve_peer(chat_id), + msg_id=message_id, + offset_id=0, + offset_date=0, + add_offset=0, + limit=1, + max_id=0, + min_id=0, + hash=0 + ) + ) + + return r.count diff --git a/pyrogram/methods/messages/get_media_group.py b/pyrogram/methods/messages/get_media_group.py new file mode 100644 index 00000000000..97770d90f58 --- /dev/null +++ b/pyrogram/methods/messages/get_media_group.py @@ -0,0 +1,73 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import logging +from typing import Union, List + +import pyrogram +from pyrogram import types + +log = logging.getLogger(__name__) + + +class GetMediaGroup: + async def get_media_group( + self: "pyrogram.Client", + chat_id: Union[int, str], + message_id: int + ) -> List["types.Message"]: + """Get the media group a message belongs to. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + message_id (``int``): + The id of one of the messages that belong to the media group. + + Returns: + List of :obj:`~pyrogram.types.Message`: On success, a list of messages of the media group is returned. + + Raises: + ValueError: + In case the passed message_id is negative or equal 0. + In case target message doesn't belong to a media group. + """ + + if message_id <= 0: + raise ValueError("Passed message_id is negative or equal to zero.") + + # Get messages with id from `id - 9` to `id + 10` to get all possible media group messages. + messages = await self.get_messages( + chat_id=chat_id, + message_ids=[msg_id for msg_id in range(message_id - 9, message_id + 10)], + replies=0 + ) + + # There can be maximum 10 items in a media group. + # If/else condition to fix the problem of getting correct `media_group_id` when `message_id` is less than 10. + media_group_id = messages[9].media_group_id if len(messages) == 19 else messages[message_id - 1].media_group_id + + if media_group_id is None: + raise ValueError("The message doesn't belong to a media group") + + return types.List(msg for msg in messages if msg.media_group_id == media_group_id) diff --git a/pyrogram/methods/messages/get_messages.py b/pyrogram/methods/messages/get_messages.py new file mode 100644 index 00000000000..3f398c36eb9 --- /dev/null +++ b/pyrogram/methods/messages/get_messages.py @@ -0,0 +1,119 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import logging +from typing import Union, List, Iterable + +import pyrogram +from pyrogram import raw +from pyrogram import types +from pyrogram import utils + +log = logging.getLogger(__name__) + + +# TODO: Rewrite using a flag for replied messages and have message_ids non-optional + + +class GetMessages: + async def get_messages( + self: "pyrogram.Client", + chat_id: Union[int, str], + message_ids: Union[int, Iterable[int]] = None, + reply_to_message_ids: Union[int, Iterable[int]] = None, + replies: int = 1 + ) -> Union["types.Message", List["types.Message"]]: + """Get one or more messages from a chat by using message identifiers. + + You can retrieve up to 200 messages at once. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + message_ids (``int`` | Iterable of ``int``, *optional*): + Pass a single message identifier or an iterable of message ids (as integers) to get the content of the + message themselves. + + reply_to_message_ids (``int`` | Iterable of ``int``, *optional*): + Pass a single message identifier or an iterable of message ids (as integers) to get the content of + the previous message you replied to using this message. + If *message_ids* is set, this argument will be ignored. + + replies (``int``, *optional*): + The number of subsequent replies to get for each message. + Pass 0 for no reply at all or -1 for unlimited replies. + Defaults to 1. + + Returns: + :obj:`~pyrogram.types.Message` | List of :obj:`~pyrogram.types.Message`: In case *message_ids* was not + a list, a single message is returned, otherwise a list of messages is returned. + + Example: + .. code-block:: python + + # Get one message + await app.get_messages(chat_id, 12345) + + # Get more than one message (list of messages) + await app.get_messages(chat_id, [12345, 12346]) + + # Get message by ignoring any replied-to message + await app.get_messages(chat_id, message_id, replies=0) + + # Get message with all chained replied-to messages + await app.get_messages(chat_id, message_id, replies=-1) + + # Get the replied-to message of a message + await app.get_messages(chat_id, reply_to_message_ids=message_id) + + Raises: + ValueError: In case of invalid arguments. + """ + ids, ids_type = ( + (message_ids, raw.types.InputMessageID) if message_ids + else (reply_to_message_ids, raw.types.InputMessageReplyTo) if reply_to_message_ids + else (None, None) + ) + + if ids is None: + raise ValueError("No argument supplied. Either pass message_ids or reply_to_message_ids") + + peer = await self.resolve_peer(chat_id) + + is_iterable = not isinstance(ids, int) + ids = list(ids) if is_iterable else [ids] + ids = [ids_type(id=i) for i in ids] + + if replies < 0: + replies = (1 << 31) - 1 + + if isinstance(peer, raw.types.InputPeerChannel): + rpc = raw.functions.channels.GetMessages(channel=peer, id=ids) + else: + rpc = raw.functions.messages.GetMessages(id=ids) + + r = await self.invoke(rpc, sleep_threshold=-1) + + messages = await utils.parse_messages(self, r, replies=replies) + + return messages if is_iterable else messages[0] if messages else None diff --git a/pyrogram/methods/messages/inline_session.py b/pyrogram/methods/messages/inline_session.py new file mode 100644 index 00000000000..a514f5a68c9 --- /dev/null +++ b/pyrogram/methods/messages/inline_session.py @@ -0,0 +1,64 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import pyrogram +from pyrogram import raw +from pyrogram.errors import AuthBytesInvalid +from pyrogram.session import Session +from pyrogram.session.auth import Auth + + +async def get_session(client: "pyrogram.Client", dc_id: int): + if dc_id == await client.storage.dc_id(): + return client + + async with client.media_sessions_lock: + if client.media_sessions.get(dc_id): + return client.media_sessions[dc_id] + + session = client.media_sessions[dc_id] = Session( + client, dc_id, + await Auth(client, dc_id, await client.storage.test_mode()).create(), + await client.storage.test_mode(), is_media=True + ) + + await session.start() + + for _ in range(3): + exported_auth = await client.invoke( + raw.functions.auth.ExportAuthorization( + dc_id=dc_id + ) + ) + + try: + await session.invoke( + raw.functions.auth.ImportAuthorization( + id=exported_auth.id, + bytes=exported_auth.bytes + ) + ) + except AuthBytesInvalid: + continue + else: + break + else: + await session.stop() + raise AuthBytesInvalid + + return session diff --git a/pyrogram/methods/messages/read_chat_history.py b/pyrogram/methods/messages/read_chat_history.py new file mode 100644 index 00000000000..4b962739369 --- /dev/null +++ b/pyrogram/methods/messages/read_chat_history.py @@ -0,0 +1,73 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw + + +class ReadChatHistory: + async def read_chat_history( + self: "pyrogram.Client", + chat_id: Union[int, str], + max_id: int = 0 + ) -> bool: + """Mark a chat's message history as read. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + max_id (``int``, *optional*): + The id of the last message you want to mark as read; all the messages before this one will be marked as + read as well. Defaults to 0 (mark every unread message as read). + + Returns: + ``bool`` - On success, True is returned. + + Example: + .. code-block:: python + + # Mark the whole chat as read + await app.read_chat_history(chat_id) + + # Mark messages as read only up to the given message id + await app.read_chat_history(chat_id, 12345) + """ + + peer = await self.resolve_peer(chat_id) + + if isinstance(peer, raw.types.InputPeerChannel): + q = raw.functions.channels.ReadHistory( + channel=peer, + max_id=max_id + ) + else: + q = raw.functions.messages.ReadHistory( + peer=peer, + max_id=max_id + ) + + await self.invoke(q) + + return True diff --git a/pyrogram/methods/messages/retract_vote.py b/pyrogram/methods/messages/retract_vote.py new file mode 100644 index 00000000000..c456f4d08be --- /dev/null +++ b/pyrogram/methods/messages/retract_vote.py @@ -0,0 +1,61 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class RetractVote: + async def retract_vote( + self: "pyrogram.Client", + chat_id: Union[int, str], + message_id: int + ) -> "types.Poll": + """Retract your vote in a poll. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + message_id (``int``): + Identifier of the original message with the poll. + + Returns: + :obj:`~pyrogram.types.Poll`: On success, the poll with the retracted vote is returned. + + Example: + .. code-block:: python + + await app.retract_vote(chat_id, message_id) + """ + r = await self.invoke( + raw.functions.messages.SendVote( + peer=await self.resolve_peer(chat_id), + msg_id=message_id, + options=[] + ) + ) + + return types.Poll._parse(self, r.updates[0]) diff --git a/pyrogram/methods/messages/search_global.py b/pyrogram/methods/messages/search_global.py new file mode 100644 index 00000000000..f566c981505 --- /dev/null +++ b/pyrogram/methods/messages/search_global.py @@ -0,0 +1,117 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import AsyncGenerator, Optional + +import pyrogram +from pyrogram import raw, enums +from pyrogram import types +from pyrogram import utils + + +class SearchGlobal: + async def search_global( + self: "pyrogram.Client", + query: str = "", + filter: "enums.MessagesFilter" = enums.MessagesFilter.EMPTY, + limit: int = 0, + ) -> Optional[AsyncGenerator["types.Message", None]]: + """Search messages globally from all of your chats. + + If you want to get the messages count only, see :meth:`~pyrogram.Client.search_global_count`. + + .. note:: + + Due to server-side limitations, you can only get up to around ~10,000 messages and each message + retrieved will not have any *reply_to_message* field. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + query (``str``, *optional*): + Text query string. + Use "@" to search for mentions. + + filter (:obj:`~pyrogram.enums.MessagesFilter`, *optional*): + Pass a filter in order to search for specific kind of messages only. + Defaults to any message (no filter). + + limit (``int``, *optional*): + Limits the number of messages to be retrieved. + By default, no limit is applied and all messages are returned. + + Returns: + ``Generator``: A generator yielding :obj:`~pyrogram.types.Message` objects. + + Example: + .. code-block:: python + + from pyrogram import enums + + # Search for "pyrogram". Get the first 50 results + async for message in app.search_global("pyrogram", limit=50): + print(message.text) + + # Search for recent photos from Global. Get the first 20 results + async for message in app.search_global(filter=enums.MessagesFilter.PHOTO, limit=20): + print(message.photo) + """ + current = 0 + # There seems to be an hard limit of 10k, beyond which Telegram starts spitting one message at a time. + total = abs(limit) or (1 << 31) + limit = min(100, total) + + offset_date = 0 + offset_peer = raw.types.InputPeerEmpty() + offset_id = 0 + + while True: + messages = await utils.parse_messages( + self, + await self.invoke( + raw.functions.messages.SearchGlobal( + q=query, + filter=filter.value(), + min_date=0, + max_date=0, + offset_rate=offset_date, + offset_peer=offset_peer, + offset_id=offset_id, + limit=limit + ), + sleep_threshold=60 + ), + replies=0 + ) + + if not messages: + return + + last = messages[-1] + + offset_date = utils.datetime_to_timestamp(last.date) + offset_peer = await self.resolve_peer(last.chat.id) + offset_id = last.id + + for message in messages: + yield message + + current += 1 + + if current >= total: + return diff --git a/pyrogram/methods/messages/search_global_count.py b/pyrogram/methods/messages/search_global_count.py new file mode 100644 index 00000000000..8323a821d27 --- /dev/null +++ b/pyrogram/methods/messages/search_global_count.py @@ -0,0 +1,62 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import pyrogram +from pyrogram import raw, enums + + +class SearchGlobalCount: + async def search_global_count( + self: "pyrogram.Client", + query: str = "", + filter: "enums.MessagesFilter" = enums.MessagesFilter.EMPTY, + ) -> int: + """Get the count of messages resulting from a global search. + + If you want to get the actual messages, see :meth:`~pyrogram.Client.search_global`. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + query (``str``, *optional*): + Text query string. + Use "@" to search for mentions. + + filter (:obj:`~pyrogram.enums.MessagesFilter`, *optional*): + Pass a filter in order to search for specific kind of messages only: + + Returns: + ``int``: On success, the messages count is returned. + """ + r = await self.invoke( + raw.functions.messages.SearchGlobal( + q=query, + filter=filter.value(), + min_date=0, + max_date=0, + offset_rate=0, + offset_peer=raw.types.InputPeerEmpty(), + offset_id=0, + limit=1 + ) + ) + + if hasattr(r, "count"): + return r.count + else: + return len(r.messages) diff --git a/pyrogram/methods/messages/search_messages.py b/pyrogram/methods/messages/search_messages.py new file mode 100644 index 00000000000..4497ff1cd3b --- /dev/null +++ b/pyrogram/methods/messages/search_messages.py @@ -0,0 +1,151 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union, List, AsyncGenerator, Optional + +import pyrogram +from pyrogram import raw, types, utils, enums + + +# noinspection PyShadowingBuiltins +async def get_chunk( + client, + chat_id: Union[int, str], + query: str = "", + filter: "enums.MessagesFilter" = enums.MessagesFilter.EMPTY, + offset: int = 0, + limit: int = 100, + from_user: Union[int, str] = None +) -> List["types.Message"]: + r = await client.invoke( + raw.functions.messages.Search( + peer=await client.resolve_peer(chat_id), + q=query, + filter=filter.value(), + min_date=0, + max_date=0, + offset_id=0, + add_offset=offset, + limit=limit, + min_id=0, + max_id=0, + from_id=( + await client.resolve_peer(from_user) + if from_user + else None + ), + hash=0 + ), + sleep_threshold=60 + ) + + return await utils.parse_messages(client, r, replies=0) + + +class SearchMessages: + # noinspection PyShadowingBuiltins + async def search_messages( + self: "pyrogram.Client", + chat_id: Union[int, str], + query: str = "", + offset: int = 0, + filter: "enums.MessagesFilter" = enums.MessagesFilter.EMPTY, + limit: int = 0, + from_user: Union[int, str] = None + ) -> Optional[AsyncGenerator["types.Message", None]]: + """Search for text and media messages inside a specific chat. + + If you want to get the messages count only, see :meth:`~pyrogram.Client.search_messages_count`. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + query (``str``, *optional*): + Text query string. + Required for text-only messages, optional for media messages (see the ``filter`` argument). + When passed while searching for media messages, the query will be applied to captions. + Defaults to "" (empty string). + + offset (``int``, *optional*): + Sequential number of the first message to be returned. + Defaults to 0. + + filter (:obj:`~pyrogram.enums.MessagesFilter`, *optional*): + Pass a filter in order to search for specific kind of messages only. + Defaults to any message (no filter). + + limit (``int``, *optional*): + Limits the number of messages to be retrieved. + By default, no limit is applied and all messages are returned. + + from_user (``int`` | ``str``, *optional*): + Unique identifier (int) or username (str) of the target user you want to search for messages from. + + Returns: + ``Generator``: A generator yielding :obj:`~pyrogram.types.Message` objects. + + Example: + .. code-block:: python + + from pyrogram import enums + + # Search for text messages in chat. Get the last 120 results + async for message in app.search_messages(chat_id, query="hello", limit=120): + print(message.text) + + # Search for pinned messages in chat + async for message in app.search_messages(chat_id, filter=enums.MessagesFilter.PINNED): + print(message.text) + + # Search for messages containing "hello" sent by yourself in chat + async for message in app.search_messages(chat, "hello", from_user="me"): + print(message.text) + """ + + current = 0 + total = abs(limit) or (1 << 31) - 1 + limit = min(100, total) + + while True: + messages = await get_chunk( + client=self, + chat_id=chat_id, + query=query, + filter=filter, + offset=offset, + limit=limit, + from_user=from_user + ) + + if not messages: + return + + offset += len(messages) + + for message in messages: + yield message + + current += 1 + + if current >= total: + return diff --git a/pyrogram/methods/messages/search_messages_count.py b/pyrogram/methods/messages/search_messages_count.py new file mode 100644 index 00000000000..ea9ae4d18e6 --- /dev/null +++ b/pyrogram/methods/messages/search_messages_count.py @@ -0,0 +1,84 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw, enums + + +class SearchMessagesCount: + async def search_messages_count( + self: "pyrogram.Client", + chat_id: Union[int, str], + query: str = "", + filter: "enums.MessagesFilter" = enums.MessagesFilter.EMPTY, + from_user: Union[int, str] = None + ) -> int: + """Get the count of messages resulting from a search inside a chat. + + If you want to get the actual messages, see :meth:`~pyrogram.Client.search_messages`. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + query (``str``, *optional*): + Text query string. + Required for text-only messages, optional for media messages (see the ``filter`` argument). + When passed while searching for media messages, the query will be applied to captions. + Defaults to "" (empty string). + + filter (:obj:`~pyrogram.enums.MessagesFilter`, *optional*): + Pass a filter in order to search for specific kind of messages only: + + from_user (``int`` | ``str``, *optional*): + Unique identifier (int) or username (str) of the target user you want to search for messages from. + + Returns: + ``int``: On success, the messages count is returned. + """ + r = await self.invoke( + raw.functions.messages.Search( + peer=await self.resolve_peer(chat_id), + q=query, + filter=filter.value(), + min_date=0, + max_date=0, + offset_id=0, + add_offset=0, + limit=1, + min_id=0, + max_id=0, + from_id=( + await self.resolve_peer(from_user) + if from_user + else None + ), + hash=0 + ) + ) + + if hasattr(r, "count"): + return r.count + else: + return len(r.messages) diff --git a/pyrogram/methods/messages/send_animation.py b/pyrogram/methods/messages/send_animation.py new file mode 100644 index 00000000000..bac16bac9f7 --- /dev/null +++ b/pyrogram/methods/messages/send_animation.py @@ -0,0 +1,271 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import os +import re +from datetime import datetime +from typing import Union, BinaryIO, List, Optional, Callable + +import pyrogram +from pyrogram import StopTransmission, enums +from pyrogram import raw +from pyrogram import types +from pyrogram import utils +from pyrogram.errors import FilePartMissing +from pyrogram.file_id import FileType + + +class SendAnimation: + async def send_animation( + self: "pyrogram.Client", + chat_id: Union[int, str], + animation: Union[str, BinaryIO], + caption: str = "", + unsave: bool = False, + parse_mode: Optional["enums.ParseMode"] = None, + caption_entities: List["types.MessageEntity"] = None, + has_spoiler: bool = None, + duration: int = 0, + width: int = 0, + height: int = 0, + thumb: Union[str, BinaryIO] = None, + file_name: str = None, + disable_notification: bool = None, + reply_to_message_id: int = None, + schedule_date: datetime = None, + protect_content: bool = None, + reply_markup: Union[ + "types.InlineKeyboardMarkup", + "types.ReplyKeyboardMarkup", + "types.ReplyKeyboardRemove", + "types.ForceReply" + ] = None, + progress: Callable = None, + progress_args: tuple = () + ) -> Optional["types.Message"]: + """Send animation files (animation or H.264/MPEG-4 AVC video without sound). + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + animation (``str`` | ``BinaryIO``): + Animation to send. + Pass a file_id as string to send an animation that exists on the Telegram servers, + pass an HTTP URL as a string for Telegram to get an animation from the Internet, + pass a file path as string to upload a new animation that exists on your local machine, or + pass a binary file-like object with its attribute ".name" set for in-memory uploads. + + caption (``str``, *optional*): + Animation caption, 0-1024 characters. + + unsave (``bool``, *optional*): + By default, the server will save into your own collection any new animation you send. + Pass True to automatically unsave the sent animation. Defaults to False. + + parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*): + By default, texts are parsed using both Markdown and HTML styles. + You can combine both syntaxes together. + + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + + has_spoiler (``bool``, *optional*): + Pass True if the animation needs to be covered with a spoiler animation. + + duration (``int``, *optional*): + Duration of sent animation in seconds. + + width (``int``, *optional*): + Animation width. + + height (``int``, *optional*): + Animation height. + + thumb (``str`` | ``BinaryIO``, *optional*): + Thumbnail of the animation file sent. + The thumbnail should be in JPEG format and less than 200 KB in size. + A thumbnail's width and height should not exceed 320 pixels. + Thumbnails can't be reused and can be only uploaded as a new file. + + file_name (``str``, *optional*): + File name of the animation sent. + Defaults to file's path basename. + + disable_notification (``bool``, *optional*): + Sends the message silently. + Users will receive a notification with no sound. + + reply_to_message_id (``int``, *optional*): + If the message is a reply, ID of the original message. + + schedule_date (:py:obj:`~datetime.datetime`, *optional*): + Date when the message will be automatically sent. + + protect_content (``bool``, *optional*): + Protects the contents of the sent message from forwarding and saving. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*): + Additional interface options. An object for an inline keyboard, custom reply keyboard, + instructions to remove reply keyboard or to force a reply from the user. + + progress (``Callable``, *optional*): + Pass a callback function to view the file transmission progress. + The function must take *(current, total)* as positional arguments (look at Other Parameters below for a + detailed description) and will be called back each time a new file chunk has been successfully + transmitted. + + progress_args (``tuple``, *optional*): + Extra custom arguments for the progress callback function. + You can pass anything you need to be available in the progress callback scope; for example, a Message + object or a Client instance in order to edit the message with the updated progress status. + + Other Parameters: + current (``int``): + The amount of bytes transmitted so far. + + total (``int``): + The total size of the file. + + *args (``tuple``, *optional*): + Extra custom arguments as defined in the ``progress_args`` parameter. + You can either keep ``*args`` or add every single extra argument in your function signature. + + Returns: + :obj:`~pyrogram.types.Message` | ``None``: On success, the sent animation message is returned, otherwise, + in case the upload is deliberately stopped with :meth:`~pyrogram.Client.stop_transmission`, None is + returned. + + Example: + .. code-block:: python + + # Send animation by uploading from local file + await app.send_animation("me", "animation.gif") + + # Add caption to the animation + await app.send_animation("me", "animation.gif", caption="animation caption") + + # Unsave the animation once is sent + await app.send_animation("me", "animation.gif", unsave=True) + + # Keep track of the progress while uploading + async def progress(current, total): + print(f"{current * 100 / total:.1f}%") + + await app.send_animation("me", "animation.gif", progress=progress) + """ + file = None + + try: + if isinstance(animation, str): + if os.path.isfile(animation): + thumb = await self.save_file(thumb) + file = await self.save_file(animation, progress=progress, progress_args=progress_args) + media = raw.types.InputMediaUploadedDocument( + mime_type=self.guess_mime_type(animation) or "video/mp4", + file=file, + thumb=thumb, + spoiler=has_spoiler, + attributes=[ + raw.types.DocumentAttributeVideo( + supports_streaming=True, + duration=duration, + w=width, + h=height + ), + raw.types.DocumentAttributeFilename(file_name=file_name or os.path.basename(animation)), + raw.types.DocumentAttributeAnimated() + ] + ) + elif re.match("^https?://", animation): + media = raw.types.InputMediaDocumentExternal( + url=animation, + spoiler=has_spoiler + ) + else: + media = utils.get_input_media_from_file_id(animation, FileType.ANIMATION) + else: + thumb = await self.save_file(thumb) + file = await self.save_file(animation, progress=progress, progress_args=progress_args) + media = raw.types.InputMediaUploadedDocument( + mime_type=self.guess_mime_type(file_name or animation.name) or "video/mp4", + file=file, + thumb=thumb, + spoiler=has_spoiler, + attributes=[ + raw.types.DocumentAttributeVideo( + supports_streaming=True, + duration=duration, + w=width, + h=height + ), + raw.types.DocumentAttributeFilename(file_name=file_name or animation.name), + raw.types.DocumentAttributeAnimated() + ] + ) + + while True: + try: + r = await self.invoke( + raw.functions.messages.SendMedia( + peer=await self.resolve_peer(chat_id), + media=media, + silent=disable_notification or None, + reply_to_msg_id=reply_to_message_id, + random_id=self.rnd_id(), + schedule_date=utils.datetime_to_timestamp(schedule_date), + noforwards=protect_content, + reply_markup=await reply_markup.write(self) if reply_markup else None, + **await utils.parse_text_entities(self, caption, parse_mode, caption_entities) + ) + ) + except FilePartMissing as e: + await self.save_file(animation, file_id=file.id, file_part=e.value) + else: + for i in r.updates: + if isinstance(i, (raw.types.UpdateNewMessage, + raw.types.UpdateNewChannelMessage, + raw.types.UpdateNewScheduledMessage)): + message = await types.Message._parse( + self, i.message, + {i.id: i for i in r.users}, + {i.id: i for i in r.chats}, + is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage) + ) + + if unsave: + document = message.animation or message.document + document_id = utils.get_input_media_from_file_id( + document.file_id, FileType.ANIMATION + ).id + + await self.invoke( + raw.functions.messages.SaveGif( + id=document_id, + unsave=True + ) + ) + + return message + + except StopTransmission: + return None diff --git a/pyrogram/methods/messages/send_audio.py b/pyrogram/methods/messages/send_audio.py new file mode 100644 index 00000000000..7a81df01954 --- /dev/null +++ b/pyrogram/methods/messages/send_audio.py @@ -0,0 +1,242 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import os +import re +from datetime import datetime +from typing import Union, BinaryIO, List, Optional, Callable + +import pyrogram +from pyrogram import StopTransmission, enums +from pyrogram import raw +from pyrogram import types +from pyrogram import utils +from pyrogram.errors import FilePartMissing +from pyrogram.file_id import FileType + + +class SendAudio: + async def send_audio( + self: "pyrogram.Client", + chat_id: Union[int, str], + audio: Union[str, BinaryIO], + caption: str = "", + parse_mode: Optional["enums.ParseMode"] = None, + caption_entities: List["types.MessageEntity"] = None, + duration: int = 0, + performer: str = None, + title: str = None, + thumb: Union[str, BinaryIO] = None, + file_name: str = None, + disable_notification: bool = None, + reply_to_message_id: int = None, + schedule_date: datetime = None, + protect_content: bool = None, + reply_markup: Union[ + "types.InlineKeyboardMarkup", + "types.ReplyKeyboardMarkup", + "types.ReplyKeyboardRemove", + "types.ForceReply" + ] = None, + progress: Callable = None, + progress_args: tuple = () + ) -> Optional["types.Message"]: + """Send audio files. + + For sending voice messages, use the :meth:`~pyrogram.Client.send_voice` method instead. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + audio (``str`` | ``BinaryIO``): + Audio file to send. + Pass a file_id as string to send an audio file that exists on the Telegram servers, + pass an HTTP URL as a string for Telegram to get an audio file from the Internet, + pass a file path as string to upload a new audio file that exists on your local machine, or + pass a binary file-like object with its attribute ".name" set for in-memory uploads. + + caption (``str``, *optional*): + Audio caption, 0-1024 characters. + + parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*): + By default, texts are parsed using both Markdown and HTML styles. + You can combine both syntaxes together. + + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + + duration (``int``, *optional*): + Duration of the audio in seconds. + + performer (``str``, *optional*): + Performer. + + title (``str``, *optional*): + Track name. + + thumb (``str`` | ``BinaryIO``, *optional*): + Thumbnail of the music file album cover. + The thumbnail should be in JPEG format and less than 200 KB in size. + A thumbnail's width and height should not exceed 320 pixels. + Thumbnails can't be reused and can be only uploaded as a new file. + + file_name (``str``, *optional*): + File name of the audio sent. + Defaults to file's path basename. + + disable_notification (``bool``, *optional*): + Sends the message silently. + Users will receive a notification with no sound. + + reply_to_message_id (``int``, *optional*): + If the message is a reply, ID of the original message. + + schedule_date (:py:obj:`~datetime.datetime`, *optional*): + Date when the message will be automatically sent. + + protect_content (``bool``, *optional*): + Protects the contents of the sent message from forwarding and saving. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*): + Additional interface options. An object for an inline keyboard, custom reply keyboard, + instructions to remove reply keyboard or to force a reply from the user. + + progress (``Callable``, *optional*): + Pass a callback function to view the file transmission progress. + The function must take *(current, total)* as positional arguments (look at Other Parameters below for a + detailed description) and will be called back each time a new file chunk has been successfully + transmitted. + + progress_args (``tuple``, *optional*): + Extra custom arguments for the progress callback function. + You can pass anything you need to be available in the progress callback scope; for example, a Message + object or a Client instance in order to edit the message with the updated progress status. + + Other Parameters: + current (``int``): + The amount of bytes transmitted so far. + + total (``int``): + The total size of the file. + + *args (``tuple``, *optional*): + Extra custom arguments as defined in the ``progress_args`` parameter. + You can either keep ``*args`` or add every single extra argument in your function signature. + + Returns: + :obj:`~pyrogram.types.Message` | ``None``: On success, the sent audio message is returned, otherwise, in + case the upload is deliberately stopped with :meth:`~pyrogram.Client.stop_transmission`, None is returned. + + Example: + .. code-block:: python + + # Send audio file by uploading from file + await app.send_audio("me", "audio.mp3") + + # Add caption to the audio + await app.send_audio("me", "audio.mp3", caption="audio caption") + + # Set audio metadata + await app.send_audio( + "me", "audio.mp3", + title="Title", performer="Performer", duration=234) + + # Keep track of the progress while uploading + async def progress(current, total): + print(f"{current * 100 / total:.1f}%") + + await app.send_audio("me", "audio.mp3", progress=progress) + """ + file = None + + try: + if isinstance(audio, str): + if os.path.isfile(audio): + thumb = await self.save_file(thumb) + file = await self.save_file(audio, progress=progress, progress_args=progress_args) + media = raw.types.InputMediaUploadedDocument( + mime_type=self.guess_mime_type(audio) or "audio/mpeg", + file=file, + thumb=thumb, + attributes=[ + raw.types.DocumentAttributeAudio( + duration=duration, + performer=performer, + title=title + ), + raw.types.DocumentAttributeFilename(file_name=file_name or os.path.basename(audio)) + ] + ) + elif re.match("^https?://", audio): + media = raw.types.InputMediaDocumentExternal( + url=audio + ) + else: + media = utils.get_input_media_from_file_id(audio, FileType.AUDIO) + else: + thumb = await self.save_file(thumb) + file = await self.save_file(audio, progress=progress, progress_args=progress_args) + media = raw.types.InputMediaUploadedDocument( + mime_type=self.guess_mime_type(file_name or audio.name) or "audio/mpeg", + file=file, + thumb=thumb, + attributes=[ + raw.types.DocumentAttributeAudio( + duration=duration, + performer=performer, + title=title + ), + raw.types.DocumentAttributeFilename(file_name=file_name or audio.name) + ] + ) + + while True: + try: + r = await self.invoke( + raw.functions.messages.SendMedia( + peer=await self.resolve_peer(chat_id), + media=media, + silent=disable_notification or None, + reply_to_msg_id=reply_to_message_id, + random_id=self.rnd_id(), + schedule_date=utils.datetime_to_timestamp(schedule_date), + noforwards=protect_content, + reply_markup=await reply_markup.write(self) if reply_markup else None, + **await utils.parse_text_entities(self, caption, parse_mode, caption_entities) + ) + ) + except FilePartMissing as e: + await self.save_file(audio, file_id=file.id, file_part=e.value) + else: + for i in r.updates: + if isinstance(i, (raw.types.UpdateNewMessage, + raw.types.UpdateNewChannelMessage, + raw.types.UpdateNewScheduledMessage)): + return await types.Message._parse( + self, i.message, + {i.id: i for i in r.users}, + {i.id: i for i in r.chats}, + is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage) + ) + except StopTransmission: + return None diff --git a/pyrogram/methods/messages/send_cached_media.py b/pyrogram/methods/messages/send_cached_media.py new file mode 100644 index 00000000000..5a9e2e1f1a2 --- /dev/null +++ b/pyrogram/methods/messages/send_cached_media.py @@ -0,0 +1,124 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 datetime import datetime +from typing import Union, List, Optional + +import pyrogram +from pyrogram import raw, enums +from pyrogram import types +from pyrogram import utils + + +class SendCachedMedia: + async def send_cached_media( + self: "pyrogram.Client", + chat_id: Union[int, str], + file_id: str, + caption: str = "", + parse_mode: Optional["enums.ParseMode"] = None, + caption_entities: List["types.MessageEntity"] = None, + disable_notification: bool = None, + reply_to_message_id: int = None, + schedule_date: datetime = None, + protect_content: bool = None, + reply_markup: Union[ + "types.InlineKeyboardMarkup", + "types.ReplyKeyboardMarkup", + "types.ReplyKeyboardRemove", + "types.ForceReply" + ] = None + ) -> Optional["types.Message"]: + """Send any media stored on the Telegram servers using a file_id. + + This convenience method works with any valid file_id only. + It does the same as calling the relevant method for sending media using a file_id, thus saving you from the + hassle of using the correct method for the media the file_id is pointing to. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + file_id (``str``): + Media to send. + Pass a file_id as string to send a media that exists on the Telegram servers. + + caption (``str``, *optional*): + Media caption, 0-1024 characters. + + parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*): + By default, texts are parsed using both Markdown and HTML styles. + You can combine both syntaxes together. + + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + + disable_notification (``bool``, *optional*): + Sends the message silently. + Users will receive a notification with no sound. + + reply_to_message_id (``int``, *optional*): + If the message is a reply, ID of the original message. + + schedule_date (:py:obj:`~datetime.datetime`, *optional*): + Date when the message will be automatically sent. + + protect_content (``bool``, *optional*): + Protects the contents of the sent message from forwarding and saving. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*): + Additional interface options. An object for an inline keyboard, custom reply keyboard, + instructions to remove reply keyboard or to force a reply from the user. + + Returns: + :obj:`~pyrogram.types.Message`: On success, the sent media message is returned. + + Example: + .. code-block:: python + + await app.send_cached_media("me", file_id) + """ + + r = await self.invoke( + raw.functions.messages.SendMedia( + peer=await self.resolve_peer(chat_id), + media=utils.get_input_media_from_file_id(file_id), + silent=disable_notification or None, + reply_to_msg_id=reply_to_message_id, + random_id=self.rnd_id(), + schedule_date=utils.datetime_to_timestamp(schedule_date), + noforwards=protect_content, + reply_markup=await reply_markup.write(self) if reply_markup else None, + **await utils.parse_text_entities(self, caption, parse_mode, caption_entities) + ) + ) + + for i in r.updates: + if isinstance(i, (raw.types.UpdateNewMessage, + raw.types.UpdateNewChannelMessage, + raw.types.UpdateNewScheduledMessage)): + return await types.Message._parse( + self, i.message, + {i.id: i for i in r.users}, + {i.id: i for i in r.chats}, + is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage) + ) diff --git a/pyrogram/methods/messages/send_chat_action.py b/pyrogram/methods/messages/send_chat_action.py new file mode 100644 index 00000000000..44b16628f86 --- /dev/null +++ b/pyrogram/methods/messages/send_chat_action.py @@ -0,0 +1,80 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw, enums + + +class SendChatAction: + async def send_chat_action( + self: "pyrogram.Client", + chat_id: Union[int, str], + action: "enums.ChatAction" + ) -> bool: + """Tell the other party that something is happening on your side. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + action (:obj:`~pyrogram.enums.ChatAction`): + Type of action to broadcast. + + Returns: + ``bool``: On success, True is returned. + + Raises: + ValueError: In case the provided string is not a valid chat action. + + Example: + .. code-block:: python + + from pyrogram import enums + + # Send "typing" chat action + await app.send_chat_action(chat_id, enums.ChatAction.TYPING) + + # Send "upload_video" chat action + await app.send_chat_action(chat_id, enums.ChatAction.UPLOAD_VIDEO) + + # Send "playing" chat action + await app.send_chat_action(chat_id, enums.ChatAction.PLAYING) + + # Cancel any current chat action + await app.send_chat_action(chat_id, enums.ChatAction.CANCEL) + """ + + action_name = action.name.lower() + + if "upload" in action_name or "history" in action_name: + action = action.value(progress=0) + else: + action = action.value() + + return await self.invoke( + raw.functions.messages.SetTyping( + peer=await self.resolve_peer(chat_id), + action=action + ) + ) diff --git a/pyrogram/methods/messages/send_contact.py b/pyrogram/methods/messages/send_contact.py new file mode 100644 index 00000000000..30f7abd3159 --- /dev/null +++ b/pyrogram/methods/messages/send_contact.py @@ -0,0 +1,121 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 datetime import datetime +from typing import Union + +import pyrogram +from pyrogram import raw, utils +from pyrogram import types + + +class SendContact: + async def send_contact( + self: "pyrogram.Client", + chat_id: Union[int, str], + phone_number: str, + first_name: str, + last_name: str = None, + vcard: str = None, + disable_notification: bool = None, + reply_to_message_id: int = None, + schedule_date: datetime = None, + protect_content: bool = None, + reply_markup: Union[ + "types.InlineKeyboardMarkup", + "types.ReplyKeyboardMarkup", + "types.ReplyKeyboardRemove", + "types.ForceReply" + ] = None + ) -> "types.Message": + """Send phone contacts. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + phone_number (``str``): + Contact's phone number. + + first_name (``str``): + Contact's first name. + + last_name (``str``, *optional*): + Contact's last name. + + vcard (``str``, *optional*): + Additional data about the contact in the form of a vCard, 0-2048 bytes + + disable_notification (``bool``, *optional*): + Sends the message silently. + Users will receive a notification with no sound. + + reply_to_message_id (``int``, *optional*): + If the message is a reply, ID of the original message. + + schedule_date (:py:obj:`~datetime.datetime`, *optional*): + Date when the message will be automatically sent. + + protect_content (``bool``, *optional*): + Protects the contents of the sent message from forwarding and saving. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*): + Additional interface options. An object for an inline keyboard, custom reply keyboard, + instructions to remove reply keyboard or to force a reply from the user. + + Returns: + :obj:`~pyrogram.types.Message`: On success, the sent contact message is returned. + + Example: + .. code-block:: python + + await app.send_contact("me", "+1-123-456-7890", "Name") + """ + r = await self.invoke( + raw.functions.messages.SendMedia( + peer=await self.resolve_peer(chat_id), + media=raw.types.InputMediaContact( + phone_number=phone_number, + first_name=first_name, + last_name=last_name or "", + vcard=vcard or "" + ), + message="", + silent=disable_notification or None, + reply_to_msg_id=reply_to_message_id, + random_id=self.rnd_id(), + schedule_date=utils.datetime_to_timestamp(schedule_date), + noforwards=protect_content, + reply_markup=await reply_markup.write(self) if reply_markup else None + ) + ) + + for i in r.updates: + if isinstance(i, (raw.types.UpdateNewMessage, + raw.types.UpdateNewChannelMessage, + raw.types.UpdateNewScheduledMessage)): + return await types.Message._parse( + self, i.message, + {i.id: i for i in r.users}, + {i.id: i for i in r.chats}, + is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage) + ) diff --git a/pyrogram/methods/messages/send_dice.py b/pyrogram/methods/messages/send_dice.py new file mode 100644 index 00000000000..c657d85dd98 --- /dev/null +++ b/pyrogram/methods/messages/send_dice.py @@ -0,0 +1,116 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 datetime import datetime +from typing import Union, Optional + +import pyrogram +from pyrogram import raw, utils +from pyrogram import types + + +class SendDice: + async def send_dice( + self: "pyrogram.Client", + chat_id: Union[int, str], + emoji: str = "🎲", + disable_notification: bool = None, + reply_to_message_id: int = None, + schedule_date: datetime = None, + protect_content: bool = None, + reply_markup: Union[ + "types.InlineKeyboardMarkup", + "types.ReplyKeyboardMarkup", + "types.ReplyKeyboardRemove", + "types.ForceReply" + ] = None + ) -> Optional["types.Message"]: + """Send a dice with a random value from 1 to 6. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + emoji (``str``, *optional*): + Emoji on which the dice throw animation is based. + Currently, must be one of "🎲", "🎯", "🏀", "⚽", "🎳", or "🎰". + Dice can have values 1-6 for "🎲", "🎯" and "🎳", values 1-5 for "🏀" and "⚽", and + values 1-64 for "🎰". + Defaults to "🎲". + + disable_notification (``bool``, *optional*): + Sends the message silently. + Users will receive a notification with no sound. + + reply_to_message_id (``int``, *optional*): + If the message is a reply, ID of the original message. + + schedule_date (:py:obj:`~datetime.datetime`, *optional*): + Date when the message will be automatically sent. + + protect_content (``bool``, *optional*): + Protects the contents of the sent message from forwarding and saving. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*): + Additional interface options. An object for an inline keyboard, custom reply keyboard, + instructions to remove reply keyboard or to force a reply from the user. + + Returns: + :obj:`~pyrogram.types.Message`: On success, the sent dice message is returned. + + Example: + .. code-block:: python + + # Send a dice + await app.send_dice(chat_id) + + # Send a dart + await app.send_dice(chat_id, "🎯") + + # Send a basketball + await app.send_dice(chat_id, "🏀") + """ + + r = await self.invoke( + raw.functions.messages.SendMedia( + peer=await self.resolve_peer(chat_id), + media=raw.types.InputMediaDice(emoticon=emoji), + silent=disable_notification or None, + reply_to_msg_id=reply_to_message_id, + random_id=self.rnd_id(), + schedule_date=utils.datetime_to_timestamp(schedule_date), + noforwards=protect_content, + reply_markup=await reply_markup.write(self) if reply_markup else None, + message="" + ) + ) + + for i in r.updates: + if isinstance(i, (raw.types.UpdateNewMessage, + raw.types.UpdateNewChannelMessage, + raw.types.UpdateNewScheduledMessage)): + return await types.Message._parse( + self, i.message, + {i.id: i for i in r.users}, + {i.id: i for i in r.chats}, + is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage) + ) diff --git a/pyrogram/methods/messages/send_document.py b/pyrogram/methods/messages/send_document.py new file mode 100644 index 00000000000..b0a4a531e33 --- /dev/null +++ b/pyrogram/methods/messages/send_document.py @@ -0,0 +1,220 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import os +import re +from datetime import datetime +from typing import Union, BinaryIO, List, Optional, Callable + +import pyrogram +from pyrogram import StopTransmission, enums +from pyrogram import raw +from pyrogram import types +from pyrogram import utils +from pyrogram.errors import FilePartMissing +from pyrogram.file_id import FileType + + +class SendDocument: + async def send_document( + self: "pyrogram.Client", + chat_id: Union[int, str], + document: Union[str, BinaryIO], + thumb: Union[str, BinaryIO] = None, + caption: str = "", + parse_mode: Optional["enums.ParseMode"] = None, + caption_entities: List["types.MessageEntity"] = None, + file_name: str = None, + force_document: bool = None, + disable_notification: bool = None, + reply_to_message_id: int = None, + schedule_date: datetime = None, + protect_content: bool = None, + reply_markup: Union[ + "types.InlineKeyboardMarkup", + "types.ReplyKeyboardMarkup", + "types.ReplyKeyboardRemove", + "types.ForceReply" + ] = None, + progress: Callable = None, + progress_args: tuple = () + ) -> Optional["types.Message"]: + """Send generic files. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + document (``str`` | ``BinaryIO``): + File to send. + Pass a file_id as string to send a file that exists on the Telegram servers, + pass an HTTP URL as a string for Telegram to get a file from the Internet, + pass a file path as string to upload a new file that exists on your local machine, or + pass a binary file-like object with its attribute ".name" set for in-memory uploads. + + thumb (``str`` | ``BinaryIO``, *optional*): + Thumbnail of the file sent. + The thumbnail should be in JPEG format and less than 200 KB in size. + A thumbnail's width and height should not exceed 320 pixels. + Thumbnails can't be reused and can be only uploaded as a new file. + + caption (``str``, *optional*): + Document caption, 0-1024 characters. + + parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*): + By default, texts are parsed using both Markdown and HTML styles. + You can combine both syntaxes together. + + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + + file_name (``str``, *optional*): + File name of the document sent. + Defaults to file's path basename. + + force_document (``bool``, *optional*): + Pass True to force sending files as document. Useful for video files that need to be sent as + document messages instead of video messages. + Defaults to False. + + disable_notification (``bool``, *optional*): + Sends the message silently. + Users will receive a notification with no sound. + + reply_to_message_id (``int``, *optional*): + If the message is a reply, ID of the original message. + + schedule_date (:py:obj:`~datetime.datetime`, *optional*): + Date when the message will be automatically sent. + + protect_content (``bool``, *optional*): + Protects the contents of the sent message from forwarding and saving. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*): + Additional interface options. An object for an inline keyboard, custom reply keyboard, + instructions to remove reply keyboard or to force a reply from the user. + + progress (``Callable``, *optional*): + Pass a callback function to view the file transmission progress. + The function must take *(current, total)* as positional arguments (look at Other Parameters below for a + detailed description) and will be called back each time a new file chunk has been successfully + transmitted. + + progress_args (``tuple``, *optional*): + Extra custom arguments for the progress callback function. + You can pass anything you need to be available in the progress callback scope; for example, a Message + object or a Client instance in order to edit the message with the updated progress status. + + Other Parameters: + current (``int``): + The amount of bytes transmitted so far. + + total (``int``): + The total size of the file. + + *args (``tuple``, *optional*): + Extra custom arguments as defined in the ``progress_args`` parameter. + You can either keep ``*args`` or add every single extra argument in your function signature. + + Returns: + :obj:`~pyrogram.types.Message` | ``None``: On success, the sent document message is returned, otherwise, in + case the upload is deliberately stopped with :meth:`~pyrogram.Client.stop_transmission`, None is returned. + + Example: + .. code-block:: python + + # Send document by uploading from local file + await app.send_document("me", "document.zip") + + # Add caption to the document file + await app.send_document("me", "document.zip", caption="document caption") + + # Keep track of the progress while uploading + async def progress(current, total): + print(f"{current * 100 / total:.1f}%") + + await app.send_document("me", "document.zip", progress=progress) + """ + file = None + + try: + if isinstance(document, str): + if os.path.isfile(document): + thumb = await self.save_file(thumb) + file = await self.save_file(document, progress=progress, progress_args=progress_args) + media = raw.types.InputMediaUploadedDocument( + mime_type=self.guess_mime_type(document) or "application/zip", + file=file, + force_file=force_document or None, + thumb=thumb, + attributes=[ + raw.types.DocumentAttributeFilename(file_name=file_name or os.path.basename(document)) + ] + ) + elif re.match("^https?://", document): + media = raw.types.InputMediaDocumentExternal( + url=document + ) + else: + media = utils.get_input_media_from_file_id(document, FileType.DOCUMENT) + else: + thumb = await self.save_file(thumb) + file = await self.save_file(document, progress=progress, progress_args=progress_args) + media = raw.types.InputMediaUploadedDocument( + mime_type=self.guess_mime_type(file_name or document.name) or "application/zip", + file=file, + thumb=thumb, + attributes=[ + raw.types.DocumentAttributeFilename(file_name=file_name or document.name) + ] + ) + + while True: + try: + r = await self.invoke( + raw.functions.messages.SendMedia( + peer=await self.resolve_peer(chat_id), + media=media, + silent=disable_notification or None, + reply_to_msg_id=reply_to_message_id, + random_id=self.rnd_id(), + schedule_date=utils.datetime_to_timestamp(schedule_date), + noforwards=protect_content, + reply_markup=await reply_markup.write(self) if reply_markup else None, + **await utils.parse_text_entities(self, caption, parse_mode, caption_entities) + ) + ) + except FilePartMissing as e: + await self.save_file(document, file_id=file.id, file_part=e.value) + else: + for i in r.updates: + if isinstance(i, (raw.types.UpdateNewMessage, + raw.types.UpdateNewChannelMessage, + raw.types.UpdateNewScheduledMessage)): + return await types.Message._parse( + self, i.message, + {i.id: i for i in r.users}, + {i.id: i for i in r.chats}, + is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage) + ) + except StopTransmission: + return None diff --git a/pyrogram/methods/messages/send_location.py b/pyrogram/methods/messages/send_location.py new file mode 100644 index 00000000000..2bd17681b01 --- /dev/null +++ b/pyrogram/methods/messages/send_location.py @@ -0,0 +1,113 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 datetime import datetime +from typing import Union + +import pyrogram +from pyrogram import raw, utils +from pyrogram import types + + +class SendLocation: + async def send_location( + self: "pyrogram.Client", + chat_id: Union[int, str], + latitude: float, + longitude: float, + disable_notification: bool = None, + reply_to_message_id: int = None, + schedule_date: datetime = None, + protect_content: bool = None, + reply_markup: Union[ + "types.InlineKeyboardMarkup", + "types.ReplyKeyboardMarkup", + "types.ReplyKeyboardRemove", + "types.ForceReply" + ] = None + ) -> "types.Message": + """Send points on the map. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + latitude (``float``): + Latitude of the location. + + longitude (``float``): + Longitude of the location. + + disable_notification (``bool``, *optional*): + Sends the message silently. + Users will receive a notification with no sound. + + reply_to_message_id (``int``, *optional*): + If the message is a reply, ID of the original message + + schedule_date (:py:obj:`~datetime.datetime`, *optional*): + Date when the message will be automatically sent. + + protect_content (``bool``, *optional*): + Protects the contents of the sent message from forwarding and saving. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*): + Additional interface options. An object for an inline keyboard, custom reply keyboard, + instructions to remove reply keyboard or to force a reply from the user. + + Returns: + :obj:`~pyrogram.types.Message`: On success, the sent location message is returned. + + Example: + .. code-block:: python + + app.send_location("me", latitude, longitude) + """ + r = await self.invoke( + raw.functions.messages.SendMedia( + peer=await self.resolve_peer(chat_id), + media=raw.types.InputMediaGeoPoint( + geo_point=raw.types.InputGeoPoint( + lat=latitude, + long=longitude + ) + ), + message="", + silent=disable_notification or None, + reply_to_msg_id=reply_to_message_id, + random_id=self.rnd_id(), + schedule_date=utils.datetime_to_timestamp(schedule_date), + noforwards=protect_content, + reply_markup=await reply_markup.write(self) if reply_markup else None + ) + ) + + for i in r.updates: + if isinstance(i, (raw.types.UpdateNewMessage, + raw.types.UpdateNewChannelMessage, + raw.types.UpdateNewScheduledMessage)): + return await types.Message._parse( + self, i.message, + {i.id: i for i in r.users}, + {i.id: i for i in r.chats}, + is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage) + ) diff --git a/pyrogram/methods/messages/send_media_group.py b/pyrogram/methods/messages/send_media_group.py new file mode 100644 index 00000000000..a8b905de23f --- /dev/null +++ b/pyrogram/methods/messages/send_media_group.py @@ -0,0 +1,417 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import logging +import os +import re +from datetime import datetime +from typing import Union, List + +import pyrogram +from pyrogram import raw +from pyrogram import types +from pyrogram import utils +from pyrogram.file_id import FileType + +log = logging.getLogger(__name__) + + +class SendMediaGroup: + # TODO: Add progress parameter + async def send_media_group( + self: "pyrogram.Client", + chat_id: Union[int, str], + media: List[Union[ + "types.InputMediaPhoto", + "types.InputMediaVideo", + "types.InputMediaAudio", + "types.InputMediaDocument" + ]], + disable_notification: bool = None, + reply_to_message_id: int = None, + schedule_date: datetime = None, + protect_content: bool = None, + ) -> List["types.Message"]: + """Send a group of photos or videos as an album. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + media (List of :obj:`~pyrogram.types.InputMediaPhoto`, :obj:`~pyrogram.types.InputMediaVideo`, :obj:`~pyrogram.types.InputMediaAudio` and :obj:`~pyrogram.types.InputMediaDocument`): + A list describing photos and videos to be sent, must include 2–10 items. + + disable_notification (``bool``, *optional*): + Sends the message silently. + Users will receive a notification with no sound. + + reply_to_message_id (``int``, *optional*): + If the message is a reply, ID of the original message. + + schedule_date (:py:obj:`~datetime.datetime`, *optional*): + Date when the message will be automatically sent. + + protect_content (``bool``, *optional*): + Protects the contents of the sent message from forwarding and saving. + + Returns: + List of :obj:`~pyrogram.types.Message`: On success, a list of the sent messages is returned. + + Example: + .. code-block:: python + + from pyrogram.types import InputMediaPhoto, InputMediaVideo + + await app.send_media_group( + "me", + [ + InputMediaPhoto("photo1.jpg"), + InputMediaPhoto("photo2.jpg", caption="photo caption"), + InputMediaVideo("video.mp4", caption="video caption") + ] + ) + """ + multi_media = [] + + for i in media: + if isinstance(i, types.InputMediaPhoto): + if isinstance(i.media, str): + if os.path.isfile(i.media): + media = await self.invoke( + raw.functions.messages.UploadMedia( + peer=await self.resolve_peer(chat_id), + media=raw.types.InputMediaUploadedPhoto( + file=await self.save_file(i.media), + spoiler=i.has_spoiler + ) + ) + ) + + media = raw.types.InputMediaPhoto( + id=raw.types.InputPhoto( + id=media.photo.id, + access_hash=media.photo.access_hash, + file_reference=media.photo.file_reference + ), + spoiler=i.has_spoiler + ) + elif re.match("^https?://", i.media): + media = await self.invoke( + raw.functions.messages.UploadMedia( + peer=await self.resolve_peer(chat_id), + media=raw.types.InputMediaPhotoExternal( + url=i.media, + spoiler=i.has_spoiler + ) + ) + ) + + media = raw.types.InputMediaPhoto( + id=raw.types.InputPhoto( + id=media.photo.id, + access_hash=media.photo.access_hash, + file_reference=media.photo.file_reference + ), + spoiler=i.has_spoiler + ) + else: + media = utils.get_input_media_from_file_id(i.media, FileType.PHOTO) + else: + media = await self.invoke( + raw.functions.messages.UploadMedia( + peer=await self.resolve_peer(chat_id), + media=raw.types.InputMediaUploadedPhoto( + file=await self.save_file(i.media), + spoiler=i.has_spoiler + ) + ) + ) + + media = raw.types.InputMediaPhoto( + id=raw.types.InputPhoto( + id=media.photo.id, + access_hash=media.photo.access_hash, + file_reference=media.photo.file_reference + ), + spoiler=i.has_spoiler + ) + elif isinstance(i, types.InputMediaVideo): + if isinstance(i.media, str): + if os.path.isfile(i.media): + media = await self.invoke( + raw.functions.messages.UploadMedia( + peer=await self.resolve_peer(chat_id), + media=raw.types.InputMediaUploadedDocument( + file=await self.save_file(i.media), + thumb=await self.save_file(i.thumb), + spoiler=i.has_spoiler, + mime_type=self.guess_mime_type(i.media) or "video/mp4", + attributes=[ + raw.types.DocumentAttributeVideo( + supports_streaming=i.supports_streaming or None, + duration=i.duration, + w=i.width, + h=i.height + ), + raw.types.DocumentAttributeFilename(file_name=os.path.basename(i.media)) + ] + ) + ) + ) + + media = raw.types.InputMediaDocument( + id=raw.types.InputDocument( + id=media.document.id, + access_hash=media.document.access_hash, + file_reference=media.document.file_reference + ), + spoiler=i.has_spoiler + ) + elif re.match("^https?://", i.media): + media = await self.invoke( + raw.functions.messages.UploadMedia( + peer=await self.resolve_peer(chat_id), + media=raw.types.InputMediaDocumentExternal( + url=i.media, + spoiler=i.has_spoiler + ) + ) + ) + + media = raw.types.InputMediaDocument( + id=raw.types.InputDocument( + id=media.document.id, + access_hash=media.document.access_hash, + file_reference=media.document.file_reference + ), + spoiler=i.has_spoiler + ) + else: + media = utils.get_input_media_from_file_id(i.media, FileType.VIDEO) + else: + media = await self.invoke( + raw.functions.messages.UploadMedia( + peer=await self.resolve_peer(chat_id), + media=raw.types.InputMediaUploadedDocument( + file=await self.save_file(i.media), + thumb=await self.save_file(i.thumb), + spoiler=i.has_spoiler, + mime_type=self.guess_mime_type(getattr(i.media, "name", "video.mp4")) or "video/mp4", + attributes=[ + raw.types.DocumentAttributeVideo( + supports_streaming=i.supports_streaming or None, + duration=i.duration, + w=i.width, + h=i.height + ), + raw.types.DocumentAttributeFilename(file_name=getattr(i.media, "name", "video.mp4")) + ] + ) + ) + ) + + media = raw.types.InputMediaDocument( + id=raw.types.InputDocument( + id=media.document.id, + access_hash=media.document.access_hash, + file_reference=media.document.file_reference + ), + spoiler=i.has_spoiler + ) + elif isinstance(i, types.InputMediaAudio): + if isinstance(i.media, str): + if os.path.isfile(i.media): + media = await self.invoke( + raw.functions.messages.UploadMedia( + peer=await self.resolve_peer(chat_id), + media=raw.types.InputMediaUploadedDocument( + mime_type=self.guess_mime_type(i.media) or "audio/mpeg", + file=await self.save_file(i.media), + thumb=await self.save_file(i.thumb), + attributes=[ + raw.types.DocumentAttributeAudio( + duration=i.duration, + performer=i.performer, + title=i.title + ), + raw.types.DocumentAttributeFilename(file_name=os.path.basename(i.media)) + ] + ) + ) + ) + + media = raw.types.InputMediaDocument( + id=raw.types.InputDocument( + id=media.document.id, + access_hash=media.document.access_hash, + file_reference=media.document.file_reference + ) + ) + elif re.match("^https?://", i.media): + media = await self.invoke( + raw.functions.messages.UploadMedia( + peer=await self.resolve_peer(chat_id), + media=raw.types.InputMediaDocumentExternal( + url=i.media + ) + ) + ) + + media = raw.types.InputMediaDocument( + id=raw.types.InputDocument( + id=media.document.id, + access_hash=media.document.access_hash, + file_reference=media.document.file_reference + ) + ) + else: + media = utils.get_input_media_from_file_id(i.media, FileType.AUDIO) + else: + media = await self.invoke( + raw.functions.messages.UploadMedia( + peer=await self.resolve_peer(chat_id), + media=raw.types.InputMediaUploadedDocument( + mime_type=self.guess_mime_type(getattr(i.media, "name", "audio.mp3")) or "audio/mpeg", + file=await self.save_file(i.media), + thumb=await self.save_file(i.thumb), + attributes=[ + raw.types.DocumentAttributeAudio( + duration=i.duration, + performer=i.performer, + title=i.title + ), + raw.types.DocumentAttributeFilename(file_name=getattr(i.media, "name", "audio.mp3")) + ] + ) + ) + ) + + media = raw.types.InputMediaDocument( + id=raw.types.InputDocument( + id=media.document.id, + access_hash=media.document.access_hash, + file_reference=media.document.file_reference + ) + ) + elif isinstance(i, types.InputMediaDocument): + if isinstance(i.media, str): + if os.path.isfile(i.media): + media = await self.invoke( + raw.functions.messages.UploadMedia( + peer=await self.resolve_peer(chat_id), + media=raw.types.InputMediaUploadedDocument( + mime_type=self.guess_mime_type(i.media) or "application/zip", + file=await self.save_file(i.media), + thumb=await self.save_file(i.thumb), + attributes=[ + raw.types.DocumentAttributeFilename(file_name=os.path.basename(i.media)) + ] + ) + ) + ) + + media = raw.types.InputMediaDocument( + id=raw.types.InputDocument( + id=media.document.id, + access_hash=media.document.access_hash, + file_reference=media.document.file_reference + ) + ) + elif re.match("^https?://", i.media): + media = await self.invoke( + raw.functions.messages.UploadMedia( + peer=await self.resolve_peer(chat_id), + media=raw.types.InputMediaDocumentExternal( + url=i.media + ) + ) + ) + + media = raw.types.InputMediaDocument( + id=raw.types.InputDocument( + id=media.document.id, + access_hash=media.document.access_hash, + file_reference=media.document.file_reference + ) + ) + else: + media = utils.get_input_media_from_file_id(i.media, FileType.DOCUMENT) + else: + media = await self.invoke( + raw.functions.messages.UploadMedia( + peer=await self.resolve_peer(chat_id), + media=raw.types.InputMediaUploadedDocument( + mime_type=self.guess_mime_type( + getattr(i.media, "name", "file.zip") + ) or "application/zip", + file=await self.save_file(i.media), + thumb=await self.save_file(i.thumb), + attributes=[ + raw.types.DocumentAttributeFilename(file_name=getattr(i.media, "name", "file.zip")) + ] + ) + ) + ) + + media = raw.types.InputMediaDocument( + id=raw.types.InputDocument( + id=media.document.id, + access_hash=media.document.access_hash, + file_reference=media.document.file_reference + ) + ) + else: + raise ValueError(f"{i.__class__.__name__} is not a supported type for send_media_group") + + multi_media.append( + raw.types.InputSingleMedia( + media=media, + random_id=self.rnd_id(), + **await self.parser.parse(i.caption, i.parse_mode) + ) + ) + + r = await self.invoke( + raw.functions.messages.SendMultiMedia( + peer=await self.resolve_peer(chat_id), + multi_media=multi_media, + silent=disable_notification or None, + reply_to_msg_id=reply_to_message_id, + schedule_date=utils.datetime_to_timestamp(schedule_date), + noforwards=protect_content + ), + sleep_threshold=60 + ) + + return await utils.parse_messages( + self, + raw.types.messages.Messages( + messages=[m.message for m in filter( + lambda u: isinstance(u, (raw.types.UpdateNewMessage, + raw.types.UpdateNewChannelMessage, + raw.types.UpdateNewScheduledMessage)), + r.updates + )], + users=r.users, + chats=r.chats + ) + ) diff --git a/pyrogram/methods/messages/send_message.py b/pyrogram/methods/messages/send_message.py new file mode 100644 index 00000000000..ad73f9a182c --- /dev/null +++ b/pyrogram/methods/messages/send_message.py @@ -0,0 +1,177 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 datetime import datetime +from typing import Union, List, Optional + +import pyrogram +from pyrogram import raw, utils, enums +from pyrogram import types + + +class SendMessage: + async def send_message( + self: "pyrogram.Client", + chat_id: Union[int, str], + text: str, + parse_mode: Optional["enums.ParseMode"] = None, + entities: List["types.MessageEntity"] = None, + disable_web_page_preview: bool = None, + disable_notification: bool = None, + reply_to_message_id: int = None, + schedule_date: datetime = None, + protect_content: bool = None, + reply_markup: Union[ + "types.InlineKeyboardMarkup", + "types.ReplyKeyboardMarkup", + "types.ReplyKeyboardRemove", + "types.ForceReply" + ] = None + ) -> "types.Message": + """Send text messages. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + text (``str``): + Text of the message to be sent. + + parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*): + By default, texts are parsed using both Markdown and HTML styles. + You can combine both syntaxes together. + + entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in message text, which can be specified instead of *parse_mode*. + + disable_web_page_preview (``bool``, *optional*): + Disables link previews for links in this message. + + disable_notification (``bool``, *optional*): + Sends the message silently. + Users will receive a notification with no sound. + + reply_to_message_id (``int``, *optional*): + If the message is a reply, ID of the original message. + + schedule_date (:py:obj:`~datetime.datetime`, *optional*): + Date when the message will be automatically sent. + + protect_content (``bool``, *optional*): + Protects the contents of the sent message from forwarding and saving. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*): + Additional interface options. An object for an inline keyboard, custom reply keyboard, + instructions to remove reply keyboard or to force a reply from the user. + + Returns: + :obj:`~pyrogram.types.Message`: On success, the sent text message is returned. + + Example: + .. code-block:: python + + # Simple example + await app.send_message("me", "Message sent with **Pyrogram**!") + + # Disable web page previews + await app.send_message("me", "https://docs.pyrogram.org", + disable_web_page_preview=True) + + # Reply to a message using its id + await app.send_message("me", "this is a reply", reply_to_message_id=123) + + .. code-block:: python + + # For bots only, send messages with keyboards attached + + from pyrogram.types import ( + ReplyKeyboardMarkup, InlineKeyboardMarkup, InlineKeyboardButton) + + # Send a normal keyboard + await app.send_message( + chat_id, "Look at that button!", + reply_markup=ReplyKeyboardMarkup([["Nice!"]])) + + # Send an inline keyboard + await app.send_message( + chat_id, "These are inline buttons", + reply_markup=InlineKeyboardMarkup( + [ + [InlineKeyboardButton("Data", callback_data="callback_data")], + [InlineKeyboardButton("Docs", url="https://docs.pyrogram.org")] + ])) + """ + + message, entities = (await utils.parse_text_entities(self, text, parse_mode, entities)).values() + + r = await self.invoke( + raw.functions.messages.SendMessage( + peer=await self.resolve_peer(chat_id), + no_webpage=disable_web_page_preview or None, + silent=disable_notification or None, + reply_to_msg_id=reply_to_message_id, + random_id=self.rnd_id(), + schedule_date=utils.datetime_to_timestamp(schedule_date), + reply_markup=await reply_markup.write(self) if reply_markup else None, + message=message, + entities=entities, + noforwards=protect_content + ) + ) + + if isinstance(r, raw.types.UpdateShortSentMessage): + peer = await self.resolve_peer(chat_id) + + peer_id = ( + peer.user_id + if isinstance(peer, raw.types.InputPeerUser) + else -peer.chat_id + ) + + return types.Message( + id=r.id, + chat=types.Chat( + id=peer_id, + type=enums.ChatType.PRIVATE, + client=self + ), + text=message, + date=utils.timestamp_to_datetime(r.date), + outgoing=r.out, + reply_markup=reply_markup, + entities=[ + types.MessageEntity._parse(None, entity, {}) + for entity in entities + ] if entities else None, + client=self + ) + + for i in r.updates: + if isinstance(i, (raw.types.UpdateNewMessage, + raw.types.UpdateNewChannelMessage, + raw.types.UpdateNewScheduledMessage)): + return await types.Message._parse( + self, i.message, + {i.id: i for i in r.users}, + {i.id: i for i in r.chats}, + is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage) + ) diff --git a/pyrogram/methods/messages/send_photo.py b/pyrogram/methods/messages/send_photo.py new file mode 100644 index 00000000000..61298a5c68a --- /dev/null +++ b/pyrogram/methods/messages/send_photo.py @@ -0,0 +1,204 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import os +import re +from datetime import datetime +from typing import Union, BinaryIO, List, Optional, Callable + +import pyrogram +from pyrogram import raw, enums +from pyrogram import types +from pyrogram import utils +from pyrogram.errors import FilePartMissing +from pyrogram.file_id import FileType + + +class SendPhoto: + async def send_photo( + self: "pyrogram.Client", + chat_id: Union[int, str], + photo: Union[str, BinaryIO], + caption: str = "", + parse_mode: Optional["enums.ParseMode"] = None, + caption_entities: List["types.MessageEntity"] = None, + has_spoiler: bool = None, + ttl_seconds: int = None, + disable_notification: bool = None, + reply_to_message_id: int = None, + schedule_date: datetime = None, + protect_content: bool = None, + reply_markup: Union[ + "types.InlineKeyboardMarkup", + "types.ReplyKeyboardMarkup", + "types.ReplyKeyboardRemove", + "types.ForceReply" + ] = None, + progress: Callable = None, + progress_args: tuple = () + ) -> Optional["types.Message"]: + """Send photos. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + photo (``str`` | ``BinaryIO``): + Photo to send. + Pass a file_id as string to send a photo that exists on the Telegram servers, + pass an HTTP URL as a string for Telegram to get a photo from the Internet, + pass a file path as string to upload a new photo that exists on your local machine, or + pass a binary file-like object with its attribute ".name" set for in-memory uploads. + + caption (``str``, *optional*): + Photo caption, 0-1024 characters. + + parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*): + By default, texts are parsed using both Markdown and HTML styles. + You can combine both syntaxes together. + + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + + has_spoiler (``bool``, *optional*): + Pass True if the photo needs to be covered with a spoiler animation. + + ttl_seconds (``int``, *optional*): + Self-Destruct Timer. + If you set a timer, the photo will self-destruct in *ttl_seconds* + seconds after it was viewed. + + disable_notification (``bool``, *optional*): + Sends the message silently. + Users will receive a notification with no sound. + + reply_to_message_id (``int``, *optional*): + If the message is a reply, ID of the original message. + + schedule_date (:py:obj:`~datetime.datetime`, *optional*): + Date when the message will be automatically sent. + + protect_content (``bool``, *optional*): + Protects the contents of the sent message from forwarding and saving. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*): + Additional interface options. An object for an inline keyboard, custom reply keyboard, + instructions to remove reply keyboard or to force a reply from the user. + + progress (``Callable``, *optional*): + Pass a callback function to view the file transmission progress. + The function must take *(current, total)* as positional arguments (look at Other Parameters below for a + detailed description) and will be called back each time a new file chunk has been successfully + transmitted. + + progress_args (``tuple``, *optional*): + Extra custom arguments for the progress callback function. + You can pass anything you need to be available in the progress callback scope; for example, a Message + object or a Client instance in order to edit the message with the updated progress status. + + Other Parameters: + current (``int``): + The amount of bytes transmitted so far. + + total (``int``): + The total size of the file. + + *args (``tuple``, *optional*): + Extra custom arguments as defined in the ``progress_args`` parameter. + You can either keep ``*args`` or add every single extra argument in your function signature. + + Returns: + :obj:`~pyrogram.types.Message` | ``None``: On success, the sent photo message is returned, otherwise, in + case the upload is deliberately stopped with :meth:`~pyrogram.Client.stop_transmission`, None is returned. + + Example: + .. code-block:: python + + # Send photo by uploading from local file + await app.send_photo("me", "photo.jpg") + + # Send photo by uploading from URL + await app.send_photo("me", "https://example.com/example.jpg") + + # Add caption to a photo + await app.send_photo("me", "photo.jpg", caption="Caption") + + # Send self-destructing photo + await app.send_photo("me", "photo.jpg", ttl_seconds=10) + """ + file = None + + try: + if isinstance(photo, str): + if os.path.isfile(photo): + file = await self.save_file(photo, progress=progress, progress_args=progress_args) + media = raw.types.InputMediaUploadedPhoto( + file=file, + ttl_seconds=ttl_seconds, + spoiler=has_spoiler, + ) + elif re.match("^https?://", photo): + media = raw.types.InputMediaPhotoExternal( + url=photo, + ttl_seconds=ttl_seconds, + spoiler=has_spoiler + ) + else: + media = utils.get_input_media_from_file_id(photo, FileType.PHOTO, ttl_seconds=ttl_seconds) + else: + file = await self.save_file(photo, progress=progress, progress_args=progress_args) + media = raw.types.InputMediaUploadedPhoto( + file=file, + ttl_seconds=ttl_seconds, + spoiler=has_spoiler + ) + + while True: + try: + r = await self.invoke( + raw.functions.messages.SendMedia( + peer=await self.resolve_peer(chat_id), + media=media, + silent=disable_notification or None, + reply_to_msg_id=reply_to_message_id, + random_id=self.rnd_id(), + schedule_date=utils.datetime_to_timestamp(schedule_date), + noforwards=protect_content, + reply_markup=await reply_markup.write(self) if reply_markup else None, + **await utils.parse_text_entities(self, caption, parse_mode, caption_entities) + ) + ) + except FilePartMissing as e: + await self.save_file(photo, file_id=file.id, file_part=e.value) + else: + for i in r.updates: + if isinstance(i, (raw.types.UpdateNewMessage, + raw.types.UpdateNewChannelMessage, + raw.types.UpdateNewScheduledMessage)): + return await types.Message._parse( + self, i.message, + {i.id: i for i in r.users}, + {i.id: i for i in r.chats}, + is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage) + ) + except pyrogram.StopTransmission: + return None diff --git a/pyrogram/methods/messages/send_poll.py b/pyrogram/methods/messages/send_poll.py new file mode 100644 index 00000000000..267dcb6dad7 --- /dev/null +++ b/pyrogram/methods/messages/send_poll.py @@ -0,0 +1,181 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 datetime import datetime +from typing import Union, List + +import pyrogram +from pyrogram import raw, utils +from pyrogram import types, enums + + +class SendPoll: + async def send_poll( + self: "pyrogram.Client", + chat_id: Union[int, str], + question: str, + options: List[str], + is_anonymous: bool = True, + type: "enums.PollType" = enums.PollType.REGULAR, + allows_multiple_answers: bool = None, + correct_option_id: int = None, + explanation: str = None, + explanation_parse_mode: "enums.ParseMode" = None, + explanation_entities: List["types.MessageEntity"] = None, + open_period: int = None, + close_date: datetime = None, + is_closed: bool = None, + disable_notification: bool = None, + protect_content: bool = None, + reply_to_message_id: int = None, + schedule_date: datetime = None, + reply_markup: Union[ + "types.InlineKeyboardMarkup", + "types.ReplyKeyboardMarkup", + "types.ReplyKeyboardRemove", + "types.ForceReply" + ] = None + ) -> "types.Message": + """Send a new poll. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + question (``str``): + Poll question, 1-255 characters. + + options (List of ``str``): + List of answer options, 2-10 strings 1-100 characters each. + + is_anonymous (``bool``, *optional*): + True, if the poll needs to be anonymous. + Defaults to True. + + type (:obj`~pyrogram.enums.PollType`, *optional*): + Poll type, :obj:`~pyrogram.enums.PollType.QUIZ` or :obj:`~pyrogram.enums.PollType.REGULAR`. + Defaults to :obj:`~pyrogram.enums.PollType.REGULAR`. + + allows_multiple_answers (``bool``, *optional*): + True, if the poll allows multiple answers, ignored for polls in quiz mode. + Defaults to False. + + correct_option_id (``int``, *optional*): + 0-based identifier of the correct answer option, required for polls in quiz mode. + + explanation (``str``, *optional*): + Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style + poll, 0-200 characters with at most 2 line feeds after entities parsing. + + explanation_parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*): + By default, texts are parsed using both Markdown and HTML styles. + You can combine both syntaxes together. + + explanation_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the poll explanation, which can be specified instead of + *parse_mode*. + + open_period (``int``, *optional*): + Amount of time in seconds the poll will be active after creation, 5-600. + Can't be used together with *close_date*. + + close_date (:py:obj:`~datetime.datetime`, *optional*): + Point in time when the poll will be automatically closed. + Must be at least 5 and no more than 600 seconds in the future. + Can't be used together with *open_period*. + + is_closed (``bool``, *optional*): + Pass True, if the poll needs to be immediately closed. + This can be useful for poll preview. + + disable_notification (``bool``, *optional*): + Sends the message silently. + Users will receive a notification with no sound. + + protect_content (``bool``, *optional*): + Protects the contents of the sent message from forwarding and saving. + + reply_to_message_id (``int``, *optional*): + If the message is a reply, ID of the original message. + + schedule_date (:py:obj:`~datetime.datetime`, *optional*): + Date when the message will be automatically sent. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*): + Additional interface options. An object for an inline keyboard, custom reply keyboard, + instructions to remove reply keyboard or to force a reply from the user. + + Returns: + :obj:`~pyrogram.types.Message`: On success, the sent poll message is returned. + + Example: + .. code-block:: python + + await app.send_poll(chat_id, "Is this a poll question?", ["Yes", "No", "Maybe"]) + """ + + solution, solution_entities = (await utils.parse_text_entities( + self, explanation, explanation_parse_mode, explanation_entities + )).values() + + r = await self.invoke( + raw.functions.messages.SendMedia( + peer=await self.resolve_peer(chat_id), + media=raw.types.InputMediaPoll( + poll=raw.types.Poll( + id=self.rnd_id(), + question=question, + answers=[ + raw.types.PollAnswer(text=text, option=bytes([i])) + for i, text in enumerate(options) + ], + closed=is_closed, + public_voters=not is_anonymous, + multiple_choice=allows_multiple_answers, + quiz=type == enums.PollType.QUIZ or False, + close_period=open_period, + close_date=utils.datetime_to_timestamp(close_date) + ), + correct_answers=[bytes([correct_option_id])] if correct_option_id is not None else None, + solution=solution, + solution_entities=solution_entities or [] + ), + message="", + silent=disable_notification, + reply_to_msg_id=reply_to_message_id, + random_id=self.rnd_id(), + schedule_date=utils.datetime_to_timestamp(schedule_date), + noforwards=protect_content, + reply_markup=await reply_markup.write(self) if reply_markup else None + ) + ) + + for i in r.updates: + if isinstance(i, (raw.types.UpdateNewMessage, + raw.types.UpdateNewChannelMessage, + raw.types.UpdateNewScheduledMessage)): + return await types.Message._parse( + self, i.message, + {i.id: i for i in r.users}, + {i.id: i for i in r.chats}, + is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage) + ) diff --git a/pyrogram/methods/messages/send_reaction.py b/pyrogram/methods/messages/send_reaction.py new file mode 100644 index 00000000000..f4d1d34340f --- /dev/null +++ b/pyrogram/methods/messages/send_reaction.py @@ -0,0 +1,73 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw + + +class SendReaction: + async def send_reaction( + self: "pyrogram.Client", + chat_id: Union[int, str], + message_id: int, + emoji: str = "", + big: bool = False + ) -> bool: + """Send a reaction to a message. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + message_id (``int``): + Identifier of the message. + + emoji (``str``, *optional*): + Reaction emoji. + Pass "" as emoji (default) to retract the reaction. + + big (``bool``, *optional*): + Pass True to show a bigger and longer reaction. + Defaults to False. + + Returns: + ``bool``: On success, True is returned. + + Example: + .. code-block:: python + + # Send a reaction + await app.send_reaction(chat_id, message_id, "🔥") + + # Retract a reaction + await app.send_reaction(chat_id, message_id) + """ + await self.invoke( + raw.functions.messages.SendReaction( + peer=await self.resolve_peer(chat_id), + msg_id=message_id, + reaction=[raw.types.ReactionEmoji(emoticon=emoji)] if emoji else None, + big=big + ) + ) + + return True diff --git a/pyrogram/methods/messages/send_sticker.py b/pyrogram/methods/messages/send_sticker.py new file mode 100644 index 00000000000..ccfaada51ef --- /dev/null +++ b/pyrogram/methods/messages/send_sticker.py @@ -0,0 +1,179 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import os +import re +from datetime import datetime +from typing import Union, BinaryIO, Optional, Callable + +import pyrogram +from pyrogram import StopTransmission +from pyrogram import raw +from pyrogram import types +from pyrogram import utils +from pyrogram.errors import FilePartMissing +from pyrogram.file_id import FileType + + +class SendSticker: + async def send_sticker( + self: "pyrogram.Client", + chat_id: Union[int, str], + sticker: Union[str, BinaryIO], + disable_notification: bool = None, + reply_to_message_id: int = None, + schedule_date: datetime = None, + protect_content: bool = None, + reply_markup: Union[ + "types.InlineKeyboardMarkup", + "types.ReplyKeyboardMarkup", + "types.ReplyKeyboardRemove", + "types.ForceReply" + ] = None, + progress: Callable = None, + progress_args: tuple = () + ) -> Optional["types.Message"]: + """Send static .webp or animated .tgs stickers. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + sticker (``str`` | ``BinaryIO``): + Sticker to send. + Pass a file_id as string to send a sticker that exists on the Telegram servers, + pass an HTTP URL as a string for Telegram to get a .webp sticker file from the Internet, + pass a file path as string to upload a new sticker that exists on your local machine, or + pass a binary file-like object with its attribute ".name" set for in-memory uploads. + + disable_notification (``bool``, *optional*): + Sends the message silently. + Users will receive a notification with no sound. + + reply_to_message_id (``int``, *optional*): + If the message is a reply, ID of the original message. + + schedule_date (:py:obj:`~datetime.datetime`, *optional*): + Date when the message will be automatically sent. + + protect_content (``bool``, *optional*): + Protects the contents of the sent message from forwarding and saving. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*): + Additional interface options. An object for an inline keyboard, custom reply keyboard, + instructions to remove reply keyboard or to force a reply from the user. + + progress (``Callable``, *optional*): + Pass a callback function to view the file transmission progress. + The function must take *(current, total)* as positional arguments (look at Other Parameters below for a + detailed description) and will be called back each time a new file chunk has been successfully + transmitted. + + progress_args (``tuple``, *optional*): + Extra custom arguments for the progress callback function. + You can pass anything you need to be available in the progress callback scope; for example, a Message + object or a Client instance in order to edit the message with the updated progress status. + + Other Parameters: + current (``int``): + The amount of bytes transmitted so far. + + total (``int``): + The total size of the file. + + *args (``tuple``, *optional*): + Extra custom arguments as defined in the ``progress_args`` parameter. + You can either keep ``*args`` or add every single extra argument in your function signature. + + Returns: + :obj:`~pyrogram.types.Message` | ``None``: On success, the sent sticker message is returned, otherwise, + in case the upload is deliberately stopped with :meth:`~pyrogram.Client.stop_transmission`, None is + returned. + + Example: + .. code-block:: python + + # Send sticker by uploading from local file + await app.send_sticker("me", "sticker.webp") + + # Send sticker using file_id + await app.send_sticker("me", file_id) + """ + file = None + + try: + if isinstance(sticker, str): + if os.path.isfile(sticker): + file = await self.save_file(sticker, progress=progress, progress_args=progress_args) + media = raw.types.InputMediaUploadedDocument( + mime_type=self.guess_mime_type(sticker) or "image/webp", + file=file, + attributes=[ + raw.types.DocumentAttributeFilename(file_name=os.path.basename(sticker)) + ] + ) + elif re.match("^https?://", sticker): + media = raw.types.InputMediaDocumentExternal( + url=sticker + ) + else: + media = utils.get_input_media_from_file_id(sticker, FileType.STICKER) + else: + file = await self.save_file(sticker, progress=progress, progress_args=progress_args) + media = raw.types.InputMediaUploadedDocument( + mime_type=self.guess_mime_type(sticker.name) or "image/webp", + file=file, + attributes=[ + raw.types.DocumentAttributeFilename(file_name=sticker.name) + ] + ) + + while True: + try: + r = await self.invoke( + raw.functions.messages.SendMedia( + peer=await self.resolve_peer(chat_id), + media=media, + silent=disable_notification or None, + reply_to_msg_id=reply_to_message_id, + random_id=self.rnd_id(), + schedule_date=utils.datetime_to_timestamp(schedule_date), + noforwards=protect_content, + reply_markup=await reply_markup.write(self) if reply_markup else None, + message="" + ) + ) + except FilePartMissing as e: + await self.save_file(sticker, file_id=file.id, file_part=e.value) + else: + for i in r.updates: + if isinstance(i, (raw.types.UpdateNewMessage, + raw.types.UpdateNewChannelMessage, + raw.types.UpdateNewScheduledMessage)): + return await types.Message._parse( + self, i.message, + {i.id: i for i in r.users}, + {i.id: i for i in r.chats}, + is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage) + ) + except StopTransmission: + return None diff --git a/pyrogram/methods/messages/send_venue.py b/pyrogram/methods/messages/send_venue.py new file mode 100644 index 00000000000..4d3167bf001 --- /dev/null +++ b/pyrogram/methods/messages/send_venue.py @@ -0,0 +1,137 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 datetime import datetime +from typing import Union + +import pyrogram +from pyrogram import raw, utils +from pyrogram import types + + +class SendVenue: + async def send_venue( + self: "pyrogram.Client", + chat_id: Union[int, str], + latitude: float, + longitude: float, + title: str, + address: str, + foursquare_id: str = "", + foursquare_type: str = "", + disable_notification: bool = None, + reply_to_message_id: int = None, + schedule_date: datetime = None, + protect_content: bool = None, + reply_markup: Union[ + "types.InlineKeyboardMarkup", + "types.ReplyKeyboardMarkup", + "types.ReplyKeyboardRemove", + "types.ForceReply" + ] = None + ) -> "types.Message": + """Send information about a venue. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + latitude (``float``): + Latitude of the venue. + + longitude (``float``): + Longitude of the venue. + + title (``str``): + Name of the venue. + + address (``str``): + Address of the venue. + + foursquare_id (``str``, *optional*): + Foursquare identifier of the venue. + + foursquare_type (``str``, *optional*): + Foursquare type of the venue, if known. + (For example, "arts_entertainment/default", "arts_entertainment/aquarium" or "food/icecream".) + + disable_notification (``bool``, *optional*): + Sends the message silently. + Users will receive a notification with no sound. + + reply_to_message_id (``int``, *optional*): + If the message is a reply, ID of the original message + + schedule_date (:py:obj:`~datetime.datetime`, *optional*): + Date when the message will be automatically sent. + + protect_content (``bool``, *optional*): + Protects the contents of the sent message from forwarding and saving. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*): + Additional interface options. An object for an inline keyboard, custom reply keyboard, + instructions to remove reply keyboard or to force a reply from the user. + + Returns: + :obj:`~pyrogram.types.Message`: On success, the sent venue message is returned. + + Example: + .. code-block:: python + + app.send_venue( + "me", latitude, longitude, + "Venue title", "Venue address") + """ + r = await self.invoke( + raw.functions.messages.SendMedia( + peer=await self.resolve_peer(chat_id), + media=raw.types.InputMediaVenue( + geo_point=raw.types.InputGeoPoint( + lat=latitude, + long=longitude + ), + title=title, + address=address, + provider="", + venue_id=foursquare_id, + venue_type=foursquare_type + ), + message="", + silent=disable_notification or None, + reply_to_msg_id=reply_to_message_id, + random_id=self.rnd_id(), + schedule_date=utils.datetime_to_timestamp(schedule_date), + noforwards=protect_content, + reply_markup=await reply_markup.write(self) if reply_markup else None + ) + ) + + for i in r.updates: + if isinstance(i, (raw.types.UpdateNewMessage, + raw.types.UpdateNewChannelMessage, + raw.types.UpdateNewScheduledMessage)): + return await types.Message._parse( + self, i.message, + {i.id: i for i in r.users}, + {i.id: i for i in r.chats}, + is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage) + ) diff --git a/pyrogram/methods/messages/send_video.py b/pyrogram/methods/messages/send_video.py new file mode 100644 index 00000000000..e869dd172d2 --- /dev/null +++ b/pyrogram/methods/messages/send_video.py @@ -0,0 +1,261 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import os +import re +from datetime import datetime +from typing import Union, BinaryIO, List, Optional, Callable + +import pyrogram +from pyrogram import StopTransmission, enums +from pyrogram import raw +from pyrogram import types +from pyrogram import utils +from pyrogram.errors import FilePartMissing +from pyrogram.file_id import FileType + + +class SendVideo: + async def send_video( + self: "pyrogram.Client", + chat_id: Union[int, str], + video: Union[str, BinaryIO], + caption: str = "", + parse_mode: Optional["enums.ParseMode"] = None, + caption_entities: List["types.MessageEntity"] = None, + has_spoiler: bool = None, + ttl_seconds: int = None, + duration: int = 0, + width: int = 0, + height: int = 0, + thumb: Union[str, BinaryIO] = None, + file_name: str = None, + supports_streaming: bool = True, + disable_notification: bool = None, + reply_to_message_id: int = None, + schedule_date: datetime = None, + protect_content: bool = None, + reply_markup: Union[ + "types.InlineKeyboardMarkup", + "types.ReplyKeyboardMarkup", + "types.ReplyKeyboardRemove", + "types.ForceReply" + ] = None, + progress: Callable = None, + progress_args: tuple = () + ) -> Optional["types.Message"]: + """Send video files. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + video (``str`` | ``BinaryIO``): + Video to send. + Pass a file_id as string to send a video that exists on the Telegram servers, + pass an HTTP URL as a string for Telegram to get a video from the Internet, + pass a file path as string to upload a new video that exists on your local machine, or + pass a binary file-like object with its attribute ".name" set for in-memory uploads. + + caption (``str``, *optional*): + Video caption, 0-1024 characters. + + parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*): + By default, texts are parsed using both Markdown and HTML styles. + You can combine both syntaxes together. + + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + + has_spoiler (``bool``, *optional*): + Pass True if the video needs to be covered with a spoiler animation. + + ttl_seconds (``int``, *optional*): + Self-Destruct Timer. + If you set a timer, the video will self-destruct in *ttl_seconds* + seconds after it was viewed. + + duration (``int``, *optional*): + Duration of sent video in seconds. + + width (``int``, *optional*): + Video width. + + height (``int``, *optional*): + Video height. + + thumb (``str`` | ``BinaryIO``, *optional*): + Thumbnail of the video sent. + The thumbnail should be in JPEG format and less than 200 KB in size. + A thumbnail's width and height should not exceed 320 pixels. + Thumbnails can't be reused and can be only uploaded as a new file. + + file_name (``str``, *optional*): + File name of the video sent. + Defaults to file's path basename. + + supports_streaming (``bool``, *optional*): + Pass True, if the uploaded video is suitable for streaming. + Defaults to True. + + disable_notification (``bool``, *optional*): + Sends the message silently. + Users will receive a notification with no sound. + + reply_to_message_id (``int``, *optional*): + If the message is a reply, ID of the original message. + + schedule_date (:py:obj:`~datetime.datetime`, *optional*): + Date when the message will be automatically sent. + + protect_content (``bool``, *optional*): + Protects the contents of the sent message from forwarding and saving. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*): + Additional interface options. An object for an inline keyboard, custom reply keyboard, + instructions to remove reply keyboard or to force a reply from the user. + + progress (``Callable``, *optional*): + Pass a callback function to view the file transmission progress. + The function must take *(current, total)* as positional arguments (look at Other Parameters below for a + detailed description) and will be called back each time a new file chunk has been successfully + transmitted. + + progress_args (``tuple``, *optional*): + Extra custom arguments for the progress callback function. + You can pass anything you need to be available in the progress callback scope; for example, a Message + object or a Client instance in order to edit the message with the updated progress status. + + Other Parameters: + current (``int``): + The amount of bytes transmitted so far. + + total (``int``): + The total size of the file. + + *args (``tuple``, *optional*): + Extra custom arguments as defined in the ``progress_args`` parameter. + You can either keep ``*args`` or add every single extra argument in your function signature. + + Returns: + :obj:`~pyrogram.types.Message` | ``None``: On success, the sent video message is returned, otherwise, in + case the upload is deliberately stopped with :meth:`~pyrogram.Client.stop_transmission`, None is returned. + + Example: + .. code-block:: python + + # Send video by uploading from local file + await app.send_video("me", "video.mp4") + + # Add caption to the video + await app.send_video("me", "video.mp4", caption="video caption") + + # Send self-destructing video + await app.send_video("me", "video.mp4", ttl_seconds=10) + + # Keep track of the progress while uploading + async def progress(current, total): + print(f"{current * 100 / total:.1f}%") + + await app.send_video("me", "video.mp4", progress=progress) + """ + file = None + + try: + if isinstance(video, str): + if os.path.isfile(video): + thumb = await self.save_file(thumb) + file = await self.save_file(video, progress=progress, progress_args=progress_args) + media = raw.types.InputMediaUploadedDocument( + mime_type=self.guess_mime_type(video) or "video/mp4", + file=file, + ttl_seconds=ttl_seconds, + spoiler=has_spoiler, + thumb=thumb, + attributes=[ + raw.types.DocumentAttributeVideo( + supports_streaming=supports_streaming or None, + duration=duration, + w=width, + h=height + ), + raw.types.DocumentAttributeFilename(file_name=file_name or os.path.basename(video)) + ] + ) + elif re.match("^https?://", video): + media = raw.types.InputMediaDocumentExternal( + url=video, + ttl_seconds=ttl_seconds, + spoiler=has_spoiler + ) + else: + media = utils.get_input_media_from_file_id(video, FileType.VIDEO, ttl_seconds=ttl_seconds) + else: + thumb = await self.save_file(thumb) + file = await self.save_file(video, progress=progress, progress_args=progress_args) + media = raw.types.InputMediaUploadedDocument( + mime_type=self.guess_mime_type(file_name or video.name) or "video/mp4", + file=file, + ttl_seconds=ttl_seconds, + spoiler=has_spoiler, + thumb=thumb, + attributes=[ + raw.types.DocumentAttributeVideo( + supports_streaming=supports_streaming or None, + duration=duration, + w=width, + h=height + ), + raw.types.DocumentAttributeFilename(file_name=file_name or video.name) + ] + ) + + while True: + try: + r = await self.invoke( + raw.functions.messages.SendMedia( + peer=await self.resolve_peer(chat_id), + media=media, + silent=disable_notification or None, + reply_to_msg_id=reply_to_message_id, + random_id=self.rnd_id(), + schedule_date=utils.datetime_to_timestamp(schedule_date), + noforwards=protect_content, + reply_markup=await reply_markup.write(self) if reply_markup else None, + **await utils.parse_text_entities(self, caption, parse_mode, caption_entities) + ) + ) + except FilePartMissing as e: + await self.save_file(video, file_id=file.id, file_part=e.value) + else: + for i in r.updates: + if isinstance(i, (raw.types.UpdateNewMessage, + raw.types.UpdateNewChannelMessage, + raw.types.UpdateNewScheduledMessage)): + return await types.Message._parse( + self, i.message, + {i.id: i for i in r.users}, + {i.id: i for i in r.chats}, + is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage) + ) + except StopTransmission: + return None diff --git a/pyrogram/methods/messages/send_video_note.py b/pyrogram/methods/messages/send_video_note.py new file mode 100644 index 00000000000..9c615d3116b --- /dev/null +++ b/pyrogram/methods/messages/send_video_note.py @@ -0,0 +1,203 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import os +from datetime import datetime +from typing import Union, BinaryIO, Optional, Callable + +import pyrogram +from pyrogram import StopTransmission +from pyrogram import raw +from pyrogram import types +from pyrogram import utils +from pyrogram.errors import FilePartMissing +from pyrogram.file_id import FileType + + +class SendVideoNote: + async def send_video_note( + self: "pyrogram.Client", + chat_id: Union[int, str], + video_note: Union[str, BinaryIO], + duration: int = 0, + length: int = 1, + thumb: Union[str, BinaryIO] = None, + disable_notification: bool = None, + reply_to_message_id: int = None, + schedule_date: datetime = None, + protect_content: bool = None, + reply_markup: Union[ + "types.InlineKeyboardMarkup", + "types.ReplyKeyboardMarkup", + "types.ReplyKeyboardRemove", + "types.ForceReply" + ] = None, + progress: Callable = None, + progress_args: tuple = () + ) -> Optional["types.Message"]: + """Send video messages. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + video_note (``str`` | ``BinaryIO``): + Video note to send. + Pass a file_id as string to send a video note that exists on the Telegram servers, + pass a file path as string to upload a new video note that exists on your local machine, or + pass a binary file-like object with its attribute ".name" set for in-memory uploads. + Sending video notes by a URL is currently unsupported. + + duration (``int``, *optional*): + Duration of sent video in seconds. + + length (``int``, *optional*): + Video width and height. + + thumb (``str`` | ``BinaryIO``, *optional*): + Thumbnail of the video sent. + The thumbnail should be in JPEG format and less than 200 KB in size. + A thumbnail's width and height should not exceed 320 pixels. + Thumbnails can't be reused and can be only uploaded as a new file. + + disable_notification (``bool``, *optional*): + Sends the message silently. + Users will receive a notification with no sound. + + reply_to_message_id (``int``, *optional*): + If the message is a reply, ID of the original message + + schedule_date (:py:obj:`~datetime.datetime`, *optional*): + Date when the message will be automatically sent. + + protect_content (``bool``, *optional*): + Protects the contents of the sent message from forwarding and saving. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*): + Additional interface options. An object for an inline keyboard, custom reply keyboard, + instructions to remove reply keyboard or to force a reply from the user. + + progress (``Callable``, *optional*): + Pass a callback function to view the file transmission progress. + The function must take *(current, total)* as positional arguments (look at Other Parameters below for a + detailed description) and will be called back each time a new file chunk has been successfully + transmitted. + + progress_args (``tuple``, *optional*): + Extra custom arguments for the progress callback function. + You can pass anything you need to be available in the progress callback scope; for example, a Message + object or a Client instance in order to edit the message with the updated progress status. + + Other Parameters: + current (``int``): + The amount of bytes transmitted so far. + + total (``int``): + The total size of the file. + + *args (``tuple``, *optional*): + Extra custom arguments as defined in the ``progress_args`` parameter. + You can either keep ``*args`` or add every single extra argument in your function signature. + + Returns: + :obj:`~pyrogram.types.Message` | ``None``: On success, the sent video note message is returned, otherwise, + in case the upload is deliberately stopped with :meth:`~pyrogram.Client.stop_transmission`, None is + returned. + + Example: + .. code-block:: python + + # Send video note by uploading from local file + await app.send_video_note("me", "video_note.mp4") + + # Set video note length + await app.send_video_note("me", "video_note.mp4", length=25) + """ + file = None + + try: + if isinstance(video_note, str): + if os.path.isfile(video_note): + thumb = await self.save_file(thumb) + file = await self.save_file(video_note, progress=progress, progress_args=progress_args) + media = raw.types.InputMediaUploadedDocument( + mime_type=self.guess_mime_type(video_note) or "video/mp4", + file=file, + thumb=thumb, + attributes=[ + raw.types.DocumentAttributeVideo( + round_message=True, + duration=duration, + w=length, + h=length + ) + ] + ) + else: + media = utils.get_input_media_from_file_id(video_note, FileType.VIDEO_NOTE) + else: + thumb = await self.save_file(thumb) + file = await self.save_file(video_note, progress=progress, progress_args=progress_args) + media = raw.types.InputMediaUploadedDocument( + mime_type=self.guess_mime_type(video_note.name) or "video/mp4", + file=file, + thumb=thumb, + attributes=[ + raw.types.DocumentAttributeVideo( + round_message=True, + duration=duration, + w=length, + h=length + ) + ] + ) + + while True: + try: + r = await self.invoke( + raw.functions.messages.SendMedia( + peer=await self.resolve_peer(chat_id), + media=media, + silent=disable_notification or None, + reply_to_msg_id=reply_to_message_id, + random_id=self.rnd_id(), + schedule_date=utils.datetime_to_timestamp(schedule_date), + noforwards=protect_content, + reply_markup=await reply_markup.write(self) if reply_markup else None, + message="" + ) + ) + except FilePartMissing as e: + await self.save_file(video_note, file_id=file.id, file_part=e.value) + else: + for i in r.updates: + if isinstance(i, (raw.types.UpdateNewMessage, + raw.types.UpdateNewChannelMessage, + raw.types.UpdateNewScheduledMessage)): + return await types.Message._parse( + self, i.message, + {i.id: i for i in r.users}, + {i.id: i for i in r.chats}, + is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage) + ) + except StopTransmission: + return None diff --git a/pyrogram/methods/messages/send_voice.py b/pyrogram/methods/messages/send_voice.py new file mode 100644 index 00000000000..266702fd839 --- /dev/null +++ b/pyrogram/methods/messages/send_voice.py @@ -0,0 +1,204 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import os +import re +from datetime import datetime +from typing import Union, BinaryIO, List, Optional, Callable + +import pyrogram +from pyrogram import StopTransmission, enums +from pyrogram import raw +from pyrogram import types +from pyrogram import utils +from pyrogram.errors import FilePartMissing +from pyrogram.file_id import FileType + + +class SendVoice: + async def send_voice( + self: "pyrogram.Client", + chat_id: Union[int, str], + voice: Union[str, BinaryIO], + caption: str = "", + parse_mode: Optional["enums.ParseMode"] = None, + caption_entities: List["types.MessageEntity"] = None, + duration: int = 0, + disable_notification: bool = None, + reply_to_message_id: int = None, + schedule_date: datetime = None, + protect_content: bool = None, + reply_markup: Union[ + "types.InlineKeyboardMarkup", + "types.ReplyKeyboardMarkup", + "types.ReplyKeyboardRemove", + "types.ForceReply" + ] = None, + progress: Callable = None, + progress_args: tuple = () + ) -> Optional["types.Message"]: + """Send audio files. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + voice (``str`` | ``BinaryIO``): + Audio file to send. + Pass a file_id as string to send an audio that exists on the Telegram servers, + pass an HTTP URL as a string for Telegram to get an audio from the Internet, + pass a file path as string to upload a new audio that exists on your local machine, or + pass a binary file-like object with its attribute ".name" set for in-memory uploads. + + caption (``str``, *optional*): + Voice message caption, 0-1024 characters. + + parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*): + By default, texts are parsed using both Markdown and HTML styles. + You can combine both syntaxes together. + + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): + List of special entities that appear in the caption, which can be specified instead of *parse_mode*. + + duration (``int``, *optional*): + Duration of the voice message in seconds. + + disable_notification (``bool``, *optional*): + Sends the message silently. + Users will receive a notification with no sound. + + reply_to_message_id (``int``, *optional*): + If the message is a reply, ID of the original message + + schedule_date (:py:obj:`~datetime.datetime`, *optional*): + Date when the message will be automatically sent. + + protect_content (``bool``, *optional*): + Protects the contents of the sent message from forwarding and saving. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*): + Additional interface options. An object for an inline keyboard, custom reply keyboard, + instructions to remove reply keyboard or to force a reply from the user. + + progress (``Callable``, *optional*): + Pass a callback function to view the file transmission progress. + The function must take *(current, total)* as positional arguments (look at Other Parameters below for a + detailed description) and will be called back each time a new file chunk has been successfully + transmitted. + + progress_args (``tuple``, *optional*): + Extra custom arguments for the progress callback function. + You can pass anything you need to be available in the progress callback scope; for example, a Message + object or a Client instance in order to edit the message with the updated progress status. + + Other Parameters: + current (``int``): + The amount of bytes transmitted so far. + + total (``int``): + The total size of the file. + + *args (``tuple``, *optional*): + Extra custom arguments as defined in the ``progress_args`` parameter. + You can either keep ``*args`` or add every single extra argument in your function signature. + + Returns: + :obj:`~pyrogram.types.Message` | ``None``: On success, the sent voice message is returned, otherwise, in + case the upload is deliberately stopped with :meth:`~pyrogram.Client.stop_transmission`, None is returned. + + Example: + .. code-block:: python + + # Send voice note by uploading from local file + await app.send_voice("me", "voice.ogg") + + # Add caption to the voice note + await app.send_voice("me", "voice.ogg", caption="voice caption") + + # Set voice note duration + await app.send_voice("me", "voice.ogg", duration=20) + """ + file = None + + try: + if isinstance(voice, str): + if os.path.isfile(voice): + file = await self.save_file(voice, progress=progress, progress_args=progress_args) + media = raw.types.InputMediaUploadedDocument( + mime_type=self.guess_mime_type(voice) or "audio/mpeg", + file=file, + attributes=[ + raw.types.DocumentAttributeAudio( + voice=True, + duration=duration + ) + ] + ) + elif re.match("^https?://", voice): + media = raw.types.InputMediaDocumentExternal( + url=voice + ) + else: + media = utils.get_input_media_from_file_id(voice, FileType.VOICE) + else: + file = await self.save_file(voice, progress=progress, progress_args=progress_args) + media = raw.types.InputMediaUploadedDocument( + mime_type=self.guess_mime_type(voice.name) or "audio/mpeg", + file=file, + attributes=[ + raw.types.DocumentAttributeAudio( + voice=True, + duration=duration + ) + ] + ) + + while True: + try: + r = await self.invoke( + raw.functions.messages.SendMedia( + peer=await self.resolve_peer(chat_id), + media=media, + silent=disable_notification or None, + reply_to_msg_id=reply_to_message_id, + random_id=self.rnd_id(), + schedule_date=utils.datetime_to_timestamp(schedule_date), + noforwards=protect_content, + reply_markup=await reply_markup.write(self) if reply_markup else None, + **await utils.parse_text_entities(self, caption, parse_mode, caption_entities) + ) + ) + except FilePartMissing as e: + await self.save_file(voice, file_id=file.id, file_part=e.value) + else: + for i in r.updates: + if isinstance(i, (raw.types.UpdateNewMessage, + raw.types.UpdateNewChannelMessage, + raw.types.UpdateNewScheduledMessage)): + return await types.Message._parse( + self, i.message, + {i.id: i for i in r.users}, + {i.id: i for i in r.chats}, + is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage) + ) + except StopTransmission: + return None diff --git a/pyrogram/methods/messages/stop_poll.py b/pyrogram/methods/messages/stop_poll.py new file mode 100644 index 00000000000..89a33434b7f --- /dev/null +++ b/pyrogram/methods/messages/stop_poll.py @@ -0,0 +1,77 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class StopPoll: + async def stop_poll( + self: "pyrogram.Client", + chat_id: Union[int, str], + message_id: int, + reply_markup: "types.InlineKeyboardMarkup" = None + ) -> "types.Poll": + """Stop a poll which was sent by you. + + Stopped polls can't be reopened and nobody will be able to vote in it anymore. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + message_id (``int``): + Identifier of the original message with the poll. + + reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*): + An InlineKeyboardMarkup object. + + Returns: + :obj:`~pyrogram.types.Poll`: On success, the stopped poll with the final results is returned. + + Example: + .. code-block:: python + + await app.stop_poll(chat_id, message_id) + """ + poll = (await self.get_messages(chat_id, message_id)).poll + + r = await self.invoke( + raw.functions.messages.EditMessage( + peer=await self.resolve_peer(chat_id), + id=message_id, + media=raw.types.InputMediaPoll( + poll=raw.types.Poll( + id=int(poll.id), + closed=True, + question="", + answers=[] + ) + ), + reply_markup=await reply_markup.write(self) if reply_markup else None + ) + ) + + return types.Poll._parse(self, r.updates[0]) diff --git a/pyrogram/methods/messages/stream_media.py b/pyrogram/methods/messages/stream_media.py new file mode 100644 index 00000000000..b432badb4cb --- /dev/null +++ b/pyrogram/methods/messages/stream_media.py @@ -0,0 +1,106 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import math +from typing import Union, Optional, BinaryIO + +import pyrogram +from pyrogram import types +from pyrogram.file_id import FileId + + +class StreamMedia: + async def stream_media( + self: "pyrogram.Client", + message: Union["types.Message", str], + limit: int = 0, + offset: int = 0 + ) -> Optional[Union[str, BinaryIO]]: + """Stream the media from a message chunk by chunk. + + You can use this method to partially download a file into memory or to selectively download chunks of file. + The chunk maximum size is 1 MiB (1024 * 1024 bytes). + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + message (:obj:`~pyrogram.types.Message` | ``str``): + Pass a Message containing the media, the media itself (message.audio, message.video, ...) or a file id + as string. + + limit (``int``, *optional*): + Limit the amount of chunks to stream. + Defaults to 0 (stream the whole media). + + offset (``int``, *optional*): + How many chunks to skip before starting to stream. + Defaults to 0 (start from the beginning). + + Returns: + ``Generator``: A generator yielding bytes chunk by chunk + + Example: + .. code-block:: python + + # Stream the whole media + async for chunk in app.stream_media(message): + print(len(chunk)) + + # Stream the first 3 chunks only + async for chunk in app.stream_media(message, limit=3): + print(len(chunk)) + + # Stream the rest of the media by skipping the first 3 chunks + async for chunk in app.stream_media(message, offset=3): + print(len(chunk)) + + # Stream the last 3 chunks only (negative offset) + async for chunk in app.stream_media(message, offset=-3): + print(len(chunk)) + """ + available_media = ("audio", "document", "photo", "sticker", "animation", "video", "voice", "video_note", + "new_chat_photo") + + if isinstance(message, types.Message): + for kind in available_media: + media = getattr(message, kind, None) + + if media is not None: + break + else: + raise ValueError("This message doesn't contain any downloadable media") + else: + media = message + + if isinstance(media, str): + file_id_str = media + else: + file_id_str = media.file_id + + file_id_obj = FileId.decode(file_id_str) + file_size = getattr(media, "file_size", 0) + + if offset < 0: + if file_size == 0: + raise ValueError("Negative offsets are not supported for file ids, pass a Message object instead") + + chunks = math.ceil(file_size / 1024 / 1024) + offset += chunks + + async for chunk in self.get_file(file_id_obj, file_size, limit, offset): + yield chunk diff --git a/pyrogram/methods/messages/vote_poll.py b/pyrogram/methods/messages/vote_poll.py new file mode 100644 index 00000000000..620fac5a151 --- /dev/null +++ b/pyrogram/methods/messages/vote_poll.py @@ -0,0 +1,69 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union, List + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class VotePoll: + async def vote_poll( + self: "pyrogram.Client", + chat_id: Union[int, str], + message_id: id, + options: Union[int, List[int]] + ) -> "types.Poll": + """Vote a poll. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + message_id (``int``): + Identifier of the original message with the poll. + + options (``Int`` | List of ``int``): + Index or list of indexes (for multiple answers) of the poll option(s) you want to vote for (0 to 9). + + Returns: + :obj:`~pyrogram.types.Poll` - On success, the poll with the chosen option is returned. + + Example: + .. code-block:: python + + await app.vote_poll(chat_id, message_id, 6) + """ + + poll = (await self.get_messages(chat_id, message_id)).poll + options = [options] if not isinstance(options, list) else options + + r = await self.invoke( + raw.functions.messages.SendVote( + peer=await self.resolve_peer(chat_id), + msg_id=message_id, + options=[poll.options[option].data for option in options] + ) + ) + + return types.Poll._parse(self, r.updates[0]) diff --git a/pyrogram/methods/password/__init__.py b/pyrogram/methods/password/__init__.py new file mode 100644 index 00000000000..e8d42926a47 --- /dev/null +++ b/pyrogram/methods/password/__init__.py @@ -0,0 +1,29 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 .change_cloud_password import ChangeCloudPassword +from .enable_cloud_password import EnableCloudPassword +from .remove_cloud_password import RemoveCloudPassword + + +class Password( + RemoveCloudPassword, + ChangeCloudPassword, + EnableCloudPassword +): + pass diff --git a/pyrogram/methods/password/change_cloud_password.py b/pyrogram/methods/password/change_cloud_password.py new file mode 100644 index 00000000000..29540d653bb --- /dev/null +++ b/pyrogram/methods/password/change_cloud_password.py @@ -0,0 +1,82 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import os + +import pyrogram +from pyrogram import raw +from pyrogram.utils import compute_password_hash, compute_password_check, btoi, itob + + +class ChangeCloudPassword: + async def change_cloud_password( + self: "pyrogram.Client", + current_password: str, + new_password: str, + new_hint: str = "" + ) -> bool: + """Change your Two-Step Verification password (Cloud Password) with a new one. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + current_password (``str``): + Your current password. + + new_password (``str``): + Your new password. + + new_hint (``str``, *optional*): + A new password hint. + + Returns: + ``bool``: True on success. + + Raises: + ValueError: In case there is no cloud password to change. + + Example: + .. code-block:: python + + # Change password only + await app.change_cloud_password("current_password", "new_password") + + # Change password and hint + await app.change_cloud_password("current_password", "new_password", new_hint="hint") + """ + r = await self.invoke(raw.functions.account.GetPassword()) + + if not r.has_password: + raise ValueError("There is no cloud password to change") + + r.new_algo.salt1 += os.urandom(32) + new_hash = btoi(compute_password_hash(r.new_algo, new_password)) + new_hash = itob(pow(r.new_algo.g, new_hash, btoi(r.new_algo.p))) + + await self.invoke( + raw.functions.account.UpdatePasswordSettings( + password=compute_password_check(r, current_password), + new_settings=raw.types.account.PasswordInputSettings( + new_algo=r.new_algo, + new_password_hash=new_hash, + hint=new_hint + ) + ) + ) + + return True diff --git a/pyrogram/methods/password/enable_cloud_password.py b/pyrogram/methods/password/enable_cloud_password.py new file mode 100644 index 00000000000..8f5e59c5707 --- /dev/null +++ b/pyrogram/methods/password/enable_cloud_password.py @@ -0,0 +1,88 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import os + +import pyrogram +from pyrogram import raw +from pyrogram.utils import compute_password_hash, btoi, itob + + +class EnableCloudPassword: + async def enable_cloud_password( + self: "pyrogram.Client", + password: str, + hint: str = "", + email: str = None + ) -> bool: + """Enable the Two-Step Verification security feature (Cloud Password) on your account. + + This password will be asked when you log-in on a new device in addition to the SMS code. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + password (``str``): + Your password. + + hint (``str``, *optional*): + A password hint. + + email (``str``, *optional*): + Recovery e-mail. + + Returns: + ``bool``: True on success. + + Raises: + ValueError: In case there is already a cloud password enabled. + + Example: + .. code-block:: python + + # Enable password without hint and email + await app.enable_cloud_password("password") + + # Enable password with hint and without email + await app.enable_cloud_password("password", hint="hint") + + # Enable password with hint and email + await app.enable_cloud_password("password", hint="hint", email="user@email.com") + """ + r = await self.invoke(raw.functions.account.GetPassword()) + + if r.has_password: + raise ValueError("There is already a cloud password enabled") + + r.new_algo.salt1 += os.urandom(32) + new_hash = btoi(compute_password_hash(r.new_algo, password)) + new_hash = itob(pow(r.new_algo.g, new_hash, btoi(r.new_algo.p))) + + await self.invoke( + raw.functions.account.UpdatePasswordSettings( + password=raw.types.InputCheckPasswordEmpty(), + new_settings=raw.types.account.PasswordInputSettings( + new_algo=r.new_algo, + new_password_hash=new_hash, + hint=hint, + email=email + ) + ) + ) + + return True diff --git a/pyrogram/methods/password/remove_cloud_password.py b/pyrogram/methods/password/remove_cloud_password.py new file mode 100644 index 00000000000..0ec16e12cd0 --- /dev/null +++ b/pyrogram/methods/password/remove_cloud_password.py @@ -0,0 +1,64 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import pyrogram +from pyrogram import raw +from pyrogram.utils import compute_password_check + + +class RemoveCloudPassword: + async def remove_cloud_password( + self: "pyrogram.Client", + password: str + ) -> bool: + """Turn off the Two-Step Verification security feature (Cloud Password) on your account. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + password (``str``): + Your current password. + + Returns: + ``bool``: True on success. + + Raises: + ValueError: In case there is no cloud password to remove. + + Example: + .. code-block:: python + + await app.remove_cloud_password("password") + """ + r = await self.invoke(raw.functions.account.GetPassword()) + + if not r.has_password: + raise ValueError("There is no cloud password to remove") + + await self.invoke( + raw.functions.account.UpdatePasswordSettings( + password=compute_password_check(r, password), + new_settings=raw.types.account.PasswordInputSettings( + new_algo=raw.types.PasswordKdfAlgoUnknown(), + new_password_hash=b"", + hint="" + ) + ) + ) + + return True diff --git a/pyrogram/methods/users/__init__.py b/pyrogram/methods/users/__init__.py new file mode 100644 index 00000000000..31fda1dc3a7 --- /dev/null +++ b/pyrogram/methods/users/__init__.py @@ -0,0 +1,49 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 .block_user import BlockUser +from .delete_profile_photos import DeleteProfilePhotos +from .get_chat_photos import GetChatPhotos +from .get_chat_photos_count import GetChatPhotosCount +from .get_common_chats import GetCommonChats +from .get_default_emoji_statuses import GetDefaultEmojiStatuses +from .get_me import GetMe +from .get_users import GetUsers +from .set_emoji_status import SetEmojiStatus +from .set_profile_photo import SetProfilePhoto +from .set_username import SetUsername +from .unblock_user import UnblockUser +from .update_profile import UpdateProfile + + +class Users( + BlockUser, + GetCommonChats, + GetChatPhotos, + SetProfilePhoto, + DeleteProfilePhotos, + GetUsers, + GetMe, + SetUsername, + GetChatPhotosCount, + UnblockUser, + UpdateProfile, + GetDefaultEmojiStatuses, + SetEmojiStatus +): + pass diff --git a/pyrogram/methods/users/block_user.py b/pyrogram/methods/users/block_user.py new file mode 100644 index 00000000000..25cd0ce84e4 --- /dev/null +++ b/pyrogram/methods/users/block_user.py @@ -0,0 +1,54 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw + + +class BlockUser: + async def block_user( + self: "pyrogram.Client", + user_id: Union[int, str] + ) -> bool: + """Block a user. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + user_id (``int`` | ``str``):: + Unique identifier (int) or username (str) of the target user. + For you yourself you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + Returns: + ``bool``: True on success + + Example: + .. code-block:: python + + await app.block_user(user_id) + """ + return bool( + await self.invoke( + raw.functions.contacts.Block( + id=await self.resolve_peer(user_id) + ) + ) + ) diff --git a/pyrogram/methods/users/delete_profile_photos.py b/pyrogram/methods/users/delete_profile_photos.py new file mode 100644 index 00000000000..2b52d02f59c --- /dev/null +++ b/pyrogram/methods/users/delete_profile_photos.py @@ -0,0 +1,63 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import List, Union + +import pyrogram +from pyrogram import raw +from pyrogram import utils +from pyrogram.file_id import FileType + + +class DeleteProfilePhotos: + async def delete_profile_photos( + self: "pyrogram.Client", + photo_ids: Union[str, List[str]] + ) -> bool: + """Delete your own profile photos. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + photo_ids (``str`` | List of ``str``): + A single :obj:`~pyrogram.types.Photo` id as string or multiple ids as list of strings for deleting + more than one photos at once. + + Returns: + ``bool``: True on success. + + Example: + .. code-block:: python + + # Get the photos to be deleted + photos = [p async for p in app.get_chat_photos("me")] + + # Delete one photo + await app.delete_profile_photos(photos[0].file_id) + + # Delete the rest of the photos + await app.delete_profile_photos([p.file_id for p in photos[1:]]) + """ + photo_ids = photo_ids if isinstance(photo_ids, list) else [photo_ids] + input_photos = [utils.get_input_media_from_file_id(i, FileType.PHOTO).id for i in photo_ids] + + return bool(await self.invoke( + raw.functions.photos.DeletePhotos( + id=input_photos + ) + )) diff --git a/pyrogram/methods/users/get_chat_photos.py b/pyrogram/methods/users/get_chat_photos.py new file mode 100644 index 00000000000..d22c68dcd87 --- /dev/null +++ b/pyrogram/methods/users/get_chat_photos.py @@ -0,0 +1,135 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union, AsyncGenerator, Optional + +import pyrogram +from pyrogram import types, raw, utils + + +class GetChatPhotos: + async def get_chat_photos( + self: "pyrogram.Client", + chat_id: Union[int, str], + limit: int = 0, + ) -> Optional[AsyncGenerator["types.Photo", None]]: + """Get a chat or a user profile photos sequentially. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + limit (``int``, *optional*): + Limits the number of profile photos to be retrieved. + By default, no limit is applied and all profile photos are returned. + + Returns: + ``Generator``: A generator yielding :obj:`~pyrogram.types.Photo` objects. + + Example: + .. code-block:: python + + async for photo in app.get_chat_photos("me"): + print(photo) + """ + peer_id = await self.resolve_peer(chat_id) + + if isinstance(peer_id, raw.types.InputPeerChannel): + r = await self.invoke( + raw.functions.channels.GetFullChannel( + channel=peer_id + ) + ) + + current = types.Photo._parse(self, r.full_chat.chat_photo) or [] + + r = await utils.parse_messages( + self, + await self.invoke( + raw.functions.messages.Search( + peer=peer_id, + q="", + filter=raw.types.InputMessagesFilterChatPhotos(), + min_date=0, + max_date=0, + offset_id=0, + add_offset=0, + limit=limit, + max_id=0, + min_id=0, + hash=0 + ) + ) + ) + + extra = [message.new_chat_photo for message in r] + + if extra: + if current: + photos = ([current] + extra) if current.file_id != extra[0].file_id else extra + else: + photos = extra + else: + if current: + photos = [current] + else: + photos = [] + + current = 0 + + for photo in photos: + yield photo + + current += 1 + + if current >= limit: + return + else: + current = 0 + total = limit or (1 << 31) + limit = min(100, total) + offset = 0 + + while True: + r = await self.invoke( + raw.functions.photos.GetUserPhotos( + user_id=peer_id, + offset=offset, + max_id=0, + limit=limit + ) + ) + + photos = [types.Photo._parse(self, photo) for photo in r.photos] + + if not photos: + return + + offset += len(photos) + + for photo in photos: + yield photo + + current += 1 + + if current >= total: + return diff --git a/pyrogram/methods/users/get_chat_photos_count.py b/pyrogram/methods/users/get_chat_photos_count.py new file mode 100644 index 00000000000..d4cf1594f17 --- /dev/null +++ b/pyrogram/methods/users/get_chat_photos_count.py @@ -0,0 +1,74 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw + + +class GetChatPhotosCount: + async def get_chat_photos_count( + self: "pyrogram.Client", + chat_id: Union[int, str] + ) -> int: + """Get the total count of photos for a chat. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + Returns: + ``int``: On success, the user profile photos count is returned. + + Example: + .. code-block:: python + + count = await app.get_chat_photos_count("me") + print(count) + """ + + peer_id = await self.resolve_peer(chat_id) + + if isinstance(peer_id, raw.types.InputPeerChannel): + r = await self.invoke( + raw.functions.messages.GetSearchCounters( + peer=peer_id, + filters=[raw.types.InputMessagesFilterChatPhotos()], + ) + ) + + return r[0].count + else: + r = await self.invoke( + raw.functions.photos.GetUserPhotos( + user_id=peer_id, + offset=0, + max_id=0, + limit=1 + ) + ) + + if isinstance(r, raw.types.photos.Photos): + return len(r.photos) + else: + return r.count diff --git a/pyrogram/methods/users/get_common_chats.py b/pyrogram/methods/users/get_common_chats.py new file mode 100644 index 00000000000..31e2bac38e6 --- /dev/null +++ b/pyrogram/methods/users/get_common_chats.py @@ -0,0 +1,67 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union, List + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class GetCommonChats: + async def get_common_chats( + self: "pyrogram.Client", + user_id: Union[int, str] + ) -> List["types.Chat"]: + """Get the common chats you have with a user. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + user_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + Returns: + List of :obj:`~pyrogram.types.Chat`: On success, a list of the common chats is returned. + + Raises: + ValueError: If the user_id doesn't belong to a user. + + Example: + .. code-block:: python + + common = await app.get_common_chats(user_id) + print(common) + """ + + peer = await self.resolve_peer(user_id) + + if isinstance(peer, raw.types.InputPeerUser): + r = await self.invoke( + raw.functions.messages.GetCommonChats( + user_id=peer, + max_id=0, + limit=100, + ) + ) + + return types.List([types.Chat._parse_chat(self, x) for x in r.chats]) + + raise ValueError(f'The user_id "{user_id}" doesn\'t belong to a user') diff --git a/pyrogram/methods/users/get_default_emoji_statuses.py b/pyrogram/methods/users/get_default_emoji_statuses.py new file mode 100644 index 00000000000..cd3768b9a44 --- /dev/null +++ b/pyrogram/methods/users/get_default_emoji_statuses.py @@ -0,0 +1,47 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import List + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class GetDefaultEmojiStatuses: + async def get_default_emoji_statuses( + self: "pyrogram.Client", + ) -> List["types.EmojiStatus"]: + """Get the default emoji statuses. + + .. include:: /_includes/usable-by/users-bots.rst + + Returns: + List of :obj:`~pyrogram.types.EmojiStatus`: On success, a list of emoji statuses is returned. + + Example: + .. code-block:: python + + default_emoji_statuses = await app.get_default_emoji_statuses() + print(default_emoji_statuses) + """ + r = await self.invoke( + raw.functions.account.GetDefaultEmojiStatuses(hash=0) + ) + + return types.List([types.EmojiStatus._parse(self, i) for i in r.statuses]) diff --git a/pyrogram/methods/users/get_me.py b/pyrogram/methods/users/get_me.py new file mode 100644 index 00000000000..a8df3ae9f36 --- /dev/null +++ b/pyrogram/methods/users/get_me.py @@ -0,0 +1,49 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class GetMe: + async def get_me( + self: "pyrogram.Client" + ) -> "types.User": + """Get your own user identity. + + .. include:: /_includes/usable-by/users-bots.rst + + Returns: + :obj:`~pyrogram.types.User`: Information about the own logged in user/bot. + + Example: + .. code-block:: python + + me = await app.get_me() + print(me) + """ + r = await self.invoke( + raw.functions.users.GetFullUser( + id=raw.types.InputUserSelf() + ) + ) + + users = {u.id: u for u in r.users} + + return types.User._parse(self, users[r.full_user.id]) diff --git a/pyrogram/methods/users/get_users.py b/pyrogram/methods/users/get_users.py new file mode 100644 index 00000000000..39821a8c5db --- /dev/null +++ b/pyrogram/methods/users/get_users.py @@ -0,0 +1,71 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import asyncio +from typing import Union, List, Iterable + +import pyrogram +from pyrogram import raw +from pyrogram import types + + +class GetUsers: + async def get_users( + self: "pyrogram.Client", + user_ids: Union[int, str, Iterable[Union[int, str]]] + ) -> Union["types.User", List["types.User"]]: + """Get information about a user. + You can retrieve up to 200 users at once. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + user_ids (``int`` | ``str`` | Iterable of ``int`` or ``str``): + A list of User identifiers (id or username) or a single user id/username. + For a contact that exists in your Telegram address book you can use his phone number (str). + + Returns: + :obj:`~pyrogram.types.User` | List of :obj:`~pyrogram.types.User`: In case *user_ids* was not a list, + a single user is returned, otherwise a list of users is returned. + + Example: + .. code-block:: python + + # Get information about one user + await app.get_users("me") + + # Get information about multiple users at once + await app.get_users([user_id1, user_id2, user_id3]) + """ + + is_iterable = not isinstance(user_ids, (int, str)) + user_ids = list(user_ids) if is_iterable else [user_ids] + user_ids = await asyncio.gather(*[self.resolve_peer(i) for i in user_ids]) + + r = await self.invoke( + raw.functions.users.GetUsers( + id=user_ids + ) + ) + + users = types.List() + + for i in r: + users.append(types.User._parse(self, i)) + + return users if is_iterable else users[0] diff --git a/pyrogram/methods/users/set_emoji_status.py b/pyrogram/methods/users/set_emoji_status.py new file mode 100644 index 00000000000..2d8c77cc02c --- /dev/null +++ b/pyrogram/methods/users/set_emoji_status.py @@ -0,0 +1,58 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Optional + +import pyrogram +from pyrogram import raw, types + + +class SetEmojiStatus: + async def set_emoji_status( + self: "pyrogram.Client", + emoji_status: Optional["types.EmojiStatus"] = None + ) -> bool: + """Set the emoji status. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + emoji_status (:obj:`~pyrogram.types.EmojiStatus`, *optional*): + The emoji status to set. None to remove. + + Returns: + ``bool``: On success, True is returned. + + Example: + .. code-block:: python + + from pyrogram import types + + await app.set_emoji_status(types.EmojiStatus(custom_emoji_id=1234567890987654321)) + """ + await self.invoke( + raw.functions.account.UpdateEmojiStatus( + emoji_status=( + emoji_status.write() + if emoji_status + else raw.types.EmojiStatusEmpty() + ) + ) + ) + + return True diff --git a/pyrogram/methods/users/set_profile_photo.py b/pyrogram/methods/users/set_profile_photo.py new file mode 100644 index 00000000000..e08e669c4ea --- /dev/null +++ b/pyrogram/methods/users/set_profile_photo.py @@ -0,0 +1,75 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union, BinaryIO + +import pyrogram +from pyrogram import raw + + +class SetProfilePhoto: + async def set_profile_photo( + self: "pyrogram.Client", + *, + photo: Union[str, BinaryIO] = None, + video: Union[str, BinaryIO] = None + ) -> bool: + """Set a new profile photo or video (H.264/MPEG-4 AVC video, max 5 seconds). + + The ``photo`` and ``video`` arguments are mutually exclusive. + Pass either one as named argument (see examples below). + + .. note:: + + This method only works for Users. + Bots profile photos must be set using BotFather. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + photo (``str`` | ``BinaryIO``, *optional*): + Profile photo to set. + Pass a file path as string to upload a new photo that exists on your local machine or + pass a binary file-like object with its attribute ".name" set for in-memory uploads. + + video (``str`` | ``BinaryIO``, *optional*): + Profile video to set. + Pass a file path as string to upload a new video that exists on your local machine or + pass a binary file-like object with its attribute ".name" set for in-memory uploads. + + Returns: + ``bool``: True on success. + + Example: + .. code-block:: python + + # Set a new profile photo + await app.set_profile_photo(photo="new_photo.jpg") + + # Set a new profile video + await app.set_profile_photo(video="new_video.mp4") + """ + + return bool( + await self.invoke( + raw.functions.photos.UploadProfilePhoto( + file=await self.save_file(photo), + video=await self.save_file(video) + ) + ) + ) diff --git a/pyrogram/methods/users/set_username.py b/pyrogram/methods/users/set_username.py new file mode 100644 index 00000000000..d336628eecd --- /dev/null +++ b/pyrogram/methods/users/set_username.py @@ -0,0 +1,57 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Optional + +import pyrogram +from pyrogram import raw + + +class SetUsername: + async def set_username( + self: "pyrogram.Client", + username: Optional[str] + ) -> bool: + """Set your own username. + + This method only works for users, not bots. Bot usernames must be changed via Bot Support or by recreating + them from scratch using BotFather. To set a channel or supergroup username you can use + :meth:`~pyrogram.Client.set_chat_username`. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + username (``str`` | ``None``): + Username to set. "" (empty string) or None to remove it. + + Returns: + ``bool``: True on success. + + Example: + .. code-block:: python + + await app.set_username("new_username") + """ + + return bool( + await self.invoke( + raw.functions.account.UpdateUsername( + username=username or "" + ) + ) + ) diff --git a/pyrogram/methods/users/unblock_user.py b/pyrogram/methods/users/unblock_user.py new file mode 100644 index 00000000000..db4fdddcff4 --- /dev/null +++ b/pyrogram/methods/users/unblock_user.py @@ -0,0 +1,54 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 typing import Union + +import pyrogram +from pyrogram import raw + + +class UnblockUser: + async def unblock_user( + self: "pyrogram.Client", + user_id: Union[int, str] + ) -> bool: + """Unblock a user. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + user_id (``int`` | ``str``):: + Unique identifier (int) or username (str) of the target user. + For you yourself you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + Returns: + ``bool``: True on success + + Example: + .. code-block:: python + + await app.unblock_user(user_id) + """ + return bool( + await self.invoke( + raw.functions.contacts.Unblock( + id=await self.resolve_peer(user_id) + ) + ) + ) diff --git a/pyrogram/methods/users/update_profile.py b/pyrogram/methods/users/update_profile.py new file mode 100644 index 00000000000..6c10d16c9d1 --- /dev/null +++ b/pyrogram/methods/users/update_profile.py @@ -0,0 +1,72 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import pyrogram +from pyrogram import raw + + +class UpdateProfile: + async def update_profile( + self: "pyrogram.Client", + first_name: str = None, + last_name: str = None, + bio: str = None + ) -> bool: + """Update your profile details such as first name, last name and bio. + + You can omit the parameters you don't want to change. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + first_name (``str``, *optional*): + The new first name. + + last_name (``str``, *optional*): + The new last name. + Pass "" (empty string) to remove it. + + bio (``str``, *optional*): + The new bio, also known as "about". Max 70 characters. + Pass "" (empty string) to remove it. + + Returns: + ``bool``: True on success. + + Example: + .. code-block:: python + + # Update your first name only + await app.update_profile(first_name="Pyrogram") + + # Update first name and bio + await app.update_profile(first_name="Pyrogram", bio="https://docs.pyrogram.org/") + + # Remove the last name + await app.update_profile(last_name="") + """ + + return bool( + await self.invoke( + raw.functions.account.UpdateProfile( + first_name=first_name, + last_name=last_name, + about=bio + ) + ) + ) diff --git a/pyrogram/methods/utilities/__init__.py b/pyrogram/methods/utilities/__init__.py new file mode 100644 index 00000000000..80a5f7419d2 --- /dev/null +++ b/pyrogram/methods/utilities/__init__.py @@ -0,0 +1,39 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 .add_handler import AddHandler +from .export_session_string import ExportSessionString +from .remove_handler import RemoveHandler +from .restart import Restart +from .run import Run +from .start import Start +from .stop import Stop +from .stop_transmission import StopTransmission + + +class Utilities( + AddHandler, + ExportSessionString, + RemoveHandler, + Restart, + Run, + Start, + Stop, + StopTransmission +): + pass diff --git a/pyrogram/methods/utilities/add_handler.py b/pyrogram/methods/utilities/add_handler.py new file mode 100644 index 00000000000..136647d9812 --- /dev/null +++ b/pyrogram/methods/utilities/add_handler.py @@ -0,0 +1,67 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import pyrogram +from pyrogram.handlers import DisconnectHandler +from pyrogram.handlers.handler import Handler + + +class AddHandler: + def add_handler( + self: "pyrogram.Client", + handler: "Handler", + group: int = 0 + ): + """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). This mechanism is explained in greater details at + :doc:`More on Updates <../../topics/more-on-updates>`. + + Parameters: + handler (``Handler``): + The handler to be registered. + + group (``int``, *optional*): + The group identifier, defaults to 0. + + Returns: + ``tuple``: A tuple consisting of *(handler, group)*. + + Example: + .. code-block:: python + + from pyrogram import Client + from pyrogram.handlers import MessageHandler + + async def hello(client, message): + print(message) + + app = Client("my_account") + + app.add_handler(MessageHandler(hello)) + + app.run() + """ + if isinstance(handler, DisconnectHandler): + self.disconnect_handler = handler.callback + else: + self.dispatcher.add_handler(handler, group) + + return handler, group diff --git a/pyrogram/methods/utilities/compose.py b/pyrogram/methods/utilities/compose.py new file mode 100644 index 00000000000..e05773b8e19 --- /dev/null +++ b/pyrogram/methods/utilities/compose.py @@ -0,0 +1,78 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import asyncio +from typing import List + +import pyrogram +from .idle import idle + + +async def compose( + clients: List["pyrogram.Client"], + sequential: bool = False +): + """Run multiple clients at once. + + This method can be used to run multiple clients at once and can be found directly in the ``pyrogram`` package. + + If you want to run a single client, you can use Client's bound method :meth:`~pyrogram.Client.run`. + + Parameters: + clients (List of :obj:`~pyrogram.Client`): + A list of client objects to run. + + sequential (``bool``, *optional*): + Pass True to run clients sequentially. + Defaults to False (run clients concurrently) + + Example: + .. code-block:: python + + import asyncio + from pyrogram import Client, compose + + + async def main(): + apps = [ + Client("account1"), + Client("account2"), + Client("account3") + ] + + ... + + await compose(apps) + + + asyncio.run(main()) + + """ + if sequential: + for c in clients: + await c.start() + else: + await asyncio.gather(*[c.start() for c in clients]) + + await idle() + + if sequential: + for c in clients: + await c.stop() + else: + await asyncio.gather(*[c.stop() for c in clients]) diff --git a/pyrogram/methods/utilities/export_session_string.py b/pyrogram/methods/utilities/export_session_string.py new file mode 100644 index 00000000000..6529484d9b9 --- /dev/null +++ b/pyrogram/methods/utilities/export_session_string.py @@ -0,0 +1,40 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import pyrogram + + +class ExportSessionString: + async def export_session_string( + self: "pyrogram.Client" + ): + """Export the current authorized session as a serialized string. + + Session strings are useful for storing in-memory authorized sessions in a portable, serialized string. + More detailed information about session strings can be found at the dedicated page of + :doc:`Storage Engines <../../topics/storage-engines>`. + + Returns: + ``str``: The session serialized into a printable, url-safe string. + + Example: + .. code-block:: python + + s = await app.export_session_string() + """ + return await self.storage.export_session_string() diff --git a/pyrogram/methods/utilities/idle.py b/pyrogram/methods/utilities/idle.py new file mode 100644 index 00000000000..00db22a2e4a --- /dev/null +++ b/pyrogram/methods/utilities/idle.py @@ -0,0 +1,87 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import asyncio +import logging +import signal +from signal import signal as signal_fn, SIGINT, SIGTERM, SIGABRT + +log = logging.getLogger(__name__) + +# Signal number to name +signals = { + k: v for v, k in signal.__dict__.items() + if v.startswith("SIG") and not v.startswith("SIG_") +} + + +async def idle(): + """Block the main script execution until a signal is received. + + This function will run indefinitely in order to block the main script execution and prevent it from + exiting while having client(s) that are still running in the background. + + It is useful for event-driven application only, that are, applications which react upon incoming Telegram + updates through handlers, rather than executing a set of methods sequentially. + + Once a signal is received (e.g.: from CTRL+C) the function will terminate and your main script will continue. + Don't forget to call :meth:`~pyrogram.Client.stop` for each running client before the script ends. + + Example: + .. code-block:: python + + import asyncio + from pyrogram import Client, idle + + + async def main(): + apps = [ + Client("account1"), + Client("account2"), + Client("account3") + ] + + ... # Set up handlers + + for app in apps: + await app.start() + + await idle() + + for app in apps: + await app.stop() + + + asyncio.run(main()) + """ + task = None + + def signal_handler(signum, __): + logging.info(f"Stop signal received ({signals[signum]}). Exiting...") + task.cancel() + + for s in (SIGINT, SIGTERM, SIGABRT): + signal_fn(s, signal_handler) + + while True: + task = asyncio.create_task(asyncio.sleep(600)) + + try: + await task + except asyncio.CancelledError: + break diff --git a/pyrogram/methods/utilities/remove_handler.py b/pyrogram/methods/utilities/remove_handler.py new file mode 100644 index 00000000000..9f1c974e694 --- /dev/null +++ b/pyrogram/methods/utilities/remove_handler.py @@ -0,0 +1,63 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import pyrogram +from pyrogram.handlers import DisconnectHandler +from pyrogram.handlers.handler import Handler + + +class RemoveHandler: + def remove_handler( + self: "pyrogram.Client", + handler: "Handler", + group: int = 0 + ): + """Remove a previously-registered update handler. + + Make sure to provide the right group where the handler was added in. You can use the return value of the + :meth:`~pyrogram.Client.add_handler` method, a tuple of *(handler, group)*, and pass it directly. + + Parameters: + handler (``Handler``): + The handler to be removed. + + group (``int``, *optional*): + The group identifier, defaults to 0. + + Example: + .. code-block:: python + + from pyrogram import Client + from pyrogram.handlers import MessageHandler + + async def hello(client, message): + print(message) + + app = Client("my_account") + + handler = app.add_handler(MessageHandler(hello)) + + # Starred expression to unpack (handler, group) + app.remove_handler(*handler) + + app.run() + """ + if isinstance(handler, DisconnectHandler): + self.disconnect_handler = None + else: + self.dispatcher.remove_handler(handler, group) diff --git a/pyrogram/methods/utilities/restart.py b/pyrogram/methods/utilities/restart.py new file mode 100644 index 00000000000..44fd6745efb --- /dev/null +++ b/pyrogram/methods/utilities/restart.py @@ -0,0 +1,72 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import pyrogram + + +class Restart: + async def restart( + self: "pyrogram.Client", + block: bool = True + ): + """Restart the Client. + + This method will first call :meth:`~pyrogram.Client.stop` and then :meth:`~pyrogram.Client.start` in a row in + order to restart a client using a single method. + + Parameters: + block (``bool``, *optional*): + Blocks the code execution until the client has been restarted. It is useful with ``block=False`` in case + you want to restart the own client within an handler in order not to cause a deadlock. + Defaults to True. + + Returns: + :obj:`~pyrogram.Client`: The restarted client itself. + + Raises: + ConnectionError: In case you try to restart a stopped Client. + + Example: + .. code-block:: python + + from pyrogram import Client + + app = Client("my_account") + + + async def main(): + await app.start() + ... # Invoke API methods + await app.restart() + ... # Invoke other API methods + await app.stop() + + + app.run(main()) + """ + + async def do_it(): + await self.stop() + await self.start() + + if block: + await do_it() + else: + self.loop.create_task(do_it()) + + return self diff --git a/pyrogram/methods/utilities/run.py b/pyrogram/methods/utilities/run.py new file mode 100644 index 00000000000..ec1bece3203 --- /dev/null +++ b/pyrogram/methods/utilities/run.py @@ -0,0 +1,86 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import asyncio +import inspect + +import pyrogram +from pyrogram.methods.utilities.idle import idle + + +class Run: + def run( + self: "pyrogram.Client", + coroutine=None + ): + """Start the client, idle the main script and finally stop the client. + + When calling this method without any argument it acts as a convenience method that calls + :meth:`~pyrogram.Client.start`, :meth:`~pyrogram.idle` and :meth:`~pyrogram.Client.stop` in sequence. + It makes running a single client less verbose. + + In case a coroutine is passed as argument, runs the coroutine until it's completed and doesn't do any client + operation. This is almost the same as :py:obj:`asyncio.run` except for the fact that Pyrogram's ``run`` uses the + current event loop instead of a new one. + + If you want to run multiple clients at once, see :meth:`pyrogram.compose`. + + Parameters: + coroutine (``Coroutine``, *optional*): + Pass a coroutine to run it until it completes. + + Raises: + ConnectionError: In case you try to run an already started client. + + Example: + .. code-block:: python + + from pyrogram import Client + + app = Client("my_account") + ... # Set handlers up + app.run() + + .. code-block:: python + + from pyrogram import Client + + app = Client("my_account") + + + async def main(): + async with app: + print(await app.get_me()) + + + app.run(main()) + """ + loop = asyncio.get_event_loop() + run = loop.run_until_complete + + if coroutine is not None: + run(coroutine) + else: + if inspect.iscoroutinefunction(self.start): + run(self.start()) + run(idle()) + run(self.stop()) + else: + self.start() + run(idle()) + self.stop() diff --git a/pyrogram/methods/utilities/start.py b/pyrogram/methods/utilities/start.py new file mode 100644 index 00000000000..d8314da182d --- /dev/null +++ b/pyrogram/methods/utilities/start.py @@ -0,0 +1,76 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import logging + +import pyrogram +from pyrogram import raw + +log = logging.getLogger(__name__) + + +class Start: + async def start( + self: "pyrogram.Client" + ): + """Start the client. + + This method connects the client to Telegram and, in case of new sessions, automatically manages the + authorization process using an interactive prompt. + + Returns: + :obj:`~pyrogram.Client`: The started client itself. + + Raises: + ConnectionError: In case you try to start an already started client. + + Example: + .. code-block:: python + + from pyrogram import Client + + app = Client("my_account") + + + async def main(): + await app.start() + ... # Invoke API methods + await app.stop() + + + app.run(main()) + """ + is_authorized = await self.connect() + + try: + if not is_authorized: + await self.authorize() + + if not await self.storage.is_bot() and self.takeout: + self.takeout_id = (await self.invoke(raw.functions.account.InitTakeoutSession())).id + log.info("Takeout session %s initiated", self.takeout_id) + + await self.invoke(raw.functions.updates.GetState()) + except (Exception, KeyboardInterrupt): + await self.disconnect() + raise + else: + self.me = await self.get_me() + await self.initialize() + + return self diff --git a/pyrogram/methods/utilities/stop.py b/pyrogram/methods/utilities/stop.py new file mode 100644 index 00000000000..1b28add0463 --- /dev/null +++ b/pyrogram/methods/utilities/stop.py @@ -0,0 +1,69 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import pyrogram + + +class Stop: + async def stop( + self: "pyrogram.Client", + block: bool = True + ): + """Stop the Client. + + This method disconnects the client from Telegram and stops the underlying tasks. + + Parameters: + block (``bool``, *optional*): + Blocks the code execution until the client has been stopped. It is useful with ``block=False`` in case + you want to stop the own client *within* a handler in order not to cause a deadlock. + Defaults to True. + + Returns: + :obj:`~pyrogram.Client`: The stopped client itself. + + Raises: + ConnectionError: In case you try to stop an already stopped client. + + Example: + .. code-block:: python + + from pyrogram import Client + + app = Client("my_account") + + + async def main(): + await app.start() + ... # Invoke API methods + await app.stop() + + + app.run(main()) + """ + + async def do_it(): + await self.terminate() + await self.disconnect() + + if block: + await do_it() + else: + self.loop.create_task(do_it()) + + return self diff --git a/pyrogram/methods/utilities/stop_transmission.py b/pyrogram/methods/utilities/stop_transmission.py new file mode 100644 index 00000000000..da645695234 --- /dev/null +++ b/pyrogram/methods/utilities/stop_transmission.py @@ -0,0 +1,43 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import pyrogram + + +class StopTransmission: + def stop_transmission(self): + """Stop downloading or uploading a file. + + This method must be called inside a progress callback function in order to stop the transmission at the + desired time. The progress callback is called every time a file chunk is uploaded/downloaded. + + Example: + .. code-block:: python + + # Stop transmission once the upload progress reaches 50% + async def progress(current, total, client): + if (current * 100 / total) > 50: + client.stop_transmission() + + async with app: + await app.send_document( + "me", "file.zip", + progress=progress, + progress_args=(app,)) + """ + raise pyrogram.StopTransmission diff --git a/pyrogram/client/ext/mime.types b/pyrogram/mime_types.py similarity index 98% rename from pyrogram/client/ext/mime.types rename to pyrogram/mime_types.py index 4db43cae706..2f6c86aa8bb 100644 --- a/pyrogram/client/ext/mime.types +++ b/pyrogram/mime_types.py @@ -1,6 +1,27 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types. +# Extended with extra mime types specific to Telegram. +mime_types = """ # 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. +# and has been placed in the public domain for unlimited redistribution. # # The table below contains both registered and (common) unregistered types. # A type that has no unique extension can be ignored -- they are listed @@ -1852,4 +1873,9 @@ video/x-msvideo avi video/x-sgi-movie movie video/x-smv smv -x-conference/x-cooltalk ice \ No newline at end of file +x-conference/x-cooltalk ice + +# Telegram animated stickers +application/x-bad-tgsticker tgs +application/x-tgsticker tgs +""" diff --git a/pyrogram/parser/__init__.py b/pyrogram/parser/__init__.py new file mode 100644 index 00000000000..00c7acae767 --- /dev/null +++ b/pyrogram/parser/__init__.py @@ -0,0 +1,19 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 .parser import Parser diff --git a/pyrogram/parser/html.py b/pyrogram/parser/html.py new file mode 100644 index 00000000000..46722a8c403 --- /dev/null +++ b/pyrogram/parser/html.py @@ -0,0 +1,243 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import html +import logging +import re +from html.parser import HTMLParser +from typing import Optional + +import pyrogram +from pyrogram import raw +from pyrogram.enums import MessageEntityType +from pyrogram.errors import PeerIdInvalid +from . import utils + +log = logging.getLogger(__name__) + + +class Parser(HTMLParser): + MENTION_RE = re.compile(r"tg://user\?id=(\d+)") + + def __init__(self, client: "pyrogram.Client"): + super().__init__() + + self.client = client + + self.text = "" + self.entities = [] + self.tag_entities = {} + + def handle_starttag(self, tag, attrs): + attrs = dict(attrs) + extra = {} + + if tag in ["b", "strong"]: + entity = raw.types.MessageEntityBold + elif tag in ["i", "em"]: + entity = raw.types.MessageEntityItalic + elif tag == "u": + entity = raw.types.MessageEntityUnderline + elif tag in ["s", "del", "strike"]: + entity = raw.types.MessageEntityStrike + elif tag == "blockquote": + entity = raw.types.MessageEntityBlockquote + elif tag == "code": + entity = raw.types.MessageEntityCode + elif tag == "pre": + entity = raw.types.MessageEntityPre + extra["language"] = attrs.get("language", "") + elif tag == "spoiler": + entity = raw.types.MessageEntitySpoiler + elif tag == "a": + url = attrs.get("href", "") + + mention = Parser.MENTION_RE.match(url) + + if mention: + entity = raw.types.InputMessageEntityMentionName + extra["user_id"] = int(mention.group(1)) + else: + entity = raw.types.MessageEntityTextUrl + extra["url"] = url + elif tag == "emoji": + entity = raw.types.MessageEntityCustomEmoji + custom_emoji_id = int(attrs.get("id")) + extra["document_id"] = custom_emoji_id + else: + return + + if tag not in self.tag_entities: + self.tag_entities[tag] = [] + + self.tag_entities[tag].append(entity(offset=len(self.text), length=0, **extra)) + + def handle_data(self, data): + data = html.unescape(data) + + for entities in self.tag_entities.values(): + for entity in entities: + entity.length += len(data) + + self.text += data + + def handle_endtag(self, tag): + try: + self.entities.append(self.tag_entities[tag].pop()) + except (KeyError, IndexError): + line, offset = self.getpos() + offset += 1 + + log.debug("Unmatched closing tag at line %s:%s", tag, line, offset) + else: + if not self.tag_entities[tag]: + self.tag_entities.pop(tag) + + def error(self, message): + pass + + +class HTML: + def __init__(self, client: Optional["pyrogram.Client"]): + self.client = client + + async def parse(self, text: str): + # Strip whitespaces from the beginning and the end, but preserve closing tags + text = re.sub(r"^\s*(<[\w<>=\s\"]*>)\s*", r"\1", text) + text = re.sub(r"\s*(]*>)\s*$", r"\1", text) + + parser = Parser(self.client) + parser.feed(utils.add_surrogates(text)) + parser.close() + + if parser.tag_entities: + unclosed_tags = [] + + for tag, entities in parser.tag_entities.items(): + unclosed_tags.append(f"<{tag}> (x{len(entities)})") + + log.info("Unclosed tags: %s", ", ".join(unclosed_tags)) + + entities = [] + + for entity in parser.entities: + if isinstance(entity, raw.types.InputMessageEntityMentionName): + try: + if self.client is not None: + entity.user_id = await self.client.resolve_peer(entity.user_id) + except PeerIdInvalid: + continue + + entities.append(entity) + + # Remove zero-length entities + entities = list(filter(lambda x: x.length > 0, entities)) + + return { + "message": utils.remove_surrogates(parser.text), + "entities": sorted(entities, key=lambda e: e.offset) or None + } + + @staticmethod + def unparse(text: str, entities: list): + def parse_one(entity): + """ + Parses a single entity and returns (start_tag, start), (end_tag, end) + """ + entity_type = entity.type + start = entity.offset + end = start + entity.length + + if entity_type in ( + MessageEntityType.BOLD, + MessageEntityType.ITALIC, + MessageEntityType.UNDERLINE, + MessageEntityType.STRIKETHROUGH, + ): + name = entity_type.name[0].lower() + start_tag = f"<{name}>" + end_tag = f"" + elif entity_type == MessageEntityType.PRE: + name = entity_type.name.lower() + language = getattr(entity, "language", "") or "" + start_tag = f'<{name} language="{language}">' if language else f"<{name}>" + end_tag = f"" + elif entity_type in ( + MessageEntityType.CODE, + MessageEntityType.BLOCKQUOTE, + MessageEntityType.SPOILER, + ): + name = entity_type.name.lower() + start_tag = f"<{name}>" + end_tag = f"" + elif entity_type == MessageEntityType.TEXT_LINK: + url = entity.url + start_tag = f'' + end_tag = "" + elif entity_type == MessageEntityType.TEXT_MENTION: + user = entity.user + start_tag = f'' + end_tag = "" + elif entity_type == MessageEntityType.CUSTOM_EMOJI: + custom_emoji_id = entity.custom_emoji_id + start_tag = f'' + end_tag = "" + else: + return + + return (start_tag, start), (end_tag, end) + + def recursive(entity_i: int) -> int: + """ + Takes the index of the entity to start parsing from, returns the number of parsed entities inside it. + Uses entities_offsets as a stack, pushing (start_tag, start) first, then parsing nested entities, + and finally pushing (end_tag, end) to the stack. + No need to sort at the end. + """ + this = parse_one(entities[entity_i]) + if this is None: + return 1 + (start_tag, start), (end_tag, end) = this + entities_offsets.append((start_tag, start)) + internal_i = entity_i + 1 + # while the next entity is inside the current one, keep parsing + while internal_i < len(entities) and entities[internal_i].offset < end: + internal_i += recursive(internal_i) + entities_offsets.append((end_tag, end)) + return internal_i - entity_i + + text = utils.add_surrogates(text) + + entities_offsets = [] + + # probably useless because entities are already sorted by telegram + entities.sort(key=lambda e: (e.offset, -e.length)) + + # main loop for first-level entities + i = 0 + while i < len(entities): + i += recursive(i) + + if entities_offsets: + last_offset = entities_offsets[-1][1] + # no need to sort, but still add entities starting from the end + for entity, offset in reversed(entities_offsets): + text = text[:offset] + entity + html.escape(text[offset:last_offset]) + text[last_offset:] + last_offset = offset + + return utils.remove_surrogates(text) diff --git a/pyrogram/parser/markdown.py b/pyrogram/parser/markdown.py new file mode 100644 index 00000000000..6219d9583dd --- /dev/null +++ b/pyrogram/parser/markdown.py @@ -0,0 +1,173 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import html +import re +from typing import Optional + +import pyrogram +from pyrogram.enums import MessageEntityType +from . import utils +from .html import HTML + +BOLD_DELIM = "**" +ITALIC_DELIM = "__" +UNDERLINE_DELIM = "--" +STRIKE_DELIM = "~~" +SPOILER_DELIM = "||" +CODE_DELIM = "`" +PRE_DELIM = "```" + +MARKDOWN_RE = re.compile(r"({d})|\[(.+?)\]\((.+?)\)".format( + d="|".join( + ["".join(i) for i in [ + [rf"\{j}" for j in i] + for i in [ + PRE_DELIM, + CODE_DELIM, + STRIKE_DELIM, + UNDERLINE_DELIM, + ITALIC_DELIM, + BOLD_DELIM, + SPOILER_DELIM + ] + ]] + ))) + +OPENING_TAG = "<{}>" +CLOSING_TAG = "" +URL_MARKUP = '{}' +FIXED_WIDTH_DELIMS = [CODE_DELIM, PRE_DELIM] + + +class Markdown: + def __init__(self, client: Optional["pyrogram.Client"]): + self.html = HTML(client) + + async def parse(self, text: str, strict: bool = False): + if strict: + text = html.escape(text) + + delims = set() + is_fixed_width = False + + for i, match in enumerate(re.finditer(MARKDOWN_RE, text)): + start, _ = match.span() + delim, text_url, url = match.groups() + full = match.group(0) + + if delim in FIXED_WIDTH_DELIMS: + is_fixed_width = not is_fixed_width + + if is_fixed_width and delim not in FIXED_WIDTH_DELIMS: + continue + + if text_url: + text = utils.replace_once(text, full, URL_MARKUP.format(url, text_url), start) + continue + + if delim == BOLD_DELIM: + tag = "b" + elif delim == ITALIC_DELIM: + tag = "i" + elif delim == UNDERLINE_DELIM: + tag = "u" + elif delim == STRIKE_DELIM: + tag = "s" + elif delim == CODE_DELIM: + tag = "code" + elif delim == PRE_DELIM: + tag = "pre" + elif delim == SPOILER_DELIM: + tag = "spoiler" + else: + continue + + if delim not in delims: + delims.add(delim) + tag = OPENING_TAG.format(tag) + else: + delims.remove(delim) + tag = CLOSING_TAG.format(tag) + + if delim == PRE_DELIM and delim in delims: + delim_and_language = text[text.find(PRE_DELIM):].split("\n")[0] + language = delim_and_language[len(PRE_DELIM):] + text = utils.replace_once(text, delim_and_language, f'
', start)
+                continue
+
+            text = utils.replace_once(text, delim, tag, start)
+
+        return await self.html.parse(text)
+
+    @staticmethod
+    def unparse(text: str, entities: list):
+        text = utils.add_surrogates(text)
+
+        entities_offsets = []
+
+        for entity in entities:
+            entity_type = entity.type
+            start = entity.offset
+            end = start + entity.length
+
+            if entity_type == MessageEntityType.BOLD:
+                start_tag = end_tag = BOLD_DELIM
+            elif entity_type == MessageEntityType.ITALIC:
+                start_tag = end_tag = ITALIC_DELIM
+            elif entity_type == MessageEntityType.UNDERLINE:
+                start_tag = end_tag = UNDERLINE_DELIM
+            elif entity_type == MessageEntityType.STRIKETHROUGH:
+                start_tag = end_tag = STRIKE_DELIM
+            elif entity_type == MessageEntityType.CODE:
+                start_tag = end_tag = CODE_DELIM
+            elif entity_type == MessageEntityType.PRE:
+                language = getattr(entity, "language", "") or ""
+                start_tag = f"{PRE_DELIM}{language}\n"
+                end_tag = f"\n{PRE_DELIM}"
+            elif entity_type == MessageEntityType.BLOCKQUOTE:
+                start_tag = end_tag = PRE_DELIM
+            elif entity_type == MessageEntityType.SPOILER:
+                start_tag = end_tag = SPOILER_DELIM
+            elif entity_type == MessageEntityType.TEXT_LINK:
+                url = entity.url
+                start_tag = "["
+                end_tag = f"]({url})"
+            elif entity_type == MessageEntityType.TEXT_MENTION:
+                user = entity.user
+                start_tag = "["
+                end_tag = f"](tg://user?id={user.id})"
+            else:
+                continue
+
+            entities_offsets.append((start_tag, start,))
+            entities_offsets.append((end_tag, end,))
+
+        entities_offsets = map(
+            lambda x: x[1],
+            sorted(
+                enumerate(entities_offsets),
+                key=lambda x: (x[1][1], x[0]),
+                reverse=True
+            )
+        )
+
+        for entity, offset in entities_offsets:
+            text = text[:offset] + entity + text[offset:]
+
+        return utils.remove_surrogates(text)
diff --git a/pyrogram/parser/parser.py b/pyrogram/parser/parser.py
new file mode 100644
index 00000000000..0ce2b2375c2
--- /dev/null
+++ b/pyrogram/parser/parser.py
@@ -0,0 +1,61 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import Optional
+
+import pyrogram
+from pyrogram import enums
+from .html import HTML
+from .markdown import Markdown
+
+
+class Parser:
+    def __init__(self, client: Optional["pyrogram.Client"]):
+        self.client = client
+        self.html = HTML(client)
+        self.markdown = Markdown(client)
+
+    async def parse(self, text: str, mode: Optional[enums.ParseMode] = None):
+        text = str(text if text else "").strip()
+
+        if mode is None:
+            if self.client:
+                mode = self.client.parse_mode
+            else:
+                mode = enums.ParseMode.DEFAULT
+
+        if mode == enums.ParseMode.DEFAULT:
+            return await self.markdown.parse(text)
+
+        if mode == enums.ParseMode.MARKDOWN:
+            return await self.markdown.parse(text, True)
+
+        if mode == enums.ParseMode.HTML:
+            return await self.html.parse(text)
+
+        if mode == enums.ParseMode.DISABLED:
+            return {"message": text, "entities": None}
+
+        raise ValueError(f'Invalid parse mode "{mode}"')
+
+    @staticmethod
+    def unparse(text: str, entities: list, is_html: bool):
+        if is_html:
+            return HTML.unparse(text, entities)
+        else:
+            return Markdown.unparse(text, entities)
diff --git a/pyrogram/parser/utils.py b/pyrogram/parser/utils.py
new file mode 100644
index 00000000000..0d604028945
--- /dev/null
+++ b/pyrogram/parser/utils.py
@@ -0,0 +1,41 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import re
+from struct import unpack
+
+# SMP = Supplementary Multilingual Plane: https://en.wikipedia.org/wiki/Plane_(Unicode)#Overview
+SMP_RE = re.compile(r"[\U00010000-\U0010FFFF]")
+
+
+def add_surrogates(text):
+    # Replace each SMP code point with a surrogate pair
+    return SMP_RE.sub(
+        lambda match:  # Split SMP in two surrogates
+        "".join(chr(i) for i in unpack("
+#
+#  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 importlib import import_module
+
+from . import types, functions, base, core
+from .all import objects
+
+for k, v in objects.items():
+    path, name = v.rsplit(".", 1)
+    objects[k] = getattr(import_module(path), name)
diff --git a/pyrogram/raw/core/__init__.py b/pyrogram/raw/core/__init__.py
new file mode 100644
index 00000000000..95c8123104a
--- /dev/null
+++ b/pyrogram/raw/core/__init__.py
@@ -0,0 +1,31 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .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 .primitives.bool import Bool, BoolFalse, BoolTrue
+from .primitives.bytes import Bytes
+from .primitives.double import Double
+from .primitives.int import Int, Long, Int128, Int256
+from .primitives.string import String
+from .primitives.vector import Vector
+from .tl_object import TLObject
diff --git a/pyrogram/raw/core/future_salt.py b/pyrogram/raw/core/future_salt.py
new file mode 100644
index 00000000000..ecef5e2ff30
--- /dev/null
+++ b/pyrogram/raw/core/future_salt.py
@@ -0,0 +1,53 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 io import BytesIO
+from typing import Any
+
+from .primitives.int import Int, Long
+from .tl_object import TLObject
+
+
+class FutureSalt(TLObject):
+    ID = 0x0949D9DC
+
+    __slots__ = ["valid_since", "valid_until", "salt"]
+
+    QUALNAME = "FutureSalt"
+
+    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(data: BytesIO, *args: Any) -> "FutureSalt":
+        valid_since = Int.read(data)
+        valid_until = Int.read(data)
+        salt = Long.read(data)
+
+        return FutureSalt(valid_since, valid_until, salt)
+
+    def write(self, *args: Any) -> bytes:
+        b = BytesIO()
+
+        b.write(Int(self.valid_since))
+        b.write(Int(self.valid_until))
+        b.write(Long(self.salt))
+
+        return b.getvalue()
diff --git a/pyrogram/raw/core/future_salts.py b/pyrogram/raw/core/future_salts.py
new file mode 100644
index 00000000000..88216387f2a
--- /dev/null
+++ b/pyrogram/raw/core/future_salts.py
@@ -0,0 +1,63 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 io import BytesIO
+from typing import Any, List
+
+from .future_salt import FutureSalt
+from .primitives.int import Int, Long
+from .tl_object import TLObject
+
+
+class FutureSalts(TLObject):
+    ID = 0xAE500895
+
+    __slots__ = ["req_msg_id", "now", "salts"]
+
+    QUALNAME = "FutureSalts"
+
+    def __init__(self, req_msg_id: int, now: int, salts: List[FutureSalt]):
+        self.req_msg_id = req_msg_id
+        self.now = now
+        self.salts = salts
+
+    @staticmethod
+    def read(data: BytesIO, *args: Any) -> "FutureSalts":
+        req_msg_id = Long.read(data)
+        now = Int.read(data)
+
+        count = Int.read(data)
+        salts = [FutureSalt.read(data) for _ in range(count)]
+
+        return FutureSalts(req_msg_id, now, salts)
+
+    def write(self, *args: Any) -> bytes:
+        b = BytesIO()
+
+        b.write(Int(self.ID, False))
+
+        b.write(Long(self.req_msg_id))
+        b.write(Int(self.now))
+
+        count = len(self.salts)
+        b.write(Int(count))
+
+        for salt in self.salts:
+            b.write(salt.write())
+
+        return b.getvalue()
diff --git a/pyrogram/raw/core/gzip_packed.py b/pyrogram/raw/core/gzip_packed.py
new file mode 100644
index 00000000000..685594d87a6
--- /dev/null
+++ b/pyrogram/raw/core/gzip_packed.py
@@ -0,0 +1,62 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 gzip import compress, decompress
+from io import BytesIO
+from typing import cast, Any
+
+from .primitives.bytes import Bytes
+from .primitives.int import Int
+from .tl_object import TLObject
+
+
+class GzipPacked(TLObject):
+    ID = 0x3072CFA1
+
+    __slots__ = ["packed_data"]
+
+    QUALNAME = "GzipPacked"
+
+    def __init__(self, packed_data: TLObject):
+        self.packed_data = packed_data
+
+    @staticmethod
+    def read(data: BytesIO, *args: Any) -> "GzipPacked":
+        # Return the Object itself instead of a GzipPacked wrapping it
+        return cast(GzipPacked, TLObject.read(
+            BytesIO(
+                decompress(
+                    Bytes.read(data)
+                )
+            )
+        ))
+
+    def write(self, *args: Any) -> bytes:
+        b = BytesIO()
+
+        b.write(Int(self.ID, False))
+
+        b.write(
+            Bytes(
+                compress(
+                    self.packed_data.write()
+                )
+            )
+        )
+
+        return b.getvalue()
diff --git a/pyrogram/raw/core/list.py b/pyrogram/raw/core/list.py
new file mode 100644
index 00000000000..a7bbc16b2e5
--- /dev/null
+++ b/pyrogram/raw/core/list.py
@@ -0,0 +1,26 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import List as TList, Any
+
+from .tl_object import TLObject
+
+
+class List(TList[Any], TLObject):
+    def __repr__(self) -> str:
+        return f"pyrogram.raw.core.List([{','.join(TLObject.__repr__(i) for i in self)}])"
diff --git a/pyrogram/raw/core/message.py b/pyrogram/raw/core/message.py
new file mode 100644
index 00000000000..1357cf8690e
--- /dev/null
+++ b/pyrogram/raw/core/message.py
@@ -0,0 +1,56 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 io import BytesIO
+from typing import Any
+
+from .primitives.int import Int, Long
+from .tl_object import TLObject
+
+
+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: TLObject, msg_id: int, seq_no: int, length: int):
+        self.msg_id = msg_id
+        self.seq_no = seq_no
+        self.length = length
+        self.body = body
+
+    @staticmethod
+    def read(data: BytesIO, *args: Any) -> "Message":
+        msg_id = Long.read(data)
+        seq_no = Int.read(data)
+        length = Int.read(data)
+        body = data.read(length)
+
+        return Message(TLObject.read(BytesIO(body)), msg_id, seq_no, length)
+
+    def write(self, *args: Any) -> bytes:
+        b = BytesIO()
+
+        b.write(Long(self.msg_id))
+        b.write(Int(self.seq_no))
+        b.write(Int(self.length))
+        b.write(self.body.write())
+
+        return b.getvalue()
diff --git a/pyrogram/raw/core/msg_container.py b/pyrogram/raw/core/msg_container.py
new file mode 100644
index 00000000000..454a150741d
--- /dev/null
+++ b/pyrogram/raw/core/msg_container.py
@@ -0,0 +1,53 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 io import BytesIO
+from typing import List, Any
+
+from .message import Message
+from .primitives.int import Int
+from .tl_object import TLObject
+
+
+class MsgContainer(TLObject):
+    ID = 0x73F1F8DC
+
+    __slots__ = ["messages"]
+
+    QUALNAME = "MsgContainer"
+
+    def __init__(self, messages: List[Message]):
+        self.messages = messages
+
+    @staticmethod
+    def read(data: BytesIO, *args: Any) -> "MsgContainer":
+        count = Int.read(data)
+        return MsgContainer([Message.read(data) for _ in range(count)])
+
+    def write(self, *args: Any) -> bytes:
+        b = BytesIO()
+
+        b.write(Int(self.ID, False))
+
+        count = len(self.messages)
+        b.write(Int(count))
+
+        for message in self.messages:
+            b.write(message.write())
+
+        return b.getvalue()
diff --git a/pyrogram/raw/core/primitives/__init__.py b/pyrogram/raw/core/primitives/__init__.py
new file mode 100644
index 00000000000..88f2fa5363c
--- /dev/null
+++ b/pyrogram/raw/core/primitives/__init__.py
@@ -0,0 +1,24 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .bool import Bool, BoolFalse, BoolTrue
+from .bytes import Bytes
+from .double import Double
+from .int import Int, Long, Int128, Int256
+from .string import String
+from .vector import Vector
diff --git a/pyrogram/raw/core/primitives/bool.py b/pyrogram/raw/core/primitives/bool.py
new file mode 100644
index 00000000000..02cc12d131f
--- /dev/null
+++ b/pyrogram/raw/core/primitives/bool.py
@@ -0,0 +1,48 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 io import BytesIO
+from typing import Any
+
+from ..tl_object import TLObject
+
+
+class BoolFalse(bytes, TLObject):
+    ID = 0xBC799737
+    value = False
+
+    @classmethod
+    def read(cls, *args: Any) -> bool:
+        return cls.value
+
+    def __new__(cls) -> bytes:  # type: ignore
+        return cls.ID.to_bytes(4, "little")
+
+
+class BoolTrue(BoolFalse):
+    ID = 0x997275B5
+    value = True
+
+
+class Bool(bytes, TLObject):
+    @classmethod
+    def read(cls, data: BytesIO, *args: Any) -> bool:
+        return int.from_bytes(data.read(4), "little") == BoolTrue.ID
+
+    def __new__(cls, value: bool) -> bytes:  # type: ignore
+        return BoolTrue() if value else BoolFalse()
diff --git a/pyrogram/raw/core/primitives/bytes.py b/pyrogram/raw/core/primitives/bytes.py
new file mode 100644
index 00000000000..eace1feb99c
--- /dev/null
+++ b/pyrogram/raw/core/primitives/bytes.py
@@ -0,0 +1,55 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 io import BytesIO
+from typing import Any
+
+from ..tl_object import TLObject
+
+
+class Bytes(bytes, TLObject):
+    @classmethod
+    def read(cls, data: BytesIO, *args: Any) -> bytes:
+        length = int.from_bytes(data.read(1), "little")
+
+        if length <= 253:
+            x = data.read(length)
+            data.read(-(length + 1) % 4)
+        else:
+            length = int.from_bytes(data.read(3), "little")
+            x = data.read(length)
+            data.read(-length % 4)
+
+        return x
+
+    def __new__(cls, value: bytes) -> bytes:  # type: ignore
+        length = len(value)
+
+        if length <= 253:
+            return (
+                bytes([length])
+                + value
+                + bytes(-(length + 1) % 4)
+            )
+        else:
+            return (
+                bytes([254])
+                + length.to_bytes(3, "little")
+                + value
+                + bytes(-length % 4)
+            )
diff --git a/pyrogram/raw/core/primitives/double.py b/pyrogram/raw/core/primitives/double.py
new file mode 100644
index 00000000000..bb7878bf1e8
--- /dev/null
+++ b/pyrogram/raw/core/primitives/double.py
@@ -0,0 +1,32 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 io import BytesIO
+from struct import unpack, pack
+from typing import cast, Any
+
+from ..tl_object import TLObject
+
+
+class Double(bytes, TLObject):
+    @classmethod
+    def read(cls, data: BytesIO, *args: Any) -> float:
+        return cast(float, unpack("d", data.read(8))[0])
+
+    def __new__(cls, value: float) -> bytes:  # type: ignore
+        return pack("d", value)
diff --git a/pyrogram/raw/core/primitives/int.py b/pyrogram/raw/core/primitives/int.py
new file mode 100644
index 00000000000..5d5ec177d12
--- /dev/null
+++ b/pyrogram/raw/core/primitives/int.py
@@ -0,0 +1,45 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 io import BytesIO
+from typing import Any
+
+from ..tl_object import TLObject
+
+
+class Int(bytes, TLObject):
+    SIZE = 4
+
+    @classmethod
+    def read(cls, data: BytesIO, signed: bool = True, *args: Any) -> int:
+        return int.from_bytes(data.read(cls.SIZE), "little", signed=signed)
+
+    def __new__(cls, value: int, signed: bool = True) -> bytes:  # type: ignore
+        return value.to_bytes(cls.SIZE, "little", signed=signed)
+
+
+class Long(Int):
+    SIZE = 8
+
+
+class Int128(Int):
+    SIZE = 16
+
+
+class Int256(Int):
+    SIZE = 32
diff --git a/pyrogram/raw/core/primitives/string.py b/pyrogram/raw/core/primitives/string.py
new file mode 100644
index 00000000000..66f992717b2
--- /dev/null
+++ b/pyrogram/raw/core/primitives/string.py
@@ -0,0 +1,31 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 io import BytesIO
+from typing import cast
+
+from .bytes import Bytes
+
+
+class String(Bytes):
+    @classmethod
+    def read(cls, data: BytesIO, *args) -> str:  # type: ignore
+        return cast(bytes, super(String, String).read(data)).decode(errors="replace")
+
+    def __new__(cls, value: str) -> bytes:  # type: ignore
+        return super().__new__(cls, value.encode())
diff --git a/pyrogram/raw/core/primitives/vector.py b/pyrogram/raw/core/primitives/vector.py
new file mode 100644
index 00000000000..c6c6e8e5d45
--- /dev/null
+++ b/pyrogram/raw/core/primitives/vector.py
@@ -0,0 +1,59 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 io import BytesIO
+from typing import cast, Union, Any
+
+from .int import Int, Long
+from ..list import List
+from ..tl_object import TLObject
+
+
+class Vector(bytes, 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_bare(b: BytesIO, size: int) -> Union[int, Any]:
+        if size == 4:
+            return Int.read(b)
+
+        if size == 8:
+            return Long.read(b)
+
+        return TLObject.read(b)
+
+    @classmethod
+    def read(cls, data: BytesIO, t: Any = None, *args: Any) -> List:
+        count = Int.read(data)
+        left = len(data.read())
+        size = (left / count) if count else 0
+        data.seek(-left, 1)
+
+        return List(
+            t.read(data) if t
+            else Vector.read_bare(data, size)
+            for _ in range(count)
+        )
+
+    def __new__(cls, value: list, t: Any = None) -> bytes:  # type: ignore
+        return b"".join(
+            [Int(cls.ID, False), Int(len(value))]
+            + [cast(bytes, t(i)) if t else i.write() for i in value]
+        )
diff --git a/pyrogram/raw/core/tl_object.py b/pyrogram/raw/core/tl_object.py
new file mode 100644
index 00000000000..ff67566ea81
--- /dev/null
+++ b/pyrogram/raw/core/tl_object.py
@@ -0,0 +1,82 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 io import BytesIO
+from json import dumps
+from typing import cast, List, Any, Union, Dict
+
+from ..all import objects
+
+
+class TLObject:
+    __slots__: List[str] = []
+
+    QUALNAME = "Base"
+
+    @classmethod
+    def read(cls, b: BytesIO, *args: Any) -> Any:
+        return cast(TLObject, objects[int.from_bytes(b.read(4), "little")]).read(b, *args)
+
+    def write(self, *args: Any) -> bytes:
+        pass
+
+    @staticmethod
+    def default(obj: "TLObject") -> Union[str, Dict[str, str]]:
+        if isinstance(obj, bytes):
+            return repr(obj)
+
+        return {
+            "_": 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:
+        if not hasattr(self, "QUALNAME"):
+            return repr(self)
+
+        return "pyrogram.raw.{}({})".format(
+            self.QUALNAME,
+            ", ".join(
+                f"{attr}={repr(getattr(self, attr))}"
+                for attr in self.__slots__
+                if getattr(self, attr) is not None
+            )
+        )
+
+    def __eq__(self, other: Any) -> 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 __call__(self, *args: Any, **kwargs: Any) -> Any:
+        pass
diff --git a/pyrogram/session/__init__.py b/pyrogram/session/__init__.py
index 3a3107c04d7..b414049b2b9 100644
--- a/pyrogram/session/__init__.py
+++ b/pyrogram/session/__init__.py
@@ -1,20 +1,20 @@
-# Pyrogram - Telegram MTProto API Client Library for Python
-# Copyright (C) 2017-2019 Dan Tès 
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
 #
-# This file is part of Pyrogram.
+#  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 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.
+#  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 .
+#  You should have received a copy of the GNU Lesser General Public License
+#  along with Pyrogram.  If not, see .
 
 from .auth import Auth
 from .session import Session
diff --git a/pyrogram/session/auth.py b/pyrogram/session/auth.py
index 89e5b61f474..c5d9cd9a501 100644
--- a/pyrogram/session/auth.py
+++ b/pyrogram/session/auth.py
@@ -1,31 +1,34 @@
-# Pyrogram - Telegram MTProto API Client Library for Python
-# Copyright (C) 2017-2019 Dan Tès 
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
 #
-# This file is part of Pyrogram.
+#  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 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.
+#  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 .
+#  You should have received a copy of the GNU Lesser General Public License
+#  along with Pyrogram.  If not, see .
 
+import asyncio
 import logging
 import time
 from hashlib import sha1
 from io import BytesIO
 from os import urandom
 
-from pyrogram.api import functions, types
-from pyrogram.api.core import Object, Long, Int
+import pyrogram
+from pyrogram import raw
 from pyrogram.connection import Connection
-from pyrogram.crypto import AES, RSA, Prime
+from pyrogram.crypto import aes, rsa, prime
+from pyrogram.errors import SecurityCheckMismatch
+from pyrogram.raw.core import TLObject, Long, Int
 from .internals import MsgId
 
 log = logging.getLogger(__name__)
@@ -34,16 +37,16 @@
 class Auth:
     MAX_RETRIES = 5
 
-    def __init__(self, dc_id: int, test_mode: bool, ipv6: bool, proxy: dict):
+    def __init__(self, client: "pyrogram.Client", dc_id: int, test_mode: bool):
         self.dc_id = dc_id
         self.test_mode = test_mode
-        self.ipv6 = ipv6
-        self.proxy = proxy
+        self.ipv6 = client.ipv6
+        self.proxy = client.proxy
 
         self.connection = None
 
     @staticmethod
-    def pack(data: Object) -> bytes:
+    def pack(data: TLObject) -> bytes:
         return (
             bytes(8)
             + Long(MsgId())
@@ -54,16 +57,16 @@ def pack(data: Object) -> bytes:
     @staticmethod
     def unpack(b: BytesIO):
         b.seek(20)  # Skip auth_key_id (8), message_id (8) and message_length (4)
-        return Object.read(b)
+        return TLObject.read(b)
 
-    def send(self, data: Object):
+    async def invoke(self, data: TLObject):
         data = self.pack(data)
-        self.connection.send(data)
-        response = BytesIO(self.connection.recv())
+        await self.connection.send(data)
+        response = BytesIO(await self.connection.recv())
 
         return self.unpack(response)
 
-    def create(self):
+    async def create(self):
         """
         https://core.telegram.org/mtproto/auth_key
         https://core.telegram.org/mtproto/samples-auth_key
@@ -76,40 +79,40 @@ def create(self):
             self.connection = Connection(self.dc_id, self.test_mode, self.ipv6, self.proxy)
 
             try:
-                log.info("Start creating a new auth key on DC{}".format(self.dc_id))
+                log.info("Start creating a new auth key on DC%s", self.dc_id)
 
-                self.connection.connect()
+                await self.connection.connect()
 
                 # Step 1; Step 2
                 nonce = int.from_bytes(urandom(16), "little", signed=True)
-                log.debug("Send req_pq: {}".format(nonce))
-                res_pq = self.send(functions.ReqPqMulti(nonce=nonce))
-                log.debug("Got ResPq: {}".format(res_pq.server_nonce))
-                log.debug("Server public key fingerprints: {}".format(res_pq.server_public_key_fingerprints))
+                log.debug("Send req_pq: %s", nonce)
+                res_pq = await self.invoke(raw.functions.ReqPqMulti(nonce=nonce))
+                log.debug("Got ResPq: %s", res_pq.server_nonce)
+                log.debug("Server public key fingerprints: %s", res_pq.server_public_key_fingerprints)
 
                 for i in res_pq.server_public_key_fingerprints:
-                    if i in RSA.server_public_keys:
-                        log.debug("Using fingerprint: {}".format(i))
+                    if i in rsa.server_public_keys:
+                        log.debug("Using fingerprint: %s", i)
                         public_key_fingerprint = i
                         break
                     else:
-                        log.debug("Fingerprint unknown: {}".format(i))
+                        log.debug("Fingerprint unknown: %s", i)
                 else:
                     raise Exception("Public key not found")
 
                 # Step 3
                 pq = int.from_bytes(res_pq.pq, "big")
-                log.debug("Start PQ factorization: {}".format(pq))
+                log.debug("Start PQ factorization: %s", pq)
                 start = time.time()
-                g = Prime.decompose(pq)
+                g = prime.decompose(pq)
                 p, q = sorted((g, pq // g))  # p < q
-                log.debug("Done PQ factorization ({}s): {} {}".format(round(time.time() - start, 3), p, q))
+                log.debug("Done PQ factorization (%ss): %s %s", round(time.time() - start, 3), p, q)
 
                 # Step 4
                 server_nonce = res_pq.server_nonce
                 new_nonce = int.from_bytes(urandom(32), "little", signed=True)
 
-                data = types.PQInnerData(
+                data = raw.types.PQInnerData(
                     pq=res_pq.pq,
                     p=p.to_bytes(4, "big"),
                     q=q.to_bytes(4, "big"),
@@ -121,14 +124,14 @@ def create(self):
                 sha = sha1(data).digest()
                 padding = urandom(- (len(data) + len(sha)) % 255)
                 data_with_hash = sha + data + padding
-                encrypted_data = RSA.encrypt(data_with_hash, public_key_fingerprint)
+                encrypted_data = rsa.encrypt(data_with_hash, public_key_fingerprint)
 
                 log.debug("Done encrypt data with RSA")
 
                 # Step 5. TODO: Handle "server_DH_params_fail". Code assumes response is ok
                 log.debug("Send req_DH_params")
-                server_dh_params = self.send(
-                    functions.ReqDHParams(
+                server_dh_params = await self.invoke(
+                    raw.functions.ReqDHParams(
                         nonce=nonce,
                         server_nonce=server_nonce,
                         p=p.to_bytes(4, "big"),
@@ -155,17 +158,17 @@ def create(self):
 
                 server_nonce = int.from_bytes(server_nonce, "little", signed=True)
 
-                answer_with_hash = AES.ige256_decrypt(encrypted_answer, tmp_aes_key, tmp_aes_iv)
+                answer_with_hash = aes.ige256_decrypt(encrypted_answer, tmp_aes_key, tmp_aes_iv)
                 answer = answer_with_hash[20:]
 
-                server_dh_inner_data = Object.read(BytesIO(answer))
+                server_dh_inner_data = TLObject.read(BytesIO(answer))
 
                 log.debug("Done decrypting answer")
 
                 dh_prime = int.from_bytes(server_dh_inner_data.dh_prime, "big")
                 delta_time = server_dh_inner_data.server_time - time.time()
 
-                log.debug("Delta time: {}".format(round(delta_time, 3)))
+                log.debug("Delta time: %s", round(delta_time, 3))
 
                 # Step 6
                 g = server_dh_inner_data.g
@@ -174,7 +177,7 @@ def create(self):
 
                 retry_id = 0
 
-                data = types.ClientDHInnerData(
+                data = raw.types.ClientDHInnerData(
                     nonce=nonce,
                     server_nonce=server_nonce,
                     retry_id=retry_id,
@@ -184,11 +187,11 @@ def create(self):
                 sha = sha1(data).digest()
                 padding = urandom(- (len(data) + len(sha)) % 16)
                 data_with_hash = sha + data + padding
-                encrypted_data = AES.ige256_encrypt(data_with_hash, tmp_aes_key, tmp_aes_iv)
+                encrypted_data = aes.ige256_encrypt(data_with_hash, tmp_aes_key, tmp_aes_iv)
 
                 log.debug("Send set_client_DH_params")
-                set_client_dh_params_answer = self.send(
-                    functions.SetClientDHParams(
+                set_client_dh_params_answer = await self.invoke(
+                    raw.functions.SetClientDHParams(
                         nonce=nonce,
                         server_nonce=server_nonce,
                         encrypted_data=encrypted_data
@@ -208,55 +211,71 @@ def create(self):
                 # Security checks
                 #######################
 
-                assert dh_prime == Prime.CURRENT_DH_PRIME
+                SecurityCheckMismatch.check(dh_prime == prime.CURRENT_DH_PRIME, "dh_prime == prime.CURRENT_DH_PRIME")
                 log.debug("DH parameters check: OK")
 
                 # https://core.telegram.org/mtproto/security_guidelines#g-a-and-g-b-validation
                 g_b = int.from_bytes(g_b, "big")
-                assert 1 < g < dh_prime - 1
-                assert 1 < g_a < dh_prime - 1
-                assert 1 < g_b < dh_prime - 1
-                assert 2 ** (2048 - 64) < g_a < dh_prime - 2 ** (2048 - 64)
-                assert 2 ** (2048 - 64) < g_b < dh_prime - 2 ** (2048 - 64)
+                SecurityCheckMismatch.check(1 < g < dh_prime - 1, "1 < g < dh_prime - 1")
+                SecurityCheckMismatch.check(1 < g_a < dh_prime - 1, "1 < g_a < dh_prime - 1")
+                SecurityCheckMismatch.check(1 < g_b < dh_prime - 1, "1 < g_b < dh_prime - 1")
+                SecurityCheckMismatch.check(
+                    2 ** (2048 - 64) < g_a < dh_prime - 2 ** (2048 - 64),
+                    "2 ** (2048 - 64) < g_a < dh_prime - 2 ** (2048 - 64)"
+                )
+                SecurityCheckMismatch.check(
+                    2 ** (2048 - 64) < g_b < dh_prime - 2 ** (2048 - 64),
+                    "2 ** (2048 - 64) < g_b < dh_prime - 2 ** (2048 - 64)"
+                )
                 log.debug("g_a and g_b validation: OK")
 
                 # https://core.telegram.org/mtproto/security_guidelines#checking-sha1-hash-values
                 answer = server_dh_inner_data.write()  # Call .write() to remove padding
-                assert answer_with_hash[:20] == sha1(answer).digest()
+                SecurityCheckMismatch.check(
+                    answer_with_hash[:20] == sha1(answer).digest(),
+                    "answer_with_hash[:20] == sha1(answer).digest()"
+                )
                 log.debug("SHA1 hash values check: OK")
 
                 # https://core.telegram.org/mtproto/security_guidelines#checking-nonce-server-nonce-and-new-nonce-fields
                 # 1st message
-                assert nonce == res_pq.nonce
+                SecurityCheckMismatch.check(nonce == res_pq.nonce, "nonce == res_pq.nonce")
                 # 2nd message
                 server_nonce = int.from_bytes(server_nonce, "little", signed=True)
-                assert nonce == server_dh_params.nonce
-                assert server_nonce == server_dh_params.server_nonce
+                SecurityCheckMismatch.check(nonce == server_dh_params.nonce, "nonce == server_dh_params.nonce")
+                SecurityCheckMismatch.check(
+                    server_nonce == server_dh_params.server_nonce,
+                    "server_nonce == server_dh_params.server_nonce"
+                )
                 # 3rd message
-                assert nonce == set_client_dh_params_answer.nonce
-                assert server_nonce == set_client_dh_params_answer.server_nonce
+                SecurityCheckMismatch.check(
+                    nonce == set_client_dh_params_answer.nonce,
+                    "nonce == set_client_dh_params_answer.nonce"
+                )
+                SecurityCheckMismatch.check(
+                    server_nonce == set_client_dh_params_answer.server_nonce,
+                    "server_nonce == set_client_dh_params_answer.server_nonce"
+                )
                 server_nonce = server_nonce.to_bytes(16, "little", signed=True)
                 log.debug("Nonce fields check: OK")
 
                 # Step 9
-                server_salt = AES.xor(new_nonce[:8], server_nonce[:8])
+                server_salt = aes.xor(new_nonce[:8], server_nonce[:8])
 
-                log.debug("Server salt: {}".format(int.from_bytes(server_salt, "little")))
+                log.debug("Server salt: %s", int.from_bytes(server_salt, "little"))
 
-                log.info(
-                    "Done auth key exchange: {}".format(
-                        set_client_dh_params_answer.__class__.__name__
-                    )
-                )
+                log.info("Done auth key exchange: %s", set_client_dh_params_answer.__class__.__name__)
             except Exception as e:
+                log.info("Retrying due to %s: %s", type(e).__name__, e)
+
                 if retries_left:
                     retries_left -= 1
                 else:
                     raise e
 
-                time.sleep(1)
+                await asyncio.sleep(1)
                 continue
             else:
                 return auth_key
             finally:
-                self.connection.close()
+                await self.connection.close()
diff --git a/pyrogram/session/internals/__init__.py b/pyrogram/session/internals/__init__.py
index 272855a7691..31dcc80f124 100644
--- a/pyrogram/session/internals/__init__.py
+++ b/pyrogram/session/internals/__init__.py
@@ -1,20 +1,20 @@
-# Pyrogram - Telegram MTProto API Client Library for Python
-# Copyright (C) 2017-2019 Dan Tès 
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
 #
-# This file is part of Pyrogram.
+#  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 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.
+#  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 .
+#  You should have received a copy of the GNU Lesser General Public License
+#  along with Pyrogram.  If not, see .
 
 from .data_center import DataCenter
 from .msg_factory import MsgFactory
diff --git a/pyrogram/session/internals/data_center.py b/pyrogram/session/internals/data_center.py
index fd51932ab7b..d3146263526 100644
--- a/pyrogram/session/internals/data_center.py
+++ b/pyrogram/session/internals/data_center.py
@@ -1,20 +1,22 @@
-# Pyrogram - Telegram MTProto API Client Library for Python
-# Copyright (C) 2017-2019 Dan Tès 
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
 #
-# This file is part of Pyrogram.
+#  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 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.
+#  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 .
+#  You should have received a copy of the GNU Lesser General Public License
+#  along with Pyrogram.  If not, see .
+
+from typing import Tuple
 
 
 class DataCenter:
@@ -22,23 +24,26 @@ class DataCenter:
         1: "149.154.175.10",
         2: "149.154.167.40",
         3: "149.154.175.117",
-        121: "95.213.217.195"
     }
 
     PROD = {
-        1: "149.154.175.50",
+        1: "149.154.175.53",
         2: "149.154.167.51",
         3: "149.154.175.100",
         4: "149.154.167.91",
-        5: "91.108.56.149",
-        121: "95.213.217.195"
+        5: "91.108.56.130",
+        203: "91.105.192.100"
+    }
+
+    PROD_MEDIA = {
+        2: "149.154.167.151",
+        4: "149.154.164.250"
     }
 
     TEST_IPV6 = {
         1: "2001:b28:f23d:f001::e",
         2: "2001:67c:4e8:f002::e",
         3: "2001:b28:f23d:f003::e",
-        121: "2a03:b0c0:3:d0::114:d001"
     }
 
     PROD_IPV6 = {
@@ -47,19 +52,32 @@ class DataCenter:
         3: "2001:b28:f23d:f003::a",
         4: "2001:67c:4e8:f004::a",
         5: "2001:b28:f23f:f005::a",
-        121: "2a03:b0c0:3:d0::114:d001"
+        203: "2a0a:f280:0203:000a:5000:0000:0000:0100"
     }
 
-    def __new__(cls, dc_id: int, test_mode: bool, ipv6: bool):
-        if ipv6:
-            return (
-                (cls.TEST_IPV6[dc_id], 80)
-                if test_mode
-                else (cls.PROD_IPV6[dc_id], 443)
-            )
+    PROD_IPV6_MEDIA = {
+        2: "2001:067c:04e8:f002:0000:0000:0000:000b",
+        4: "2001:067c:04e8:f004:0000:0000:0000:000b"
+    }
+
+    def __new__(cls, dc_id: int, test_mode: bool, ipv6: bool, media: bool) -> Tuple[str, int]:
+        if test_mode:
+            if ipv6:
+                ip = cls.TEST_IPV6[dc_id]
+            else:
+                ip = cls.TEST[dc_id]
+
+            return ip, 80
         else:
-            return (
-                (cls.TEST[dc_id], 80)
-                if test_mode
-                else (cls.PROD[dc_id], 443)
-            )
+            if ipv6:
+                if media:
+                    ip = cls.PROD_IPV6_MEDIA.get(dc_id, cls.PROD_IPV6[dc_id])
+                else:
+                    ip = cls.PROD_IPV6[dc_id]
+            else:
+                if media:
+                    ip = cls.PROD_MEDIA.get(dc_id, cls.PROD[dc_id])
+                else:
+                    ip = cls.PROD[dc_id]
+
+            return ip, 443
diff --git a/pyrogram/session/internals/msg_factory.py b/pyrogram/session/internals/msg_factory.py
index 7d922ec32b9..837f17d0e45 100644
--- a/pyrogram/session/internals/msg_factory.py
+++ b/pyrogram/session/internals/msg_factory.py
@@ -1,38 +1,38 @@
-# Pyrogram - Telegram MTProto API Client Library for Python
-# Copyright (C) 2017-2019 Dan Tès 
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
 #
-# This file is part of Pyrogram.
+#  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 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.
+#  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 .
+#  You should have received a copy of the GNU Lesser General Public License
+#  along with Pyrogram.  If not, see .
 
-from pyrogram.api.core import Message, MsgContainer, Object
-from pyrogram.api.functions import Ping
-from pyrogram.api.types import MsgsAck, HttpWait
+from pyrogram.raw.core import Message, MsgContainer, TLObject
+from pyrogram.raw.functions import Ping
+from pyrogram.raw.types import MsgsAck, HttpWait
 from .msg_id import MsgId
 from .seq_no import SeqNo
 
-not_content_related = [Ping, HttpWait, MsgsAck, MsgContainer]
+not_content_related = (Ping, HttpWait, MsgsAck, MsgContainer)
 
 
 class MsgFactory:
     def __init__(self):
         self.seq_no = SeqNo()
 
-    def __call__(self, body: Object) -> Message:
+    def __call__(self, body: TLObject) -> Message:
         return Message(
             body,
             MsgId(),
-            self.seq_no(type(body) not in not_content_related),
+            self.seq_no(not isinstance(body, not_content_related)),
             len(body)
         )
diff --git a/pyrogram/session/internals/msg_id.py b/pyrogram/session/internals/msg_id.py
index 3826aaa5973..da2e264ff67 100644
--- a/pyrogram/session/internals/msg_id.py
+++ b/pyrogram/session/internals/msg_id.py
@@ -1,35 +1,35 @@
-# Pyrogram - Telegram MTProto API Client Library for Python
-# Copyright (C) 2017-2019 Dan Tès 
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
 #
-# This file is part of Pyrogram.
+#  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 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.
+#  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 .
+#  You should have received a copy of the GNU Lesser General Public License
+#  along with Pyrogram.  If not, see .
 
-from threading import Lock
-from time import time
+import logging
+import time
+
+log = logging.getLogger(__name__)
 
 
 class MsgId:
     last_time = 0
     offset = 0
-    lock = Lock()
 
     def __new__(cls) -> int:
-        with cls.lock:
-            now = time()
-            cls.offset = cls.offset + 4 if now == cls.last_time else 0
-            msg_id = int(now * 2 ** 32) + cls.offset
-            cls.last_time = now
+        now = int(time.time())
+        cls.offset = (cls.offset + 4) if now == cls.last_time else 0
+        msg_id = (now * 2 ** 32) + cls.offset
+        cls.last_time = now
 
-            return msg_id
+        return msg_id
diff --git a/pyrogram/session/internals/seq_no.py b/pyrogram/session/internals/seq_no.py
index ebc3efea429..79501d9863f 100644
--- a/pyrogram/session/internals/seq_no.py
+++ b/pyrogram/session/internals/seq_no.py
@@ -1,34 +1,30 @@
-# Pyrogram - Telegram MTProto API Client Library for Python
-# Copyright (C) 2017-2019 Dan Tès 
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
 #
-# This file is part of Pyrogram.
+#  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 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.
+#  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 threading import Lock
+#  You should have received a copy of the GNU Lesser General Public License
+#  along with Pyrogram.  If not, see .
 
 
 class SeqNo:
     def __init__(self):
         self.content_related_messages_sent = 0
-        self.lock = Lock()
 
     def __call__(self, is_content_related: bool) -> int:
-        with self.lock:
-            seq_no = (self.content_related_messages_sent * 2) + (1 if is_content_related else 0)
+        seq_no = (self.content_related_messages_sent * 2) + (1 if is_content_related else 0)
 
-            if is_content_related:
-                self.content_related_messages_sent += 1
+        if is_content_related:
+            self.content_related_messages_sent += 1
 
-            return seq_no
+        return seq_no
diff --git a/pyrogram/session/session.py b/pyrogram/session/session.py
index 66207037af9..0ed967a1a2b 100644
--- a/pyrogram/session/session.py
+++ b/pyrogram/session/session.py
@@ -1,39 +1,38 @@
-# Pyrogram - Telegram MTProto API Client Library for Python
-# Copyright (C) 2017-2019 Dan Tès 
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
 #
-# This file is part of Pyrogram.
+#  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 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.
+#  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 .
+#  You should have received a copy of the GNU Lesser General Public License
+#  along with Pyrogram.  If not, see .
 
+import asyncio
+import bisect
 import logging
-import threading
-import time
-from datetime import timedelta, datetime
-from hashlib import sha1, sha256
+import os
+from hashlib import sha1
 from io import BytesIO
-from os import urandom
-from queue import Queue
-from threading import Event, Thread
 
 import pyrogram
-from pyrogram import __copyright__, __license__, __version__
-from pyrogram.api import functions, types, core
-from pyrogram.api.all import layer
-from pyrogram.api.core import Message, Object, MsgContainer, Long, FutureSalt, Int
+from pyrogram import raw
 from pyrogram.connection import Connection
-from pyrogram.crypto import AES, KDF
-from pyrogram.errors import RPCError, InternalServerError, AuthKeyDuplicated
+from pyrogram.crypto import mtproto
+from pyrogram.errors import (
+    RPCError, InternalServerError, AuthKeyDuplicated, FloodWait, ServiceUnavailable, BadMsgNotification,
+    SecurityCheckMismatch
+)
+from pyrogram.raw.all import layer
+from pyrogram.raw.core import TLObject, MsgContainer, Int, FutureSalts
 from .internals import MsgId, MsgFactory
 
 log = logging.getLogger(__name__)
@@ -42,48 +41,37 @@
 class Result:
     def __init__(self):
         self.value = None
-        self.event = Event()
+        self.event = asyncio.Event()
 
 
 class Session:
-    INITIAL_SALT = 0x616e67656c696361
-    NET_WORKERS = 1
-    START_TIMEOUT = 1
+    START_TIMEOUT = 2
     WAIT_TIMEOUT = 15
-    MAX_RETRIES = 5
-    ACKS_THRESHOLD = 8
+    SLEEP_THRESHOLD = 10
+    MAX_RETRIES = 10
+    ACKS_THRESHOLD = 10
     PING_INTERVAL = 5
+    STORED_MSG_IDS_MAX_SIZE = 1000 * 2
 
-    notice_displayed = False
-
-    BAD_MSG_DESCRIPTION = {
-        16: "[16] msg_id too low, the client time has to be synchronized",
-        17: "[17] msg_id too high, the client time has to be synchronized",
-        18: "[18] incorrect two lower order msg_id bits, the server expects client message msg_id to be divisible by 4",
-        19: "[19] container msg_id is the same as msg_id of a previously received message",
-        20: "[20] message too old, it cannot be verified by the server",
-        32: "[32] msg_seqno too low",
-        33: "[33] msg_seqno too high",
-        34: "[34] an even msg_seqno expected, but odd received",
-        35: "[35] odd msg_seqno expected, but even received",
-        48: "[48] incorrect server salt",
-        64: "[64] invalid container"
+    TRANSPORT_ERRORS = {
+        404: "auth key not found",
+        429: "transport flood",
+        444: "invalid DC"
     }
 
-    def __init__(self,
-                 client: pyrogram,
-                 dc_id: int,
-                 auth_key: bytes,
-                 is_media: bool = False,
-                 is_cdn: bool = False):
-        if not Session.notice_displayed:
-            print("Pyrogram v{}, {}".format(__version__, __copyright__))
-            print("Licensed under the terms of the " + __license__, end="\n\n")
-            Session.notice_displayed = True
-
+    def __init__(
+        self,
+        client: "pyrogram.Client",
+        dc_id: int,
+        auth_key: bytes,
+        test_mode: bool,
+        is_media: bool = False,
+        is_cdn: bool = False
+    ):
         self.client = client
         self.dc_id = dc_id
         self.auth_key = auth_key
+        self.test_mode = test_mode
         self.is_media = is_media
         self.is_cdn = is_cdn
 
@@ -91,349 +79,334 @@ def __init__(self,
 
         self.auth_key_id = sha1(auth_key).digest()[-8:]
 
-        self.session_id = Long(MsgId())
+        self.session_id = os.urandom(8)
         self.msg_factory = MsgFactory()
 
-        self.current_salt = None
+        self.salt = 0
 
         self.pending_acks = set()
 
-        self.recv_queue = Queue()
         self.results = {}
 
-        self.ping_thread = None
-        self.ping_thread_event = Event()
+        self.stored_msg_ids = []
+
+        self.ping_task = None
+        self.ping_task_event = asyncio.Event()
 
-        self.next_salt_thread = None
-        self.next_salt_thread_event = Event()
+        self.recv_task = None
 
-        self.net_worker_list = []
+        self.is_started = asyncio.Event()
 
-        self.is_connected = Event()
+        self.loop = asyncio.get_event_loop()
 
-    def start(self):
+    async def start(self):
         while True:
-            self.connection = Connection(self.dc_id, self.client.test_mode, self.client.ipv6, self.client.proxy)
+            self.connection = Connection(
+                self.dc_id,
+                self.test_mode,
+                self.client.ipv6,
+                self.client.proxy,
+                self.is_media
+            )
 
             try:
-                self.connection.connect()
-
-                for i in range(self.NET_WORKERS):
-                    self.net_worker_list.append(
-                        Thread(
-                            target=self.net_worker,
-                            name="NetWorker#{}".format(i + 1)
-                        )
-                    )
+                await self.connection.connect()
 
-                    self.net_worker_list[-1].start()
+                self.recv_task = self.loop.create_task(self.recv_worker())
 
-                Thread(target=self.recv, name="RecvThread").start()
-
-                self.current_salt = FutureSalt(0, 0, self.INITIAL_SALT)
-                self.current_salt = FutureSalt(
-                    0, 0,
-                    self._send(
-                        functions.Ping(ping_id=0),
-                        timeout=self.START_TIMEOUT
-                    ).new_server_salt
-                )
-                self.current_salt = self._send(functions.GetFutureSalts(num=1), timeout=self.START_TIMEOUT).salts[0]
-
-                self.next_salt_thread = Thread(target=self.next_salt, name="NextSaltThread")
-                self.next_salt_thread.start()
+                await self.send(raw.functions.Ping(ping_id=0), timeout=self.START_TIMEOUT)
 
                 if not self.is_cdn:
-                    self._send(
-                        functions.InvokeWithLayer(
+                    await self.send(
+                        raw.functions.InvokeWithLayer(
                             layer=layer,
-                            query=functions.InitConnection(
-                                api_id=self.client.api_id,
+                            query=raw.functions.InitConnection(
+                                api_id=await self.client.storage.api_id(),
                                 app_version=self.client.app_version,
                                 device_model=self.client.device_model,
                                 system_version=self.client.system_version,
                                 system_lang_code=self.client.lang_code,
                                 lang_code=self.client.lang_code,
                                 lang_pack="",
-                                query=functions.help.GetConfig(),
+                                query=raw.functions.help.GetConfig(),
                             )
                         ),
                         timeout=self.START_TIMEOUT
                     )
 
-                self.ping_thread = Thread(target=self.ping, name="PingThread")
-                self.ping_thread.start()
-
-                log.info("Session initialized: Layer {}".format(layer))
-                log.info("Device: {} - {}".format(self.client.device_model, self.client.app_version))
-                log.info("System: {} ({})".format(self.client.system_version, self.client.lang_code.upper()))
+                self.ping_task = self.loop.create_task(self.ping_worker())
 
+                log.info("Session initialized: Layer %s", layer)
+                log.info("Device: %s - %s", self.client.device_model, self.client.app_version)
+                log.info("System: %s (%s)", self.client.system_version, self.client.lang_code)
             except AuthKeyDuplicated as e:
-                self.stop()
+                await self.stop()
                 raise e
-            except (OSError, TimeoutError, RPCError):
-                self.stop()
+            except (OSError, RPCError):
+                await self.stop()
             except Exception as e:
-                self.stop()
+                await self.stop()
                 raise e
             else:
                 break
 
-        self.is_connected.set()
-
-        log.debug("Session started")
+        self.is_started.set()
 
-    def stop(self):
-        self.is_connected.clear()
+        log.info("Session started")
 
-        self.ping_thread_event.set()
-        self.next_salt_thread_event.set()
+    async def stop(self):
+        self.is_started.clear()
 
-        if self.ping_thread is not None:
-            self.ping_thread.join()
+        self.stored_msg_ids.clear()
 
-        if self.next_salt_thread is not None:
-            self.next_salt_thread.join()
+        self.ping_task_event.set()
 
-        self.ping_thread_event.clear()
-        self.next_salt_thread_event.clear()
+        if self.ping_task is not None:
+            await self.ping_task
 
-        self.connection.close()
+        self.ping_task_event.clear()
 
-        for i in range(self.NET_WORKERS):
-            self.recv_queue.put(None)
+        await self.connection.close()
 
-        for i in self.net_worker_list:
-            i.join()
-
-        self.net_worker_list.clear()
-        self.recv_queue.queue.clear()
-
-        for i in self.results.values():
-            i.event.set()
+        if self.recv_task:
+            await self.recv_task
 
         if not self.is_media and callable(self.client.disconnect_handler):
             try:
-                self.client.disconnect_handler(self.client)
+                await self.client.disconnect_handler(self.client)
             except Exception as e:
-                log.error(e, exc_info=True)
-
-        log.debug("Session stopped")
-
-    def restart(self):
-        self.stop()
-        self.start()
+                log.exception(e)
+
+        log.info("Session stopped")
+
+    async def restart(self):
+        await self.stop()
+        await self.start()
+
+    async def handle_packet(self, packet):
+        data = await self.loop.run_in_executor(
+            pyrogram.crypto_executor,
+            mtproto.unpack,
+            BytesIO(packet),
+            self.session_id,
+            self.auth_key,
+            self.auth_key_id
+        )
+
+        messages = (
+            data.body.messages
+            if isinstance(data.body, MsgContainer)
+            else [data]
+        )
+
+        log.debug("Received: %s", data)
+
+        for msg in messages:
+            if msg.seq_no % 2 != 0:
+                if msg.msg_id in self.pending_acks:
+                    continue
+                else:
+                    self.pending_acks.add(msg.msg_id)
 
-    def pack(self, message: Message):
-        data = Long(self.current_salt.salt) + self.session_id + message.write()
-        padding = urandom(-(len(data) + 12) % 16 + 12)
+            try:
+                if len(self.stored_msg_ids) > Session.STORED_MSG_IDS_MAX_SIZE:
+                    del self.stored_msg_ids[:Session.STORED_MSG_IDS_MAX_SIZE // 2]
 
-        # 88 = 88 + 0 (outgoing message)
-        msg_key_large = sha256(self.auth_key[88: 88 + 32] + data + padding).digest()
-        msg_key = msg_key_large[8:24]
-        aes_key, aes_iv = KDF(self.auth_key, msg_key, True)
+                if self.stored_msg_ids:
+                    if msg.msg_id < self.stored_msg_ids[0]:
+                        raise SecurityCheckMismatch("The msg_id is lower than all the stored values")
 
-        return self.auth_key_id + msg_key + AES.ige256_encrypt(data + padding, aes_key, aes_iv)
+                    if msg.msg_id in self.stored_msg_ids:
+                        raise SecurityCheckMismatch("The msg_id is equal to any of the stored values")
 
-    def unpack(self, b: BytesIO) -> Message:
-        assert b.read(8) == self.auth_key_id, b.getvalue()
+                    time_diff = (msg.msg_id - MsgId()) / 2 ** 32
 
-        msg_key = b.read(16)
-        aes_key, aes_iv = KDF(self.auth_key, msg_key, False)
-        data = BytesIO(AES.ige256_decrypt(b.read(), aes_key, aes_iv))
-        data.read(8)
+                    if time_diff > 30:
+                        raise SecurityCheckMismatch("The msg_id belongs to over 30 seconds in the future. "
+                                                    "Most likely the client time has to be synchronized.")
 
-        # https://core.telegram.org/mtproto/security_guidelines#checking-session-id
-        assert data.read(8) == self.session_id
+                    if time_diff < -300:
+                        raise SecurityCheckMismatch("The msg_id belongs to over 300 seconds in the past. "
+                                                    "Most likely the client time has to be synchronized.")
+            except SecurityCheckMismatch as e:
+                log.info("Discarding packet: %s", e)
+                await self.connection.close()
+                return
+            else:
+                bisect.insort(self.stored_msg_ids, msg.msg_id)
 
-        message = Message.read(data)
+            if isinstance(msg.body, (raw.types.MsgDetailedInfo, raw.types.MsgNewDetailedInfo)):
+                self.pending_acks.add(msg.body.answer_msg_id)
+                continue
 
-        # https://core.telegram.org/mtproto/security_guidelines#checking-sha256-hash-value-of-msg-key
-        # https://core.telegram.org/mtproto/security_guidelines#checking-message-length
-        # 96 = 88 + 8 (incoming message)
-        assert msg_key == sha256(self.auth_key[96:96 + 32] + data.getvalue()).digest()[8:24]
+            if isinstance(msg.body, raw.types.NewSessionCreated):
+                continue
 
-        # https://core.telegram.org/mtproto/security_guidelines#checking-msg-id
-        # TODO: check for lower msg_ids
-        assert message.msg_id % 2 != 0
+            msg_id = None
 
-        return message
+            if isinstance(msg.body, (raw.types.BadMsgNotification, raw.types.BadServerSalt)):
+                msg_id = msg.body.bad_msg_id
+            elif isinstance(msg.body, (FutureSalts, raw.types.RpcResult)):
+                msg_id = msg.body.req_msg_id
+            elif isinstance(msg.body, raw.types.Pong):
+                msg_id = msg.body.msg_id
+            else:
+                if self.client is not None:
+                    self.loop.create_task(self.client.handle_updates(msg.body))
 
-    def net_worker(self):
-        name = threading.current_thread().name
-        log.debug("{} started".format(name))
+            if msg_id in self.results:
+                self.results[msg_id].value = getattr(msg.body, "result", msg.body)
+                self.results[msg_id].event.set()
 
-        while True:
-            packet = self.recv_queue.get()
-
-            if packet is None:
-                break
+        if len(self.pending_acks) >= self.ACKS_THRESHOLD:
+            log.debug("Sending %s acks", len(self.pending_acks))
 
             try:
-                data = self.unpack(BytesIO(packet))
-
-                messages = (
-                    data.body.messages
-                    if isinstance(data.body, MsgContainer)
-                    else [data]
-                )
-
-                log.debug(data)
-
-                for msg in messages:
-                    if msg.seq_no % 2 != 0:
-                        if msg.msg_id in self.pending_acks:
-                            continue
-                        else:
-                            self.pending_acks.add(msg.msg_id)
-
-                    if isinstance(msg.body, (types.MsgDetailedInfo, types.MsgNewDetailedInfo)):
-                        self.pending_acks.add(msg.body.answer_msg_id)
-                        continue
-
-                    if isinstance(msg.body, types.NewSessionCreated):
-                        continue
-
-                    msg_id = None
-
-                    if isinstance(msg.body, (types.BadMsgNotification, types.BadServerSalt)):
-                        msg_id = msg.body.bad_msg_id
-                    elif isinstance(msg.body, (core.FutureSalts, types.RpcResult)):
-                        msg_id = msg.body.req_msg_id
-                    elif isinstance(msg.body, types.Pong):
-                        msg_id = msg.body.msg_id
-                    else:
-                        if self.client is not None:
-                            self.client.updates_queue.put(msg.body)
-
-                    if msg_id in self.results:
-                        self.results[msg_id].value = getattr(msg.body, "result", msg.body)
-                        self.results[msg_id].event.set()
-
-                if len(self.pending_acks) >= self.ACKS_THRESHOLD:
-                    log.info("Send {} acks".format(len(self.pending_acks)))
-
-                    try:
-                        self._send(types.MsgsAck(msg_ids=list(self.pending_acks)), False)
-                    except (OSError, TimeoutError):
-                        pass
-                    else:
-                        self.pending_acks.clear()
-            except Exception as e:
-                log.error(e, exc_info=True)
-
-        log.debug("{} stopped".format(name))
+                await self.send(raw.types.MsgsAck(msg_ids=list(self.pending_acks)), False)
+            except OSError:
+                pass
+            else:
+                self.pending_acks.clear()
 
-    def ping(self):
-        log.debug("PingThread started")
+    async def ping_worker(self):
+        log.info("PingTask started")
 
         while True:
-            self.ping_thread_event.wait(self.PING_INTERVAL)
-
-            if self.ping_thread_event.is_set():
-                break
-
             try:
-                self._send(functions.PingDelayDisconnect(
-                    ping_id=0, disconnect_delay=self.WAIT_TIMEOUT + 10
-                ), False)
-            except (OSError, TimeoutError, RPCError):
+                await asyncio.wait_for(self.ping_task_event.wait(), self.PING_INTERVAL)
+            except asyncio.TimeoutError:
                 pass
-
-        log.debug("PingThread stopped")
-
-    def next_salt(self):
-        log.debug("NextSaltThread started")
-
-        while True:
-            now = datetime.now()
-
-            # Seconds to wait until middle-overlap, which is
-            # 15 minutes before/after the current/next salt end/start time
-            dt = (self.current_salt.valid_until - now).total_seconds() - 900
-
-            log.debug("Current salt: {} | Next salt in {:.0f}m {:.0f}s ({})".format(
-                self.current_salt.salt,
-                dt // 60,
-                dt % 60,
-                now + timedelta(seconds=dt)
-            ))
-
-            self.next_salt_thread_event.wait(dt)
-
-            if self.next_salt_thread_event.is_set():
+            else:
                 break
 
             try:
-                self.current_salt = self._send(functions.GetFutureSalts(num=1)).salts[0]
-            except (OSError, TimeoutError, RPCError):
-                self.connection.close()
-                break
+                await self.send(
+                    raw.functions.PingDelayDisconnect(
+                        ping_id=0, disconnect_delay=self.WAIT_TIMEOUT + 10
+                    ), False
+                )
+            except (OSError, RPCError):
+                pass
 
-        log.debug("NextSaltThread stopped")
+        log.info("PingTask stopped")
 
-    def recv(self):
-        log.debug("RecvThread started")
+    async def recv_worker(self):
+        log.info("NetworkTask started")
 
         while True:
-            packet = self.connection.recv()
+            packet = await self.connection.recv()
 
             if packet is None or len(packet) == 4:
                 if packet:
-                    log.warning("Server sent \"{}\"".format(Int.read(BytesIO(packet))))
+                    error_code = -Int.read(BytesIO(packet))
+
+                    log.warning(
+                        "Server sent transport error: %s (%s)",
+                        error_code, Session.TRANSPORT_ERRORS.get(error_code, "unknown error")
+                    )
+
+                if self.is_started.is_set():
+                    self.loop.create_task(self.restart())
 
-                if self.is_connected.is_set():
-                    Thread(target=self.restart, name="RestartThread").start()
                 break
 
-            self.recv_queue.put(packet)
+            self.loop.create_task(self.handle_packet(packet))
 
-        log.debug("RecvThread stopped")
+        log.info("NetworkTask stopped")
 
-    def _send(self, data: Object, wait_response: bool = True, timeout: float = WAIT_TIMEOUT):
+    async def send(self, data: TLObject, wait_response: bool = True, timeout: float = WAIT_TIMEOUT):
         message = self.msg_factory(data)
         msg_id = message.msg_id
 
         if wait_response:
             self.results[msg_id] = Result()
 
-        payload = self.pack(message)
+        log.debug("Sent: %s", message)
+
+        payload = await self.loop.run_in_executor(
+            pyrogram.crypto_executor,
+            mtproto.pack,
+            message,
+            self.salt,
+            self.session_id,
+            self.auth_key,
+            self.auth_key_id
+        )
 
         try:
-            self.connection.send(payload)
+            await self.connection.send(payload)
         except OSError as e:
             self.results.pop(msg_id, None)
             raise e
 
         if wait_response:
-            self.results[msg_id].event.wait(timeout)
+            try:
+                await asyncio.wait_for(self.results[msg_id].event.wait(), timeout)
+            except asyncio.TimeoutError:
+                pass
+
             result = self.results.pop(msg_id).value
 
             if result is None:
-                raise TimeoutError
-            elif isinstance(result, types.RpcError):
+                raise TimeoutError("Request timed out")
+
+            if isinstance(result, raw.types.RpcError):
+                if isinstance(data, (raw.functions.InvokeWithoutUpdates, raw.functions.InvokeWithTakeout)):
+                    data = data.query
+
                 RPCError.raise_it(result, type(data))
-            elif isinstance(result, types.BadMsgNotification):
-                raise Exception(self.BAD_MSG_DESCRIPTION.get(
-                    result.error_code,
-                    "Error code {}".format(result.error_code)
-                ))
-            else:
-                return result
 
-    def send(self, data: Object, retries: int = MAX_RETRIES, timeout: float = WAIT_TIMEOUT):
-        self.is_connected.wait(self.WAIT_TIMEOUT)
+            if isinstance(result, raw.types.BadMsgNotification):
+                log.warning("%s: %s", BadMsgNotification.__name__, BadMsgNotification(result.error_code))
+
+            if isinstance(result, raw.types.BadServerSalt):
+                self.salt = result.new_server_salt
+                return await self.send(data, wait_response, timeout)
+
+            return result
 
+    async def invoke(
+        self,
+        query: TLObject,
+        retries: int = MAX_RETRIES,
+        timeout: float = WAIT_TIMEOUT,
+        sleep_threshold: float = SLEEP_THRESHOLD
+    ):
         try:
-            return self._send(data, timeout=timeout)
-        except (OSError, TimeoutError, InternalServerError) as e:
-            if retries == 0:
-                raise e from None
-
-            (log.warning if retries < 3 else log.info)(
-                "{}: {} Retrying {}".format(
-                    Session.MAX_RETRIES - retries,
-                    datetime.now(), type(data)))
-
-            time.sleep(0.5)
-            return self.send(data, retries - 1, timeout)
+            await asyncio.wait_for(self.is_started.wait(), self.WAIT_TIMEOUT)
+        except asyncio.TimeoutError:
+            pass
+
+        if isinstance(query, (raw.functions.InvokeWithoutUpdates, raw.functions.InvokeWithTakeout)):
+            inner_query = query.query
+        else:
+            inner_query = query
+
+        query_name = ".".join(inner_query.QUALNAME.split(".")[1:])
+
+        while True:
+            try:
+                return await self.send(query, timeout=timeout)
+            except FloodWait as e:
+                amount = e.value
+
+                if amount > sleep_threshold >= 0:
+                    raise
+
+                log.warning('[%s] Waiting for %s seconds before continuing (required by "%s")',
+                            self.client.name, amount, query_name)
+
+                await asyncio.sleep(amount)
+            except (OSError, InternalServerError, ServiceUnavailable) as e:
+                if retries == 0:
+                    raise e from None
+
+                (log.warning if retries < 2 else log.info)(
+                    '[%s] Retrying "%s" due to: %s',
+                    Session.MAX_RETRIES - retries + 1,
+                    query_name, str(e) or repr(e)
+                )
+
+                await asyncio.sleep(0.5)
+
+                return await self.invoke(query, retries - 1, timeout)
diff --git a/pyrogram/storage/__init__.py b/pyrogram/storage/__init__.py
new file mode 100644
index 00000000000..2a43309a1b3
--- /dev/null
+++ b/pyrogram/storage/__init__.py
@@ -0,0 +1,21 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .file_storage import FileStorage
+from .memory_storage import MemoryStorage
+from .storage import Storage
diff --git a/pyrogram/storage/file_storage.py b/pyrogram/storage/file_storage.py
new file mode 100644
index 00000000000..aebe9176716
--- /dev/null
+++ b/pyrogram/storage/file_storage.py
@@ -0,0 +1,69 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import logging
+import os
+import sqlite3
+from pathlib import Path
+
+from .sqlite_storage import SQLiteStorage
+
+log = logging.getLogger(__name__)
+
+
+class FileStorage(SQLiteStorage):
+    FILE_EXTENSION = ".session"
+
+    def __init__(self, name: str, workdir: Path):
+        super().__init__(name)
+
+        self.database = workdir / (self.name + self.FILE_EXTENSION)
+
+    def update(self):
+        version = self.version()
+
+        if version == 1:
+            with self.conn:
+                self.conn.execute("DELETE FROM peers")
+
+            version += 1
+
+        if version == 2:
+            with self.conn:
+                self.conn.execute("ALTER TABLE sessions ADD api_id INTEGER")
+
+            version += 1
+
+        self.version(version)
+
+    async def open(self):
+        path = self.database
+        file_exists = path.is_file()
+
+        self.conn = sqlite3.connect(str(path), timeout=1, check_same_thread=False)
+
+        if not file_exists:
+            self.create()
+        else:
+            self.update()
+
+        with self.conn:
+            self.conn.execute("VACUUM")
+
+    async def delete(self):
+        os.remove(self.database)
diff --git a/pyrogram/storage/memory_storage.py b/pyrogram/storage/memory_storage.py
new file mode 100644
index 00000000000..2c01f4474da
--- /dev/null
+++ b/pyrogram/storage/memory_storage.py
@@ -0,0 +1,73 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import base64
+import logging
+import sqlite3
+import struct
+
+from .sqlite_storage import SQLiteStorage
+
+log = logging.getLogger(__name__)
+
+
+class MemoryStorage(SQLiteStorage):
+    def __init__(self, name: str, session_string: str = None):
+        super().__init__(name)
+
+        self.session_string = session_string
+
+    async def open(self):
+        self.conn = sqlite3.connect(":memory:", check_same_thread=False)
+        self.create()
+
+        if self.session_string:
+            # Old format
+            if len(self.session_string) in [self.SESSION_STRING_SIZE, self.SESSION_STRING_SIZE_64]:
+                dc_id, test_mode, auth_key, user_id, is_bot = struct.unpack(
+                    (self.OLD_SESSION_STRING_FORMAT
+                     if len(self.session_string) == self.SESSION_STRING_SIZE else
+                     self.OLD_SESSION_STRING_FORMAT_64),
+                    base64.urlsafe_b64decode(self.session_string + "=" * (-len(self.session_string) % 4))
+                )
+
+                await self.dc_id(dc_id)
+                await self.test_mode(test_mode)
+                await self.auth_key(auth_key)
+                await self.user_id(user_id)
+                await self.is_bot(is_bot)
+                await self.date(0)
+
+                log.warning("You are using an old session string format. Use export_session_string to update")
+                return
+
+            dc_id, api_id, test_mode, auth_key, user_id, is_bot = struct.unpack(
+                self.SESSION_STRING_FORMAT,
+                base64.urlsafe_b64decode(self.session_string + "=" * (-len(self.session_string) % 4))
+            )
+
+            await self.dc_id(dc_id)
+            await self.api_id(api_id)
+            await self.test_mode(test_mode)
+            await self.auth_key(auth_key)
+            await self.user_id(user_id)
+            await self.is_bot(is_bot)
+            await self.date(0)
+
+    async def delete(self):
+        pass
diff --git a/pyrogram/storage/sqlite_storage.py b/pyrogram/storage/sqlite_storage.py
new file mode 100644
index 00000000000..e28b9b746eb
--- /dev/null
+++ b/pyrogram/storage/sqlite_storage.py
@@ -0,0 +1,222 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import inspect
+import sqlite3
+import time
+from typing import List, Tuple, Any
+
+from pyrogram import raw
+from .storage import Storage
+from .. import utils
+
+# language=SQLite
+SCHEMA = """
+CREATE TABLE sessions
+(
+    dc_id     INTEGER PRIMARY KEY,
+    api_id    INTEGER,
+    test_mode INTEGER,
+    auth_key  BLOB,
+    date      INTEGER NOT NULL,
+    user_id   INTEGER,
+    is_bot    INTEGER
+);
+
+CREATE TABLE peers
+(
+    id             INTEGER PRIMARY KEY,
+    access_hash    INTEGER,
+    type           INTEGER NOT NULL,
+    username       TEXT,
+    phone_number   TEXT,
+    last_update_on INTEGER NOT NULL DEFAULT (CAST(STRFTIME('%s', 'now') AS INTEGER))
+);
+
+CREATE TABLE version
+(
+    number INTEGER PRIMARY KEY
+);
+
+CREATE INDEX idx_peers_id ON peers (id);
+CREATE INDEX idx_peers_username ON peers (username);
+CREATE INDEX idx_peers_phone_number ON peers (phone_number);
+
+CREATE TRIGGER trg_peers_last_update_on
+    AFTER UPDATE
+    ON peers
+BEGIN
+    UPDATE peers
+    SET last_update_on = CAST(STRFTIME('%s', 'now') AS INTEGER)
+    WHERE id = NEW.id;
+END;
+"""
+
+
+def get_input_peer(peer_id: int, access_hash: int, peer_type: str):
+    if peer_type in ["user", "bot"]:
+        return raw.types.InputPeerUser(
+            user_id=peer_id,
+            access_hash=access_hash
+        )
+
+    if peer_type == "group":
+        return raw.types.InputPeerChat(
+            chat_id=-peer_id
+        )
+
+    if peer_type in ["channel", "supergroup"]:
+        return raw.types.InputPeerChannel(
+            channel_id=utils.get_channel_id(peer_id),
+            access_hash=access_hash
+        )
+
+    raise ValueError(f"Invalid peer type: {peer_type}")
+
+
+class SQLiteStorage(Storage):
+    VERSION = 3
+    USERNAME_TTL = 8 * 60 * 60
+
+    def __init__(self, name: str):
+        super().__init__(name)
+
+        self.conn = None  # type: sqlite3.Connection
+
+    def create(self):
+        with self.conn:
+            self.conn.executescript(SCHEMA)
+
+            self.conn.execute(
+                "INSERT INTO version VALUES (?)",
+                (self.VERSION,)
+            )
+
+            self.conn.execute(
+                "INSERT INTO sessions VALUES (?, ?, ?, ?, ?, ?, ?)",
+                (2, None, None, None, 0, None, None)
+            )
+
+    async def open(self):
+        raise NotImplementedError
+
+    async def save(self):
+        await self.date(int(time.time()))
+        self.conn.commit()
+
+    async def close(self):
+        self.conn.close()
+
+    async def delete(self):
+        raise NotImplementedError
+
+    async def update_peers(self, peers: List[Tuple[int, int, str, str, str]]):
+        self.conn.executemany(
+            "REPLACE INTO peers (id, access_hash, type, username, phone_number)"
+            "VALUES (?, ?, ?, ?, ?)",
+            peers
+        )
+
+    async def get_peer_by_id(self, peer_id: int):
+        r = self.conn.execute(
+            "SELECT id, access_hash, type FROM peers WHERE id = ?",
+            (peer_id,)
+        ).fetchone()
+
+        if r is None:
+            raise KeyError(f"ID not found: {peer_id}")
+
+        return get_input_peer(*r)
+
+    async def get_peer_by_username(self, username: str):
+        r = self.conn.execute(
+            "SELECT id, access_hash, type, last_update_on FROM peers WHERE username = ?"
+            "ORDER BY last_update_on DESC",
+            (username,)
+        ).fetchone()
+
+        if r is None:
+            raise KeyError(f"Username not found: {username}")
+
+        if abs(time.time() - r[3]) > self.USERNAME_TTL:
+            raise KeyError(f"Username expired: {username}")
+
+        return get_input_peer(*r[:3])
+
+    async def get_peer_by_phone_number(self, phone_number: str):
+        r = self.conn.execute(
+            "SELECT id, access_hash, type FROM peers WHERE phone_number = ?",
+            (phone_number,)
+        ).fetchone()
+
+        if r is None:
+            raise KeyError(f"Phone number not found: {phone_number}")
+
+        return get_input_peer(*r)
+
+    def _get(self):
+        attr = inspect.stack()[2].function
+
+        return self.conn.execute(
+            f"SELECT {attr} FROM sessions"
+        ).fetchone()[0]
+
+    def _set(self, value: Any):
+        attr = inspect.stack()[2].function
+
+        with self.conn:
+            self.conn.execute(
+                f"UPDATE sessions SET {attr} = ?",
+                (value,)
+            )
+
+    def _accessor(self, value: Any = object):
+        return self._get() if value == object else self._set(value)
+
+    async def dc_id(self, value: int = object):
+        return self._accessor(value)
+
+    async def api_id(self, value: int = object):
+        return self._accessor(value)
+
+    async def test_mode(self, value: bool = object):
+        return self._accessor(value)
+
+    async def auth_key(self, value: bytes = object):
+        return self._accessor(value)
+
+    async def date(self, value: int = object):
+        return self._accessor(value)
+
+    async def user_id(self, value: int = object):
+        return self._accessor(value)
+
+    async def is_bot(self, value: bool = object):
+        return self._accessor(value)
+
+    def version(self, value: int = object):
+        if value == object:
+            return self.conn.execute(
+                "SELECT number FROM version"
+            ).fetchone()[0]
+        else:
+            with self.conn:
+                self.conn.execute(
+                    "UPDATE version SET number = ?",
+                    (value,)
+                )
diff --git a/pyrogram/storage/storage.py b/pyrogram/storage/storage.py
new file mode 100644
index 00000000000..0689b6826c5
--- /dev/null
+++ b/pyrogram/storage/storage.py
@@ -0,0 +1,91 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import base64
+import struct
+from typing import List, Tuple
+
+
+class Storage:
+    OLD_SESSION_STRING_FORMAT = ">B?256sI?"
+    OLD_SESSION_STRING_FORMAT_64 = ">B?256sQ?"
+    SESSION_STRING_SIZE = 351
+    SESSION_STRING_SIZE_64 = 356
+
+    SESSION_STRING_FORMAT = ">BI?256sQ?"
+
+    def __init__(self, name: str):
+        self.name = name
+
+    async def open(self):
+        raise NotImplementedError
+
+    async def save(self):
+        raise NotImplementedError
+
+    async def close(self):
+        raise NotImplementedError
+
+    async def delete(self):
+        raise NotImplementedError
+
+    async def update_peers(self, peers: List[Tuple[int, int, str, str, str]]):
+        raise NotImplementedError
+
+    async def get_peer_by_id(self, peer_id: int):
+        raise NotImplementedError
+
+    async def get_peer_by_username(self, username: str):
+        raise NotImplementedError
+
+    async def get_peer_by_phone_number(self, phone_number: str):
+        raise NotImplementedError
+
+    async def dc_id(self, value: int = object):
+        raise NotImplementedError
+
+    async def api_id(self, value: int = object):
+        raise NotImplementedError
+
+    async def test_mode(self, value: bool = object):
+        raise NotImplementedError
+
+    async def auth_key(self, value: bytes = object):
+        raise NotImplementedError
+
+    async def date(self, value: int = object):
+        raise NotImplementedError
+
+    async def user_id(self, value: int = object):
+        raise NotImplementedError
+
+    async def is_bot(self, value: bool = object):
+        raise NotImplementedError
+
+    async def export_session_string(self):
+        packed = struct.pack(
+            self.SESSION_STRING_FORMAT,
+            await self.dc_id(),
+            await self.api_id(),
+            await self.test_mode(),
+            await self.auth_key(),
+            await self.user_id(),
+            await self.is_bot()
+        )
+
+        return base64.urlsafe_b64encode(packed).decode().rstrip("=")
diff --git a/pyrogram/sync.py b/pyrogram/sync.py
new file mode 100644
index 00000000000..94c82a3dd64
--- /dev/null
+++ b/pyrogram/sync.py
@@ -0,0 +1,113 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import asyncio
+import functools
+import inspect
+import threading
+
+from pyrogram import types
+from pyrogram.methods import Methods
+from pyrogram.methods.utilities import idle as idle_module, compose as compose_module
+
+
+def async_to_sync(obj, name):
+    function = getattr(obj, name)
+    main_loop = asyncio.get_event_loop()
+
+    def async_to_sync_gen(agen, loop, is_main_thread):
+        async def anext(agen):
+            try:
+                return await agen.__anext__(), False
+            except StopAsyncIteration:
+                return None, True
+
+        while True:
+            if is_main_thread:
+                item, done = loop.run_until_complete(anext(agen))
+            else:
+                item, done = asyncio.run_coroutine_threadsafe(anext(agen), loop).result()
+
+            if done:
+                break
+
+            yield item
+
+    @functools.wraps(function)
+    def async_to_sync_wrap(*args, **kwargs):
+        coroutine = function(*args, **kwargs)
+
+        try:
+            loop = asyncio.get_event_loop()
+        except RuntimeError:
+            loop = asyncio.new_event_loop()
+            asyncio.set_event_loop(loop)
+
+        if threading.current_thread() is threading.main_thread() or not main_loop.is_running():
+            if loop.is_running():
+                return coroutine
+            else:
+                if inspect.iscoroutine(coroutine):
+                    return loop.run_until_complete(coroutine)
+
+                if inspect.isasyncgen(coroutine):
+                    return async_to_sync_gen(coroutine, loop, True)
+        else:
+            if inspect.iscoroutine(coroutine):
+                if loop.is_running():
+                    async def coro_wrapper():
+                        return await asyncio.wrap_future(asyncio.run_coroutine_threadsafe(coroutine, main_loop))
+
+                    return coro_wrapper()
+                else:
+                    return asyncio.run_coroutine_threadsafe(coroutine, main_loop).result()
+
+            if inspect.isasyncgen(coroutine):
+                if loop.is_running():
+                    return coroutine
+                else:
+                    return async_to_sync_gen(coroutine, main_loop, False)
+
+    setattr(obj, name, async_to_sync_wrap)
+
+
+def wrap(source):
+    for name in dir(source):
+        method = getattr(source, name)
+
+        if not name.startswith("_"):
+            if inspect.iscoroutinefunction(method) or inspect.isasyncgenfunction(method):
+                async_to_sync(source, name)
+
+
+# Wrap all Client's relevant methods
+wrap(Methods)
+
+# Wrap types' bound methods
+for class_name in dir(types):
+    cls = getattr(types, class_name)
+
+    if inspect.isclass(cls):
+        wrap(cls)
+
+# Special case for idle and compose, because they are not inside Methods
+async_to_sync(idle_module, "idle")
+idle = getattr(idle_module, "idle")
+
+async_to_sync(compose_module, "compose")
+compose = getattr(compose_module, "compose")
diff --git a/pyrogram/types/__init__.py b/pyrogram/types/__init__.py
new file mode 100644
index 00000000000..e0859bbabd5
--- /dev/null
+++ b/pyrogram/types/__init__.py
@@ -0,0 +1,28 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .authorization import *
+from .bots_and_keyboards import *
+from .inline_mode import *
+from .input_media import *
+from .input_message_content import *
+from .list import List
+from .messages_and_media import *
+from .object import Object
+from .update import *
+from .user_and_chats import *
diff --git a/pyrogram/types/authorization/__init__.py b/pyrogram/types/authorization/__init__.py
new file mode 100644
index 00000000000..b31ad26a538
--- /dev/null
+++ b/pyrogram/types/authorization/__init__.py
@@ -0,0 +1,22 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .sent_code import SentCode
+from .terms_of_service import TermsOfService
+
+__all__ = ["TermsOfService", "SentCode"]
diff --git a/pyrogram/types/authorization/sent_code.py b/pyrogram/types/authorization/sent_code.py
new file mode 100644
index 00000000000..2b29bb3af4d
--- /dev/null
+++ b/pyrogram/types/authorization/sent_code.py
@@ -0,0 +1,62 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 pyrogram import raw, enums
+from ..object import Object
+
+
+class SentCode(Object):
+    """Contains info on a sent confirmation code.
+
+    Parameters:
+        type (:obj:`~pyrogram.enums.SentCodeType`):
+            Type of the current sent code.
+
+        phone_code_hash (``str``):
+            Confirmation code identifier useful for the next authorization steps (either
+            :meth:`~pyrogram.Client.sign_in` or :meth:`~pyrogram.Client.sign_up`).
+
+        next_type (:obj:`~pyrogram.enums.NextCodeType`, *optional*):
+            Type of the next code to be sent with :meth:`~pyrogram.Client.resend_code`.
+
+        timeout (``int``, *optional*):
+            Delay in seconds before calling :meth:`~pyrogram.Client.resend_code`.
+    """
+
+    def __init__(
+        self, *,
+        type: "enums.SentCodeType",
+        phone_code_hash: str,
+        next_type: "enums.NextCodeType" = None,
+        timeout: int = None
+    ):
+        super().__init__()
+
+        self.type = type
+        self.phone_code_hash = phone_code_hash
+        self.next_type = next_type
+        self.timeout = timeout
+
+    @staticmethod
+    def _parse(sent_code: raw.types.auth.SentCode) -> "SentCode":
+        return SentCode(
+            type=enums.SentCodeType(type(sent_code.type)),
+            phone_code_hash=sent_code.phone_code_hash,
+            next_type=enums.NextCodeType(type(sent_code.next_type)) if sent_code.next_type else None,
+            timeout=sent_code.timeout
+        )
diff --git a/pyrogram/types/authorization/terms_of_service.py b/pyrogram/types/authorization/terms_of_service.py
new file mode 100644
index 00000000000..3c5ffa6c6d8
--- /dev/null
+++ b/pyrogram/types/authorization/terms_of_service.py
@@ -0,0 +1,56 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import List
+
+from pyrogram import raw
+from pyrogram import types
+from ..object import Object
+
+
+class TermsOfService(Object):
+    """Telegram's Terms of Service returned by :meth:`~pyrogram.Client.sign_in`.
+
+    Parameters:
+        id (``str``):
+            Terms of Service identifier.
+
+        text (``str``):
+            Terms of Service text.
+
+        entities (List of :obj:`~pyrogram.types.MessageEntity`):
+            Special entities like URLs that appear in the text.
+    """
+
+    def __init__(self, *, id: str, text: str, entities: List["types.MessageEntity"]):
+        super().__init__()
+
+        self.id = id
+        self.text = text
+        self.entities = entities
+
+    @staticmethod
+    def _parse(terms_of_service: "raw.types.help.TermsOfService") -> "TermsOfService":
+        return TermsOfService(
+            id=terms_of_service.id.data,
+            text=terms_of_service.text,
+            entities=[
+                types.MessageEntity._parse(None, entity, {})
+                for entity in terms_of_service.entities
+            ] if terms_of_service.entities else None
+        )
diff --git a/pyrogram/types/bots_and_keyboards/__init__.py b/pyrogram/types/bots_and_keyboards/__init__.py
new file mode 100644
index 00000000000..6f05a3b486f
--- /dev/null
+++ b/pyrogram/types/bots_and_keyboards/__init__.py
@@ -0,0 +1,71 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .bot_command import BotCommand
+from .bot_command_scope import BotCommandScope
+from .bot_command_scope_all_chat_administrators import BotCommandScopeAllChatAdministrators
+from .bot_command_scope_all_group_chats import BotCommandScopeAllGroupChats
+from .bot_command_scope_all_private_chats import BotCommandScopeAllPrivateChats
+from .bot_command_scope_chat import BotCommandScopeChat
+from .bot_command_scope_chat_administrators import BotCommandScopeChatAdministrators
+from .bot_command_scope_chat_member import BotCommandScopeChatMember
+from .bot_command_scope_default import BotCommandScopeDefault
+from .callback_game import CallbackGame
+from .callback_query import CallbackQuery
+from .force_reply import ForceReply
+from .game_high_score import GameHighScore
+from .inline_keyboard_button import InlineKeyboardButton
+from .inline_keyboard_markup import InlineKeyboardMarkup
+from .keyboard_button import KeyboardButton
+from .login_url import LoginUrl
+from .menu_button import MenuButton
+from .menu_button_commands import MenuButtonCommands
+from .menu_button_default import MenuButtonDefault
+from .menu_button_web_app import MenuButtonWebApp
+from .reply_keyboard_markup import ReplyKeyboardMarkup
+from .reply_keyboard_remove import ReplyKeyboardRemove
+from .sent_web_app_message import SentWebAppMessage
+from .web_app_info import WebAppInfo
+
+__all__ = [
+    "CallbackGame",
+    "CallbackQuery",
+    "ForceReply",
+    "GameHighScore",
+    "InlineKeyboardButton",
+    "InlineKeyboardMarkup",
+    "KeyboardButton",
+    "ReplyKeyboardMarkup",
+    "ReplyKeyboardRemove",
+    "LoginUrl",
+    "BotCommand",
+    "BotCommandScope",
+    "BotCommandScopeAllChatAdministrators",
+    "BotCommandScopeAllGroupChats",
+    "BotCommandScopeAllPrivateChats",
+    "BotCommandScopeChat",
+    "BotCommandScopeChatAdministrators",
+    "BotCommandScopeChatMember",
+    "BotCommandScopeDefault",
+    "WebAppInfo",
+    "MenuButton",
+    "MenuButtonCommands",
+    "MenuButtonWebApp",
+    "MenuButtonDefault",
+    "SentWebAppMessage"
+]
diff --git a/pyrogram/types/bots_and_keyboards/bot_command.py b/pyrogram/types/bots_and_keyboards/bot_command.py
new file mode 100644
index 00000000000..88f459dcc46
--- /dev/null
+++ b/pyrogram/types/bots_and_keyboards/bot_command.py
@@ -0,0 +1,53 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 pyrogram import raw
+
+from ..object import Object
+
+
+class BotCommand(Object):
+    """A bot command with the standard slash "/" prefix.
+
+    Parameters:
+        command (``str``):
+            Text of the command; 1-32 characters.
+            Can contain only lowercase English letters, digits and underscores.
+
+        description (``str``):
+            Description of the command; 1-256 characters.
+    """
+
+    def __init__(self, command: str, description: str):
+        super().__init__()
+
+        self.command = command
+        self.description = description
+
+    def write(self) -> "raw.types.BotCommand":
+        return raw.types.BotCommand(
+            command=self.command,
+            description=self.description,
+        )
+
+    @staticmethod
+    def read(c: "raw.types.BotCommand") -> "BotCommand":
+        return BotCommand(
+            command=c.command,
+            description=c.description
+        )
diff --git a/pyrogram/types/bots_and_keyboards/bot_command_scope.py b/pyrogram/types/bots_and_keyboards/bot_command_scope.py
new file mode 100644
index 00000000000..2db638e9a7c
--- /dev/null
+++ b/pyrogram/types/bots_and_keyboards/bot_command_scope.py
@@ -0,0 +1,73 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import pyrogram
+from pyrogram import raw
+from ..object import Object
+
+
+class BotCommandScope(Object):
+    """Represents the scope to which bot commands are applied.
+
+    Currently, the following 7 scopes are supported:
+
+    - :obj:`~pyrogram.types.BotCommandScopeDefault`
+    - :obj:`~pyrogram.types.BotCommandScopeAllPrivateChats`
+    - :obj:`~pyrogram.types.BotCommandScopeAllGroupChats`
+    - :obj:`~pyrogram.types.BotCommandScopeAllChatAdministrators`
+    - :obj:`~pyrogram.types.BotCommandScopeChat`
+    - :obj:`~pyrogram.types.BotCommandScopeChatAdministrators`
+    - :obj:`~pyrogram.types.BotCommandScopeChatMember`
+
+    **Determining list of commands**
+
+    The following algorithm is used to determine the list of commands for a particular user viewing the bot menu.
+    The first list of commands which is set is returned:
+
+    **Commands in the chat with the bot**:
+
+    - BotCommandScopeChat + language_code
+    - BotCommandScopeChat
+    - BotCommandScopeAllPrivateChats + language_code
+    - BotCommandScopeAllPrivateChats
+    - BotCommandScopeDefault + language_code
+    - BotCommandScopeDefault
+
+    **Commands in group and supergroup chats**
+
+    - BotCommandScopeChatMember + language_code
+    - BotCommandScopeChatMember
+    - BotCommandScopeChatAdministrators + language_code (administrators only)
+    - BotCommandScopeChatAdministrators (administrators only)
+    - BotCommandScopeChat + language_code
+    - BotCommandScopeChat
+    - BotCommandScopeAllChatAdministrators + language_code (administrators only)
+    - BotCommandScopeAllChatAdministrators (administrators only)
+    - BotCommandScopeAllGroupChats + language_code
+    - BotCommandScopeAllGroupChats
+    - BotCommandScopeDefault + language_code
+    - BotCommandScopeDefault
+    """
+
+    def __init__(self, type: str):
+        super().__init__()
+
+        self.type = type
+
+    async def write(self, client: "pyrogram.Client") -> "raw.base.BotCommandScope":
+        raise NotImplementedError
diff --git a/pyrogram/types/bots_and_keyboards/bot_command_scope_all_chat_administrators.py b/pyrogram/types/bots_and_keyboards/bot_command_scope_all_chat_administrators.py
new file mode 100644
index 00000000000..0cc6339c774
--- /dev/null
+++ b/pyrogram/types/bots_and_keyboards/bot_command_scope_all_chat_administrators.py
@@ -0,0 +1,32 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import pyrogram
+from pyrogram import raw
+from .bot_command_scope import BotCommandScope
+
+
+class BotCommandScopeAllChatAdministrators(BotCommandScope):
+    """Represents the scope of bot commands, covering all group and supergroup chat administrators.
+    """
+
+    def __init__(self):
+        super().__init__("all_chat_administrators")
+
+    async def write(self, client: "pyrogram.Client") -> "raw.base.BotCommandScope":
+        return raw.types.BotCommandScopeChatAdmins()
diff --git a/pyrogram/types/bots_and_keyboards/bot_command_scope_all_group_chats.py b/pyrogram/types/bots_and_keyboards/bot_command_scope_all_group_chats.py
new file mode 100644
index 00000000000..5f8457f61dc
--- /dev/null
+++ b/pyrogram/types/bots_and_keyboards/bot_command_scope_all_group_chats.py
@@ -0,0 +1,32 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import pyrogram
+from pyrogram import raw
+from .bot_command_scope import BotCommandScope
+
+
+class BotCommandScopeAllGroupChats(BotCommandScope):
+    """Represents the scope of bot commands, covering all group and supergroup chats.
+    """
+
+    def __init__(self):
+        super().__init__("all_group_chats")
+
+    async def write(self, client: "pyrogram.Client") -> "raw.base.BotCommandScope":
+        return raw.types.BotCommandScopeChats()
diff --git a/pyrogram/types/bots_and_keyboards/bot_command_scope_all_private_chats.py b/pyrogram/types/bots_and_keyboards/bot_command_scope_all_private_chats.py
new file mode 100644
index 00000000000..14b80f0dcd9
--- /dev/null
+++ b/pyrogram/types/bots_and_keyboards/bot_command_scope_all_private_chats.py
@@ -0,0 +1,32 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import pyrogram
+from pyrogram import raw
+from .bot_command_scope import BotCommandScope
+
+
+class BotCommandScopeAllPrivateChats(BotCommandScope):
+    """Represents the scope of bot commands, covering all private chats.
+    """
+
+    def __init__(self):
+        super().__init__("all_private_chats")
+
+    async def write(self, client: "pyrogram.Client") -> "raw.base.BotCommandScope":
+        return raw.types.BotCommandScopeUsers()
diff --git a/pyrogram/types/bots_and_keyboards/bot_command_scope_chat.py b/pyrogram/types/bots_and_keyboards/bot_command_scope_chat.py
new file mode 100644
index 00000000000..28912383b73
--- /dev/null
+++ b/pyrogram/types/bots_and_keyboards/bot_command_scope_chat.py
@@ -0,0 +1,43 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import Union
+
+import pyrogram
+from pyrogram import raw
+from .bot_command_scope import BotCommandScope
+
+
+class BotCommandScopeChat(BotCommandScope):
+    """Represents the scope of bot commands, covering a specific chat.
+
+    Parameters:
+        chat_id (``int`` | ``str``):
+            Unique identifier for the target chat or username of the target supergroup (in the format
+            @supergroupusername).
+    """
+
+    def __init__(self, chat_id: Union[int, str]):
+        super().__init__("chat")
+
+        self.chat_id = chat_id
+
+    async def write(self, client: "pyrogram.Client") -> "raw.base.BotCommandScope":
+        return raw.types.BotCommandScopePeer(
+            peer=await client.resolve_peer(self.chat_id)
+        )
diff --git a/pyrogram/types/bots_and_keyboards/bot_command_scope_chat_administrators.py b/pyrogram/types/bots_and_keyboards/bot_command_scope_chat_administrators.py
new file mode 100644
index 00000000000..6f42a29f2b3
--- /dev/null
+++ b/pyrogram/types/bots_and_keyboards/bot_command_scope_chat_administrators.py
@@ -0,0 +1,43 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import Union
+
+import pyrogram
+from pyrogram import raw
+from .bot_command_scope import BotCommandScope
+
+
+class BotCommandScopeChatAdministrators(BotCommandScope):
+    """Represents the scope of bot commands, covering all administrators of a specific group or supergroup chat.
+
+    Parameters:
+        chat_id (``int`` | ``str``):
+            Unique identifier for the target chat or username of the target supergroup (in the format
+            @supergroupusername).
+    """
+
+    def __init__(self, chat_id: Union[int, str]):
+        super().__init__("chat_administrators")
+
+        self.chat_id = chat_id
+
+    async def write(self, client: "pyrogram.Client") -> "raw.base.BotCommandScope":
+        return raw.types.BotCommandScopePeerAdmins(
+            peer=await client.resolve_peer(self.chat_id)
+        )
diff --git a/pyrogram/types/bots_and_keyboards/bot_command_scope_chat_member.py b/pyrogram/types/bots_and_keyboards/bot_command_scope_chat_member.py
new file mode 100644
index 00000000000..8d91df1413c
--- /dev/null
+++ b/pyrogram/types/bots_and_keyboards/bot_command_scope_chat_member.py
@@ -0,0 +1,48 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import Union
+
+import pyrogram
+from pyrogram import raw
+from .bot_command_scope import BotCommandScope
+
+
+class BotCommandScopeChatMember(BotCommandScope):
+    """Represents the scope of bot commands, covering a specific member of a group or supergroup chat.
+
+    Parameters:
+        chat_id (``int`` | ``str``):
+            Unique identifier for the target chat or username of the target supergroup (in the format
+            @supergroupusername).
+
+        user_id (``int`` | ``str``):
+            Unique identifier of the target user.
+    """
+
+    def __init__(self, chat_id: Union[int, str], user_id: Union[int, str]):
+        super().__init__("chat_member")
+
+        self.chat_id = chat_id
+        self.user_id = user_id
+
+    async def write(self, client: "pyrogram.Client") -> "raw.base.BotCommandScope":
+        return raw.types.BotCommandScopePeerUser(
+            peer=await client.resolve_peer(self.chat_id),
+            user_id=await client.resolve_peer(self.user_id)
+        )
diff --git a/pyrogram/types/bots_and_keyboards/bot_command_scope_default.py b/pyrogram/types/bots_and_keyboards/bot_command_scope_default.py
new file mode 100644
index 00000000000..d11ab012f89
--- /dev/null
+++ b/pyrogram/types/bots_and_keyboards/bot_command_scope_default.py
@@ -0,0 +1,33 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import pyrogram
+from pyrogram import raw
+from .bot_command_scope import BotCommandScope
+
+
+class BotCommandScopeDefault(BotCommandScope):
+    """Represents the default scope of bot commands.
+    Default commands are used if no commands with a narrower scope are specified for the user.
+    """
+
+    def __init__(self):
+        super().__init__("default")
+
+    async def write(self, client: "pyrogram.Client") -> "raw.base.BotCommandScope":
+        return raw.types.BotCommandScopeDefault()
diff --git a/pyrogram/types/bots_and_keyboards/callback_game.py b/pyrogram/types/bots_and_keyboards/callback_game.py
new file mode 100644
index 00000000000..3bd89270ffc
--- /dev/null
+++ b/pyrogram/types/bots_and_keyboards/callback_game.py
@@ -0,0 +1,29 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 ..object import Object
+
+
+class CallbackGame(Object):
+    """Placeholder, currently holds no information.
+
+    Use BotFather to set up your game.
+    """
+
+    def __init__(self):
+        super().__init__()
diff --git a/pyrogram/types/bots_and_keyboards/callback_query.py b/pyrogram/types/bots_and_keyboards/callback_query.py
new file mode 100644
index 00000000000..efdd14ca4d1
--- /dev/null
+++ b/pyrogram/types/bots_and_keyboards/callback_query.py
@@ -0,0 +1,313 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import Union, List, Match, Optional
+
+import pyrogram
+from pyrogram import raw, enums
+from pyrogram import types
+from ..object import Object
+from ..update import Update
+from ... import utils
+
+
+class CallbackQuery(Object, Update):
+    """An incoming callback query from a callback button in an inline keyboard.
+
+    If the button that originated the query was attached to a message sent by the bot, the field *message*
+    will be present. If the button was attached to a message sent via the bot (in inline mode), the field
+    *inline_message_id* will be present. Exactly one of the fields *data* or *game_short_name* will be present.
+
+    Parameters:
+        id (``str``):
+            Unique identifier for this query.
+
+        from_user (:obj:`~pyrogram.types.User`):
+            Sender.
+
+        chat_instance (``str``, *optional*):
+            Global identifier, uniquely corresponding to the chat to which the message with the callback button was
+            sent. Useful for high scores in games.
+
+        message (:obj:`~pyrogram.types.Message`, *optional*):
+            Message with the callback button that originated the query. Note that message content and message date will
+            not be available if the message is too old.
+
+        inline_message_id (``str``):
+            Identifier of the message sent via the bot in inline mode, that originated the query.
+
+        data (``str`` | ``bytes``, *optional*):
+            Data associated with the callback button. Be aware that a bad client can send arbitrary data in this field.
+
+        game_short_name (``str``, *optional*):
+            Short name of a Game to be returned, serves as the unique identifier for the game.
+
+        matches (List of regex Matches, *optional*):
+            A list containing all `Match Objects `_ that match
+            the data of this callback query. Only applicable when using :obj:`Filters.regex `.
+    """
+
+    def __init__(
+        self,
+        *,
+        client: "pyrogram.Client" = None,
+        id: str,
+        from_user: "types.User",
+        chat_instance: str,
+        message: "types.Message" = None,
+        inline_message_id: str = None,
+        data: Union[str, bytes] = None,
+        game_short_name: str = None,
+        matches: List[Match] = None
+    ):
+        super().__init__(client)
+
+        self.id = id
+        self.from_user = from_user
+        self.chat_instance = chat_instance
+        self.message = message
+        self.inline_message_id = inline_message_id
+        self.data = data
+        self.game_short_name = game_short_name
+        self.matches = matches
+
+    @staticmethod
+    async def _parse(client: "pyrogram.Client", callback_query, users) -> "CallbackQuery":
+        message = None
+        inline_message_id = None
+
+        if isinstance(callback_query, raw.types.UpdateBotCallbackQuery):
+            chat_id = utils.get_peer_id(callback_query.peer)
+            message_id = callback_query.msg_id
+
+            message = client.message_cache[(chat_id, message_id)]
+
+            if not message:
+                message = await client.get_messages(chat_id, message_id)
+        elif isinstance(callback_query, raw.types.UpdateInlineBotCallbackQuery):
+            inline_message_id = utils.pack_inline_message_id(callback_query.msg_id)
+
+        # Try to decode callback query data into string. If that fails, fallback to bytes instead of decoding by
+        # ignoring/replacing errors, this way, button clicks will still work.
+        try:
+            data = callback_query.data.decode()
+        except (UnicodeDecodeError, AttributeError):
+            data = callback_query.data
+
+        return CallbackQuery(
+            id=str(callback_query.query_id),
+            from_user=types.User._parse(client, users[callback_query.user_id]),
+            message=message,
+            inline_message_id=inline_message_id,
+            chat_instance=str(callback_query.chat_instance),
+            data=data,
+            game_short_name=callback_query.game_short_name,
+            client=client
+        )
+
+    async def answer(self, text: str = None, show_alert: bool = None, url: str = None, cache_time: int = 0):
+        """Bound method *answer* of :obj:`~pyrogram.types.CallbackQuery`.
+
+        Use this method as a shortcut for:
+
+        .. code-block:: python
+
+            await client.answer_callback_query(
+                callback_query.id,
+                text="Hello",
+                show_alert=True
+            )
+
+        Example:
+            .. code-block:: python
+
+                await callback_query.answer("Hello", show_alert=True)
+
+        Parameters:
+            text (``str``, *optional*):
+                Text of the notification. If not specified, nothing will be shown to the user, 0-200 characters.
+
+            show_alert (``bool`` *optional*):
+                If true, an alert will be shown by the client instead of a notification at the top of the chat screen.
+                Defaults to False.
+
+            url (``str`` *optional*):
+                URL that will be opened by the user's client.
+                If you have created a Game and accepted the conditions via @Botfather, specify the URL that opens your
+                game – note that this will only work if the query comes from a callback_game button.
+                Otherwise, you may use links like t.me/your_bot?start=XXXX that open your bot with a parameter.
+
+            cache_time (``int`` *optional*):
+                The maximum amount of time in seconds that the result of the callback query may be cached client-side.
+                Telegram apps will support caching starting in version 3.14. Defaults to 0.
+        """
+        return await self._client.answer_callback_query(
+            callback_query_id=self.id,
+            text=text,
+            show_alert=show_alert,
+            url=url,
+            cache_time=cache_time
+        )
+
+    async def edit_message_text(
+        self,
+        text: str,
+        parse_mode: Optional["enums.ParseMode"] = None,
+        disable_web_page_preview: bool = None,
+        reply_markup: "types.InlineKeyboardMarkup" = None
+    ) -> Union["types.Message", bool]:
+        """Edit the text of messages attached to callback queries.
+
+        Bound method *edit_message_text* of :obj:`~pyrogram.types.CallbackQuery`.
+
+        Parameters:
+            text (``str``):
+                New text of the message.
+
+            parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+                By default, texts are parsed using both Markdown and HTML styles.
+                You can combine both syntaxes together.
+
+            disable_web_page_preview (``bool``, *optional*):
+                Disables link previews for links in this message.
+
+            reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*):
+                An InlineKeyboardMarkup object.
+
+        Returns:
+            :obj:`~pyrogram.types.Message` | ``bool``: On success, if the edited message was sent by the bot, the edited
+            message is returned, otherwise True is returned (message sent via the bot, as inline query result).
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+        if self.inline_message_id is None:
+            return await self._client.edit_message_text(
+                chat_id=self.message.chat.id,
+                message_id=self.message.id,
+                text=text,
+                parse_mode=parse_mode,
+                disable_web_page_preview=disable_web_page_preview,
+                reply_markup=reply_markup
+            )
+        else:
+            return await self._client.edit_inline_text(
+                inline_message_id=self.inline_message_id,
+                text=text,
+                parse_mode=parse_mode,
+                disable_web_page_preview=disable_web_page_preview,
+                reply_markup=reply_markup
+            )
+
+    async def edit_message_caption(
+        self,
+        caption: str,
+        parse_mode: Optional["enums.ParseMode"] = None,
+        reply_markup: "types.InlineKeyboardMarkup" = None
+    ) -> Union["types.Message", bool]:
+        """Edit the caption of media messages attached to callback queries.
+
+        Bound method *edit_message_caption* of :obj:`~pyrogram.types.CallbackQuery`.
+
+        Parameters:
+            caption (``str``):
+                New caption of the message.
+
+            parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+                By default, texts are parsed using both Markdown and HTML styles.
+                You can combine both syntaxes together.
+
+            reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*):
+                An InlineKeyboardMarkup object.
+
+        Returns:
+            :obj:`~pyrogram.types.Message` | ``bool``: On success, if the edited message was sent by the bot, the edited
+            message is returned, otherwise True is returned (message sent via the bot, as inline query result).
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+        return await self.edit_message_text(caption, parse_mode, reply_markup=reply_markup)
+
+    async def edit_message_media(
+        self,
+        media: "types.InputMedia",
+        reply_markup: "types.InlineKeyboardMarkup" = None
+    ) -> Union["types.Message", bool]:
+        """Edit animation, audio, document, photo or video messages attached to callback queries.
+
+        Bound method *edit_message_media* of :obj:`~pyrogram.types.CallbackQuery`.
+
+        Parameters:
+            media (:obj:`~pyrogram.types.InputMedia`):
+                One of the InputMedia objects describing an animation, audio, document, photo or video.
+
+            reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*):
+                An InlineKeyboardMarkup object.
+
+        Returns:
+            :obj:`~pyrogram.types.Message` | ``bool``: On success, if the edited message was sent by the bot, the edited
+            message is returned, otherwise True is returned (message sent via the bot, as inline query result).
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+        if self.inline_message_id is None:
+            return await self._client.edit_message_media(
+                chat_id=self.message.chat.id,
+                message_id=self.message.id,
+                media=media,
+                reply_markup=reply_markup
+            )
+        else:
+            return await self._client.edit_inline_media(
+                inline_message_id=self.inline_message_id,
+                media=media,
+                reply_markup=reply_markup
+            )
+
+    async def edit_message_reply_markup(
+        self,
+        reply_markup: "types.InlineKeyboardMarkup" = None
+    ) -> Union["types.Message", bool]:
+        """Edit only the reply markup of messages attached to callback queries.
+
+        Bound method *edit_message_reply_markup* of :obj:`~pyrogram.types.CallbackQuery`.
+
+        Parameters:
+            reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`):
+                An InlineKeyboardMarkup object.
+
+        Returns:
+            :obj:`~pyrogram.types.Message` | ``bool``: On success, if the edited message was sent by the bot, the edited
+            message is returned, otherwise True is returned (message sent via the bot, as inline query result).
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+        if self.inline_message_id is None:
+            return await self._client.edit_message_reply_markup(
+                chat_id=self.message.chat.id,
+                message_id=self.message.id,
+                reply_markup=reply_markup
+            )
+        else:
+            return await self._client.edit_inline_reply_markup(
+                inline_message_id=self.inline_message_id,
+                reply_markup=reply_markup
+            )
diff --git a/pyrogram/types/bots_and_keyboards/force_reply.py b/pyrogram/types/bots_and_keyboards/force_reply.py
new file mode 100644
index 00000000000..4cb137d8457
--- /dev/null
+++ b/pyrogram/types/bots_and_keyboards/force_reply.py
@@ -0,0 +1,66 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import pyrogram
+from pyrogram import raw
+
+from ..object import Object
+
+
+class ForceReply(Object):
+    """Object used to force clients to show a reply interface.
+
+    Upon receiving a message with this object, Telegram clients will display a reply interface to the user.
+
+    This acts as if the user has selected the bot's message and tapped "Reply".
+    This can be extremely useful if you want to create user-friendly step-by-step interfaces without having to
+    sacrifice privacy mode.
+
+    Parameters:
+        selective (``bool``, *optional*):
+            Use this parameter if you want to force reply from specific users only. Targets:
+            1) users that are @mentioned in the text of the Message object;
+            2) if the bot's message is a reply (has reply_to_message_id), sender of the original message.
+
+        placeholder (``str``, *optional*):
+            The placeholder to be shown in the input field when the reply is active; 1-64 characters.
+    """
+
+    def __init__(
+        self,
+        selective: bool = None,
+        placeholder: str = None
+    ):
+        super().__init__()
+
+        self.selective = selective
+        self.placeholder = placeholder
+
+    @staticmethod
+    def read(b):
+        return ForceReply(
+            selective=b.selective,
+            placeholder=b.placeholder
+        )
+
+    async def write(self, _: "pyrogram.Client"):
+        return raw.types.ReplyKeyboardForceReply(
+            single_use=True,
+            selective=self.selective or None,
+            placeholder=self.placeholder or None
+        )
diff --git a/pyrogram/types/bots_and_keyboards/game_high_score.py b/pyrogram/types/bots_and_keyboards/game_high_score.py
new file mode 100644
index 00000000000..295b83fb231
--- /dev/null
+++ b/pyrogram/types/bots_and_keyboards/game_high_score.py
@@ -0,0 +1,70 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import pyrogram
+from pyrogram import raw, utils
+from pyrogram import types
+from ..object import Object
+
+
+class GameHighScore(Object):
+    """One row of the high scores table for a game.
+
+    Parameters:
+        user (:obj:`~pyrogram.types.User`):
+            User.
+
+        score (``int``):
+            Score.
+
+        position (``int``, *optional*):
+            Position in high score table for the game.
+    """
+
+    def __init__(
+        self,
+        *,
+        client: "pyrogram.Client" = None,
+        user: "types.User",
+        score: int,
+        position: int = None
+    ):
+        super().__init__(client)
+
+        self.user = user
+        self.score = score
+        self.position = position
+
+    @staticmethod
+    def _parse(client, game_high_score: raw.types.HighScore, users: dict) -> "GameHighScore":
+        users = {i.id: i for i in users}
+
+        return GameHighScore(
+            user=types.User._parse(client, users[game_high_score.user_id]),
+            score=game_high_score.score,
+            position=game_high_score.pos,
+            client=client
+        )
+
+    @staticmethod
+    def _parse_action(client, service: raw.types.MessageService, users: dict):
+        return GameHighScore(
+            user=types.User._parse(client, users[utils.get_raw_peer_id(service.from_id or service.peer_id)]),
+            score=service.action.score,
+            client=client
+        )
diff --git a/pyrogram/types/bots_and_keyboards/inline_keyboard_button.py b/pyrogram/types/bots_and_keyboards/inline_keyboard_button.py
new file mode 100644
index 00000000000..a1d8a7adc89
--- /dev/null
+++ b/pyrogram/types/bots_and_keyboards/inline_keyboard_button.py
@@ -0,0 +1,208 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import Union
+
+import pyrogram
+from pyrogram import raw
+from pyrogram import types
+from ..object import Object
+
+
+class InlineKeyboardButton(Object):
+    """One button of an inline keyboard.
+
+    You must use exactly one of the optional fields.
+
+    Parameters:
+        text (``str``):
+            Label text on the button.
+
+        callback_data (``str`` | ``bytes``, *optional*):
+            Data to be sent in a callback query to the bot when button is pressed, 1-64 bytes.
+
+        url (``str``, *optional*):
+            HTTP url to be opened when button is pressed.
+
+        web_app (:obj:`~pyrogram.types.WebAppInfo`, *optional*):
+            Description of the `Web App `_ that will be launched when the user
+            presses the button. The Web App will be able to send an arbitrary message on behalf of the user using the
+            method :meth:`~pyrogram.Client.answer_web_app_query`. Available only in private chats between a user and the
+            bot.
+
+        login_url (:obj:`~pyrogram.types.LoginUrl`, *optional*):
+             An HTTP URL used to automatically authorize the user. Can be used as a replacement for
+             the `Telegram Login Widget `_.
+
+        user_id (``int``, *optional*):
+            User id, for links to the user profile.
+
+        switch_inline_query (``str``, *optional*):
+            If set, pressing the button will prompt the user to select one of their chats, open that chat and insert
+            the bot's username and the specified inline query in the input field. Can be empty, in which case just
+            the bot's username will be inserted.Note: This offers an easy way for users to start using your bot in
+            inline mode when they are currently in a private chat with it. Especially useful when combined with
+            switch_pm… actions – in this case the user will be automatically returned to the chat they switched from,
+            skipping the chat selection screen.
+
+        switch_inline_query_current_chat (``str``, *optional*):
+            If set, pressing the button will insert the bot's username and the specified inline query in the current
+            chat's input field. Can be empty, in which case only the bot's username will be inserted.This offers a
+            quick way for the user to open your bot in inline mode in the same chat – good for selecting something
+            from multiple options.
+
+        callback_game (:obj:`~pyrogram.types.CallbackGame`, *optional*):
+            Description of the game that will be launched when the user presses the button.
+            **NOTE**: This type of button **must** always be the first button in the first row.
+    """
+
+    def __init__(
+        self,
+        text: str,
+        callback_data: Union[str, bytes] = None,
+        url: str = None,
+        web_app: "types.WebAppInfo" = None,
+        login_url: "types.LoginUrl" = None,
+        user_id: int = None,
+        switch_inline_query: str = None,
+        switch_inline_query_current_chat: str = None,
+        callback_game: "types.CallbackGame" = None
+    ):
+        super().__init__()
+
+        self.text = str(text)
+        self.callback_data = callback_data
+        self.url = url
+        self.web_app = web_app
+        self.login_url = login_url
+        self.user_id = user_id
+        self.switch_inline_query = switch_inline_query
+        self.switch_inline_query_current_chat = switch_inline_query_current_chat
+        self.callback_game = callback_game
+        # self.pay = pay
+
+    @staticmethod
+    def read(b: "raw.base.KeyboardButton"):
+        if isinstance(b, raw.types.KeyboardButtonCallback):
+            # Try decode data to keep it as string, but if fails, fallback to bytes so we don't lose any information,
+            # instead of decoding by ignoring/replacing errors.
+            try:
+                data = b.data.decode()
+            except UnicodeDecodeError:
+                data = b.data
+
+            return InlineKeyboardButton(
+                text=b.text,
+                callback_data=data
+            )
+
+        if isinstance(b, raw.types.KeyboardButtonUrl):
+            return InlineKeyboardButton(
+                text=b.text,
+                url=b.url
+            )
+
+        if isinstance(b, raw.types.KeyboardButtonUrlAuth):
+            return InlineKeyboardButton(
+                text=b.text,
+                login_url=types.LoginUrl.read(b)
+            )
+
+        if isinstance(b, raw.types.KeyboardButtonUserProfile):
+            return InlineKeyboardButton(
+                text=b.text,
+                user_id=b.user_id
+            )
+
+        if isinstance(b, raw.types.KeyboardButtonSwitchInline):
+            if b.same_peer:
+                return InlineKeyboardButton(
+                    text=b.text,
+                    switch_inline_query_current_chat=b.query
+                )
+            else:
+                return InlineKeyboardButton(
+                    text=b.text,
+                    switch_inline_query=b.query
+                )
+
+        if isinstance(b, raw.types.KeyboardButtonGame):
+            return InlineKeyboardButton(
+                text=b.text,
+                callback_game=types.CallbackGame()
+            )
+
+        if isinstance(b, raw.types.KeyboardButtonWebView):
+            return InlineKeyboardButton(
+                text=b.text,
+                web_app=types.WebAppInfo(
+                    url=b.url
+                )
+            )
+
+    async def write(self, client: "pyrogram.Client"):
+        if self.callback_data is not None:
+            # Telegram only wants bytes, but we are allowed to pass strings too, for convenience.
+            data = bytes(self.callback_data, "utf-8") if isinstance(self.callback_data, str) else self.callback_data
+
+            return raw.types.KeyboardButtonCallback(
+                text=self.text,
+                data=data
+            )
+
+        if self.url is not None:
+            return raw.types.KeyboardButtonUrl(
+                text=self.text,
+                url=self.url
+            )
+
+        if self.login_url is not None:
+            return self.login_url.write(
+                text=self.text,
+                bot=await client.resolve_peer(self.login_url.bot_username or "self")
+            )
+
+        if self.user_id is not None:
+            return raw.types.InputKeyboardButtonUserProfile(
+                text=self.text,
+                user_id=await client.resolve_peer(self.user_id)
+            )
+
+        if self.switch_inline_query is not None:
+            return raw.types.KeyboardButtonSwitchInline(
+                text=self.text,
+                query=self.switch_inline_query
+            )
+
+        if self.switch_inline_query_current_chat is not None:
+            return raw.types.KeyboardButtonSwitchInline(
+                text=self.text,
+                query=self.switch_inline_query_current_chat,
+                same_peer=True
+            )
+
+        if self.callback_game is not None:
+            return raw.types.KeyboardButtonGame(
+                text=self.text
+            )
+
+        if self.web_app is not None:
+            return raw.types.KeyboardButtonWebView(
+                text=self.text,
+                url=self.web_app.url
+            )
diff --git a/pyrogram/types/bots_and_keyboards/inline_keyboard_markup.py b/pyrogram/types/bots_and_keyboards/inline_keyboard_markup.py
new file mode 100644
index 00000000000..e0fd3230591
--- /dev/null
+++ b/pyrogram/types/bots_and_keyboards/inline_keyboard_markup.py
@@ -0,0 +1,76 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import List
+
+import pyrogram
+from pyrogram import raw
+from pyrogram import types
+from ..object import Object
+
+
+class InlineKeyboardMarkup(Object):
+    """An inline keyboard that appears right next to the message it belongs to.
+
+    Parameters:
+        inline_keyboard (List of List of :obj:`~pyrogram.types.InlineKeyboardButton`):
+            List of button rows, each represented by a List of InlineKeyboardButton objects.
+    """
+
+    def __init__(self, inline_keyboard: List[List["types.InlineKeyboardButton"]]):
+        super().__init__()
+
+        self.inline_keyboard = inline_keyboard
+
+    @staticmethod
+    def read(o):
+        inline_keyboard = []
+
+        for i in o.rows:
+            row = []
+
+            for j in i.buttons:
+                row.append(types.InlineKeyboardButton.read(j))
+
+            inline_keyboard.append(row)
+
+        return InlineKeyboardMarkup(
+            inline_keyboard=inline_keyboard
+        )
+
+    async def write(self, client: "pyrogram.Client"):
+        rows = []
+
+        for r in self.inline_keyboard:
+            buttons = []
+
+            for b in r:
+                buttons.append(await b.write(client))
+
+            rows.append(raw.types.KeyboardButtonRow(buttons=buttons))
+
+        return raw.types.ReplyInlineMarkup(rows=rows)
+
+        # There seems to be a Python issues with nested async comprehensions.
+        # See: https://bugs.python.org/issue33346
+        #
+        # return raw.types.ReplyInlineMarkup(
+        #     rows=[raw.types.KeyboardButtonRow(
+        #         buttons=[await j.write(client) for j in i]
+        #     ) for i in self.inline_keyboard]
+        # )
diff --git a/pyrogram/types/bots_and_keyboards/keyboard_button.py b/pyrogram/types/bots_and_keyboards/keyboard_button.py
new file mode 100644
index 00000000000..5c8d4b733cf
--- /dev/null
+++ b/pyrogram/types/bots_and_keyboards/keyboard_button.py
@@ -0,0 +1,95 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 pyrogram import raw, types
+from ..object import Object
+
+
+class KeyboardButton(Object):
+    """One button of the reply keyboard.
+    For simple text buttons String can be used instead of this object to specify text of the button.
+    Optional fields are mutually exclusive.
+
+    Parameters:
+        text (``str``):
+            Text of the button. If none of the optional fields are used, it will be sent as a message when
+            the button is pressed.
+
+        request_contact (``bool``, *optional*):
+            If True, the user's phone number will be sent as a contact when the button is pressed.
+            Available in private chats only.
+
+        request_location (``bool``, *optional*):
+            If True, the user's current location will be sent when the button is pressed.
+            Available in private chats only.
+
+        web_app (:obj:`~pyrogram.types.WebAppInfo`, *optional*):
+            If specified, the described `Web App `_ will be launched when the
+            button is pressed. The Web App will be able to send a “web_app_data” service message. Available in private
+            chats only.
+
+    """
+
+    def __init__(
+        self,
+        text: str,
+        request_contact: bool = None,
+        request_location: bool = None,
+        web_app: "types.WebAppInfo" = None
+    ):
+        super().__init__()
+
+        self.text = str(text)
+        self.request_contact = request_contact
+        self.request_location = request_location
+        self.web_app = web_app
+
+    @staticmethod
+    def read(b):
+        if isinstance(b, raw.types.KeyboardButton):
+            return b.text
+
+        if isinstance(b, raw.types.KeyboardButtonRequestPhone):
+            return KeyboardButton(
+                text=b.text,
+                request_contact=True
+            )
+
+        if isinstance(b, raw.types.KeyboardButtonRequestGeoLocation):
+            return KeyboardButton(
+                text=b.text,
+                request_location=True
+            )
+
+        if isinstance(b, raw.types.KeyboardButtonSimpleWebView):
+            return KeyboardButton(
+                text=b.text,
+                web_app=types.WebAppInfo(
+                    url=b.url
+                )
+            )
+
+    def write(self):
+        if self.request_contact:
+            return raw.types.KeyboardButtonRequestPhone(text=self.text)
+        elif self.request_location:
+            return raw.types.KeyboardButtonRequestGeoLocation(text=self.text)
+        elif self.web_app:
+            return raw.types.KeyboardButtonSimpleWebView(text=self.text, url=self.web_app.url)
+        else:
+            return raw.types.KeyboardButton(text=self.text)
diff --git a/pyrogram/types/bots_and_keyboards/login_url.py b/pyrogram/types/bots_and_keyboards/login_url.py
new file mode 100644
index 00000000000..a0af5a1af01
--- /dev/null
+++ b/pyrogram/types/bots_and_keyboards/login_url.py
@@ -0,0 +1,90 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 pyrogram import raw
+
+from ..object import Object
+
+
+class LoginUrl(Object):
+    """Represents a parameter of the inline keyboard button used to automatically authorize a user.
+
+    Serves as a great replacement for the Telegram Login Widget when the user is coming from Telegram.
+    All the user needs to do is tap/click a button and confirm that they want to log in.
+
+    Parameters:
+        url (``str``):
+            An HTTP URL to be opened with user authorization data added to the query string when the button is pressed.
+            If the user refuses to provide authorization data, the original URL without information about the user will
+            be opened. The data added is the same as described in
+            `Receiving authorization data `.
+
+            **NOTE**: You **must** always check the hash of the received data to verify the authentication and the
+            integrity of the data as described in
+            `Checking authorization `_.
+
+        forward_text (``str``, *optional*):
+            New text of the button in forwarded messages.
+
+        bot_username (``str``, *optional*):
+            Username of a bot, which will be used for user authorization.
+            See `Setting up a bot `_ for more details.
+            If not specified, the current bot's username will be assumed. The url's domain must be the same as the
+            domain linked with the bot.
+            See `Linking your domain to the bot `_
+            for more details.
+
+        request_write_access (``str``, *optional*):
+            Pass True to request the permission for your bot to send messages to the user.
+
+        button_id (``int``):
+            Button identifier.
+    """
+
+    def __init__(
+        self, *,
+        url: str,
+        forward_text: str = None,
+        bot_username: str = None,
+        request_write_access: str = None,
+        button_id: int = None
+    ):
+        super().__init__()
+
+        self.url = url
+        self.forward_text = forward_text
+        self.bot_username = bot_username
+        self.request_write_access = request_write_access
+        self.button_id = button_id
+
+    @staticmethod
+    def read(b: "raw.types.KeyboardButtonUrlAuth") -> "LoginUrl":
+        return LoginUrl(
+            url=b.url,
+            forward_text=b.fwd_text,
+            button_id=b.button_id
+        )
+
+    def write(self, text: str, bot: "raw.types.InputUser"):
+        return raw.types.InputKeyboardButtonUrlAuth(
+            text=text,
+            url=self.url,
+            bot=bot,
+            fwd_text=self.forward_text,
+            request_write_access=self.request_write_access
+        )
diff --git a/pyrogram/types/bots_and_keyboards/menu_button.py b/pyrogram/types/bots_and_keyboards/menu_button.py
new file mode 100644
index 00000000000..e61e7baa29a
--- /dev/null
+++ b/pyrogram/types/bots_and_keyboards/menu_button.py
@@ -0,0 +1,44 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import pyrogram
+from pyrogram import raw
+from ..object import Object
+
+
+class MenuButton(Object):
+    """Describes the bot's menu button in a private chat.
+
+    It should be one of:
+
+    - :obj:`~pyrogram.types.MenuButtonCommands`
+    - :obj:`~pyrogram.types.MenuButtonWebApp`
+    - :obj:`~pyrogram.types.MenuButtonDefault`
+
+    If a menu button other than :obj:`~pyrogram.types.MenuButtonDefault` is set for a private chat, then it is applied
+    in the chat. Otherwise the default menu button is applied. By default, the menu button opens the list of bot
+    commands.
+    """
+
+    def __init__(self, type: str):
+        super().__init__()
+
+        self.type = type
+
+    async def write(self, client: "pyrogram.Client") -> "raw.base.BotMenuButton":
+        raise NotImplementedError
diff --git a/pyrogram/types/bots_and_keyboards/menu_button_commands.py b/pyrogram/types/bots_and_keyboards/menu_button_commands.py
new file mode 100644
index 00000000000..b2ef77c9deb
--- /dev/null
+++ b/pyrogram/types/bots_and_keyboards/menu_button_commands.py
@@ -0,0 +1,32 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import pyrogram
+from pyrogram import raw
+from .menu_button import MenuButton
+
+
+class MenuButtonCommands(MenuButton):
+    """A menu button, which opens the bot's list of commands.
+    """
+
+    def __init__(self):
+        super().__init__("commands")
+
+    async def write(self, client: "pyrogram.Client") -> "raw.types.BotMenuButtonCommands":
+        return raw.types.BotMenuButtonCommands()
diff --git a/pyrogram/types/bots_and_keyboards/menu_button_default.py b/pyrogram/types/bots_and_keyboards/menu_button_default.py
new file mode 100644
index 00000000000..a00e67633ed
--- /dev/null
+++ b/pyrogram/types/bots_and_keyboards/menu_button_default.py
@@ -0,0 +1,32 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import pyrogram
+from pyrogram import raw
+from .menu_button import MenuButton
+
+
+class MenuButtonDefault(MenuButton):
+    """Describes that no specific value for the menu button was set.
+    """
+
+    def __init__(self):
+        super().__init__("default")
+
+    async def write(self, client: "pyrogram.Client") -> "raw.types.BotMenuButtonDefault":
+        return raw.types.BotMenuButtonDefault()
diff --git a/pyrogram/types/bots_and_keyboards/menu_button_web_app.py b/pyrogram/types/bots_and_keyboards/menu_button_web_app.py
new file mode 100644
index 00000000000..109088bbcf9
--- /dev/null
+++ b/pyrogram/types/bots_and_keyboards/menu_button_web_app.py
@@ -0,0 +1,51 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import pyrogram
+from pyrogram import raw, types
+from .menu_button import MenuButton
+
+
+class MenuButtonWebApp(MenuButton):
+    """A menu button, which launches a `Web App `_.
+
+    Parameters:
+        text (``str``):
+            Text on the button
+
+        web_app (:obj:`~pyrogram.types.WebAppInfo`):
+            Description of the Web App that will be launched when the user presses the button.
+            The Web App will be able to send an arbitrary message on behalf of the user using the method
+            :meth:`~pyrogram.Client.answer_web_app_query`.
+    """
+
+    def __init__(
+        self,
+        text: str,
+        web_app: "types.WebAppInfo"
+    ):
+        super().__init__("web_app")
+
+        self.text = text
+        self.web_app = web_app
+
+    async def write(self, client: "pyrogram.Client") -> "raw.types.BotMenuButton":
+        return raw.types.BotMenuButton(
+            text=self.text,
+            url=self.web_app.url
+        )
diff --git a/pyrogram/types/bots_and_keyboards/reply_keyboard_markup.py b/pyrogram/types/bots_and_keyboards/reply_keyboard_markup.py
new file mode 100644
index 00000000000..2949c3e2066
--- /dev/null
+++ b/pyrogram/types/bots_and_keyboards/reply_keyboard_markup.py
@@ -0,0 +1,112 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import List, Union
+
+import pyrogram
+from pyrogram import raw
+from pyrogram import types
+from ..object import Object
+
+
+class ReplyKeyboardMarkup(Object):
+    """A custom keyboard with reply options.
+
+    Parameters:
+        keyboard (List of List of :obj:`~pyrogram.types.KeyboardButton`):
+            List of button rows, each represented by a List of KeyboardButton objects.
+
+        is_persistent (``bool``, *optional*):
+            Requests clients to always show the keyboard when the regular keyboard is hidden.
+            Defaults to false, in which case the custom keyboard can be hidden and opened with a keyboard icon.
+
+        resize_keyboard (``bool``, *optional*):
+            Requests clients to resize the keyboard vertically for optimal fit (e.g., make the keyboard smaller if
+            there are just two rows of buttons). Defaults to false, in which case the custom keyboard is always of
+            the same height as the app's standard keyboard.
+
+        one_time_keyboard (``bool``, *optional*):
+            Requests clients to hide the keyboard as soon as it's been used. The keyboard will still be available,
+            but clients will automatically display the usual letter-keyboard in the chat – the user can press a
+            special button in the input field to see the custom keyboard again. Defaults to false.
+
+        selective (``bool``, *optional*):
+            Use this parameter if you want to show the keyboard to specific users only. Targets:
+            1) users that are @mentioned in the text of the Message object;
+            2) if the bot's message is a reply (has reply_to_message_id), sender of the original message.
+            Example: A user requests to change the bot's language, bot replies to the request with a keyboard to
+            select the new language. Other users in the group don't see the keyboard.
+
+        placeholder (``str``, *optional*):
+            The placeholder to be shown in the input field when the keyboard is active; 1-64 characters.
+    """
+
+    def __init__(
+        self,
+        keyboard: List[List[Union["types.KeyboardButton", str]]],
+        is_persistent: bool = None,
+        resize_keyboard: bool = None,
+        one_time_keyboard: bool = None,
+        selective: bool = None,
+        placeholder: str = None
+    ):
+        super().__init__()
+
+        self.keyboard = keyboard
+        self.is_persistent = is_persistent
+        self.resize_keyboard = resize_keyboard
+        self.one_time_keyboard = one_time_keyboard
+        self.selective = selective
+        self.placeholder = placeholder
+
+    @staticmethod
+    def read(kb: "raw.base.ReplyMarkup"):
+        keyboard = []
+
+        for i in kb.rows:
+            row = []
+
+            for j in i.buttons:
+                row.append(types.KeyboardButton.read(j))
+
+            keyboard.append(row)
+
+        return ReplyKeyboardMarkup(
+            keyboard=keyboard,
+            is_persistent=kb.persistent,
+            resize_keyboard=kb.resize,
+            one_time_keyboard=kb.single_use,
+            selective=kb.selective,
+            placeholder=kb.placeholder
+        )
+
+    async def write(self, _: "pyrogram.Client"):
+        return raw.types.ReplyKeyboardMarkup(
+            rows=[raw.types.KeyboardButtonRow(
+                buttons=[
+                    types.KeyboardButton(j).write()
+                    if isinstance(j, str) else j.write()
+                    for j in i
+                ]
+            ) for i in self.keyboard],
+            resize=self.resize_keyboard or None,
+            single_use=self.one_time_keyboard or None,
+            selective=self.selective or None,
+            persistent=self.is_persistent or None,
+            placeholder=self.placeholder or None
+        )
diff --git a/pyrogram/types/bots_and_keyboards/reply_keyboard_remove.py b/pyrogram/types/bots_and_keyboards/reply_keyboard_remove.py
new file mode 100644
index 00000000000..479efe90278
--- /dev/null
+++ b/pyrogram/types/bots_and_keyboards/reply_keyboard_remove.py
@@ -0,0 +1,58 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import pyrogram
+from pyrogram import raw
+from ..object import Object
+
+
+class ReplyKeyboardRemove(Object):
+    """Object used to tell clients to remove a bot keyboard.
+
+    Upon receiving a message with this object, Telegram clients will remove the current custom keyboard and display
+    the default letter-keyboard. By default, custom keyboards are displayed until a new keyboard is sent by a bot.
+    An exception is made for one-time keyboards that are hidden immediately after the user presses a button
+    (see ReplyKeyboardMarkup).
+
+    Parameters:
+        selective (``bool``, *optional*):
+            Use this parameter if you want to remove the keyboard for specific users only. Targets:
+            1) users that are @mentioned in the text of the Message object;
+            2) if the bot's message is a reply (has reply_to_message_id), sender of the original message.
+            Example: A user votes in a poll, bot returns confirmation message in reply to the vote and removes the
+            keyboard for that user, while still showing the keyboard with poll options to users who haven't voted yet.
+    """
+
+    def __init__(
+        self,
+        selective: bool = None
+    ):
+        super().__init__()
+
+        self.selective = selective
+
+    @staticmethod
+    def read(b):
+        return ReplyKeyboardRemove(
+            selective=b.selective
+        )
+
+    async def write(self, _: "pyrogram.Client"):
+        return raw.types.ReplyKeyboardHide(
+            selective=self.selective or None
+        )
diff --git a/pyrogram/types/bots_and_keyboards/sent_web_app_message.py b/pyrogram/types/bots_and_keyboards/sent_web_app_message.py
new file mode 100644
index 00000000000..8b1df197374
--- /dev/null
+++ b/pyrogram/types/bots_and_keyboards/sent_web_app_message.py
@@ -0,0 +1,42 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 pyrogram import raw, utils
+from ..object import Object
+
+
+class SentWebAppMessage(Object):
+    """Contains information about an inline message sent by a `Web App `_ on behalf of a user.
+
+    Parameters:
+        inline_message_id (``str``):
+            Identifier of the sent inline message.
+            Available only if there is an inline keyboard attached to the message.
+    """
+
+    def __init__(
+        self, *,
+        inline_message_id: str,
+    ):
+        super().__init__()
+
+        self.inline_message_id = inline_message_id
+
+    @staticmethod
+    def _parse(obj: "raw.types.WebViewMessageSent"):
+        return SentWebAppMessage(inline_message_id=utils.pack_inline_message_id(obj.msg_id))
diff --git a/pyrogram/types/bots_and_keyboards/web_app_info.py b/pyrogram/types/bots_and_keyboards/web_app_info.py
new file mode 100644
index 00000000000..1dbb0a781c9
--- /dev/null
+++ b/pyrogram/types/bots_and_keyboards/web_app_info.py
@@ -0,0 +1,37 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 ..object import Object
+
+
+class WebAppInfo(Object):
+    """Contains information about a `Web App `_.
+
+    Parameters:
+        url (``str``):
+            An HTTPS URL of a Web App to be opened with additional data as specified in
+            `Initializing Web Apps `_.
+    """
+
+    def __init__(
+        self, *,
+        url: str,
+    ):
+        super().__init__()
+
+        self.url = url
diff --git a/pyrogram/types/inline_mode/__init__.py b/pyrogram/types/inline_mode/__init__.py
new file mode 100644
index 00000000000..f7323abf956
--- /dev/null
+++ b/pyrogram/types/inline_mode/__init__.py
@@ -0,0 +1,47 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .chosen_inline_result import ChosenInlineResult
+from .inline_query import InlineQuery
+from .inline_query_result import InlineQueryResult
+from .inline_query_result_animation import InlineQueryResultAnimation
+from .inline_query_result_article import InlineQueryResultArticle
+from .inline_query_result_audio import InlineQueryResultAudio
+from .inline_query_result_cached_animation import InlineQueryResultCachedAnimation
+from .inline_query_result_cached_document import InlineQueryResultCachedDocument
+from .inline_query_result_cached_photo import InlineQueryResultCachedPhoto
+from .inline_query_result_cached_sticker import InlineQueryResultCachedSticker
+from .inline_query_result_cached_video import InlineQueryResultCachedVideo
+from .inline_query_result_cached_voice import InlineQueryResultCachedVoice
+from .inline_query_result_contact import InlineQueryResultContact
+from .inline_query_result_document import InlineQueryResultDocument
+from .inline_query_result_location import InlineQueryResultLocation
+from .inline_query_result_photo import InlineQueryResultPhoto
+from .inline_query_result_venue import InlineQueryResultVenue
+from .inline_query_result_video import InlineQueryResultVideo
+from .inline_query_result_voice import InlineQueryResultVoice
+from .inline_query_result_cached_audio import InlineQueryResultCachedAudio
+
+__all__ = [
+    "InlineQuery", "InlineQueryResult", "InlineQueryResultArticle", "InlineQueryResultPhoto",
+    "InlineQueryResultAnimation", "InlineQueryResultAudio", "InlineQueryResultVideo", "ChosenInlineResult",
+    "InlineQueryResultContact", "InlineQueryResultDocument", "InlineQueryResultVoice", "InlineQueryResultLocation",
+    "InlineQueryResultVenue", "InlineQueryResultCachedPhoto", "InlineQueryResultCachedAnimation",
+    "InlineQueryResultCachedSticker", "InlineQueryResultCachedDocument", "InlineQueryResultCachedVideo",
+    "InlineQueryResultCachedVoice", "InlineQueryResultCachedAudio"
+]
diff --git a/pyrogram/types/inline_mode/chosen_inline_result.py b/pyrogram/types/inline_mode/chosen_inline_result.py
new file mode 100644
index 00000000000..623d7379330
--- /dev/null
+++ b/pyrogram/types/inline_mode/chosen_inline_result.py
@@ -0,0 +1,99 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 base64 import b64encode
+from struct import pack
+
+import pyrogram
+from pyrogram import raw
+from pyrogram import types
+from ..object import Object
+from ..update import Update
+
+
+class ChosenInlineResult(Object, Update):
+    """A :doc:`result ` of an inline query chosen by the user and sent to their chat partner.
+
+    .. note::
+
+        In order to receive these updates, your bot must have "inline feedback" enabled. You can enable this feature
+        with `@BotFather `_.
+
+    Parameters:
+        result_id (``str``):
+            The unique identifier for the result that was chosen.
+
+        from_user (:obj:`~pyrogram.types.User`):
+            The user that chose the result.
+
+        query (``str``):
+            The query that was used to obtain the result.
+
+        location (:obj:`~pyrogram.types.Location`, *optional*):
+            Sender location, only for bots that require user location.
+
+        inline_message_id (``str``, *optional*):
+            Identifier of the sent inline message.
+            Available only if there is an :doc:`inline keyboard ` attached to the message.
+            Will be also received in :doc:`callback queries ` and can be used to edit the message.
+    """
+
+    def __init__(
+        self,
+        *,
+        client: "pyrogram.Client" = None,
+        result_id: str,
+        from_user: "types.User",
+        query: str,
+        location: "types.Location" = None,
+        inline_message_id: str = None
+    ):
+        super().__init__(client)
+
+        self.result_id = result_id
+        self.from_user = from_user
+        self.query = query
+        self.location = location
+        self.inline_message_id = inline_message_id
+
+    @staticmethod
+    def _parse(client, chosen_inline_result: raw.types.UpdateBotInlineSend, users) -> "ChosenInlineResult":
+        inline_message_id = None
+
+        if isinstance(chosen_inline_result.msg_id, raw.types.InputBotInlineMessageID):
+            inline_message_id = b64encode(
+                pack(
+                    "`_ that match
+            the query of this inline query. Only applicable when using :obj:`Filters.regex `.
     """
-    __slots__ = ["id", "from_user", "query", "offset", "location"]
 
     def __init__(
         self,
         *,
-        client: "pyrogram.client.ext.BaseClient",
+        client: "pyrogram.Client" = None,
         id: str,
-        from_user: User,
+        from_user: "types.User",
         query: str,
         offset: str,
-        location: Location = None
+        chat_type: "enums.ChatType",
+        location: "types.Location" = None,
+        matches: List[Match] = None
     ):
         super().__init__(client)
 
-        self._client = client
         self.id = id
         self.from_user = from_user
         self.query = query
         self.offset = offset
+        self.chat_type = chat_type
         self.location = location
+        self.matches = matches
 
     @staticmethod
-    def _parse(client, inline_query: types.UpdateBotInlineQuery, users: dict) -> "InlineQuery":
+    def _parse(client, inline_query: raw.types.UpdateBotInlineQuery, users: dict) -> "InlineQuery":
+        peer_type = inline_query.peer_type
+        chat_type = None
+
+        if isinstance(peer_type, raw.types.InlineQueryPeerTypeSameBotPM):
+            chat_type = enums.ChatType.BOT
+        elif isinstance(peer_type, raw.types.InlineQueryPeerTypePM):
+            chat_type = enums.ChatType.PRIVATE
+        elif isinstance(peer_type, raw.types.InlineQueryPeerTypeChat):
+            chat_type = enums.ChatType.GROUP
+        elif isinstance(peer_type, raw.types.InlineQueryPeerTypeMegagroup):
+            chat_type = enums.ChatType.SUPERGROUP
+        elif isinstance(peer_type, raw.types.InlineQueryPeerTypeBroadcast):
+            chat_type = enums.ChatType.CHANNEL
+
         return InlineQuery(
             id=str(inline_query.query_id),
-            from_user=User._parse(client, users[inline_query.user_id]),
+            from_user=types.User._parse(client, users[inline_query.user_id]),
             query=inline_query.query,
             offset=inline_query.offset,
-            location=Location(
+            chat_type=chat_type,
+            location=types.Location(
                 longitude=inline_query.geo.long,
                 latitude=inline_query.geo.lat,
                 client=client
@@ -83,22 +106,23 @@ def _parse(client, inline_query: types.UpdateBotInlineQuery, users: dict) -> "In
             client=client
         )
 
-    def answer(
+    async def answer(
         self,
-        results: List[InlineQueryResult],
+        results: List["types.InlineQueryResult"],
         cache_time: int = 300,
-        is_personal: bool = None,
+        is_gallery: bool = False,
+        is_personal: bool = False,
         next_offset: str = "",
         switch_pm_text: str = "",
         switch_pm_parameter: str = ""
     ):
-        """Bound method *answer* of :obj:`InlineQuery `.
+        """Bound method *answer* of :obj:`~pyrogram.types.InlineQuery`.
 
         Use this method as a shortcut for:
 
         .. code-block:: python
 
-            client.answer_inline_query(
+            await client.answer_inline_query(
                 inline_query.id,
                 results=[...]
             )
@@ -106,19 +130,23 @@ def answer(
         Example:
             .. code-block:: python
 
-                inline_query.answer([...])
+                await inline_query.answer([...])
 
         Parameters:
-            results (List of :obj:`InlineQueryResult `):
+            results (List of :obj:`~pyrogram.types.InlineQueryResult`):
                 A list of results for the inline query.
 
             cache_time (``int``, *optional*):
                 The maximum amount of time in seconds that the result of the inline query may be cached on the server.
                 Defaults to 300.
 
+            is_gallery (``bool``, *optional*):
+                Pass True, if results should be displayed in gallery mode instead of list mode.
+                Defaults to False.
+
             is_personal (``bool``, *optional*):
                 Pass True, if results may be cached on the server side only for the user that sent the query.
-                By default, results may be returned to any user who sends the same query.
+                By default (False), results may be returned to any user who sends the same query.
 
             next_offset (``str``, *optional*):
                 Pass the offset that a client should send in the next query with the same text to receive more results.
@@ -141,10 +169,11 @@ def answer(
                 where they wanted to use the bot's inline capabilities.
         """
 
-        return self._client.answer_inline_query(
+        return await self._client.answer_inline_query(
             inline_query_id=self.id,
             results=results,
             cache_time=cache_time,
+            is_gallery=is_gallery,
             is_personal=is_personal,
             next_offset=next_offset,
             switch_pm_text=switch_pm_text,
diff --git a/pyrogram/types/inline_mode/inline_query_result.py b/pyrogram/types/inline_mode/inline_query_result.py
new file mode 100644
index 00000000000..8548e023c73
--- /dev/null
+++ b/pyrogram/types/inline_mode/inline_query_result.py
@@ -0,0 +1,63 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 uuid import uuid4
+
+import pyrogram
+from pyrogram import types
+from ..object import Object
+
+
+class InlineQueryResult(Object):
+    """One result of an inline query.
+
+    - :obj:`~pyrogram.types.InlineQueryResultCachedAudio`
+    - :obj:`~pyrogram.types.InlineQueryResultCachedDocument`
+    - :obj:`~pyrogram.types.InlineQueryResultCachedAnimation`
+    - :obj:`~pyrogram.types.InlineQueryResultCachedPhoto`
+    - :obj:`~pyrogram.types.InlineQueryResultCachedSticker`
+    - :obj:`~pyrogram.types.InlineQueryResultCachedVideo`
+    - :obj:`~pyrogram.types.InlineQueryResultCachedVoice`
+    - :obj:`~pyrogram.types.InlineQueryResultArticle`
+    - :obj:`~pyrogram.types.InlineQueryResultAudio`
+    - :obj:`~pyrogram.types.InlineQueryResultContact`
+    - :obj:`~pyrogram.types.InlineQueryResultDocument`
+    - :obj:`~pyrogram.types.InlineQueryResultAnimation`
+    - :obj:`~pyrogram.types.InlineQueryResultLocation`
+    - :obj:`~pyrogram.types.InlineQueryResultPhoto`
+    - :obj:`~pyrogram.types.InlineQueryResultVenue`
+    - :obj:`~pyrogram.types.InlineQueryResultVideo`
+    - :obj:`~pyrogram.types.InlineQueryResultVoice`
+    """
+
+    def __init__(
+        self,
+        type: str,
+        id: str,
+        input_message_content: "types.InputMessageContent",
+        reply_markup: "types.InlineKeyboardMarkup"
+    ):
+        super().__init__()
+
+        self.type = type
+        self.id = str(uuid4()) if id is None else str(id)
+        self.input_message_content = input_message_content
+        self.reply_markup = reply_markup
+
+    async def write(self, client: "pyrogram.Client"):
+        pass
diff --git a/pyrogram/types/inline_mode/inline_query_result_animation.py b/pyrogram/types/inline_mode/inline_query_result_animation.py
new file mode 100644
index 00000000000..71d1edf40ae
--- /dev/null
+++ b/pyrogram/types/inline_mode/inline_query_result_animation.py
@@ -0,0 +1,155 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import Optional, List
+
+import pyrogram
+from pyrogram import raw, types, utils, enums
+from .inline_query_result import InlineQueryResult
+
+
+class InlineQueryResultAnimation(InlineQueryResult):
+    """Link to an animated GIF file.
+
+    By default, this animated GIF file will be sent by the user with optional caption.
+    Alternatively, you can use *input_message_content* to send a message with the specified content instead of the
+    animation.
+
+    Parameters:
+        animation_url (``str``):
+            A valid URL for the animated GIF file.
+            File size must not exceed 1 MB.
+
+        animation_width (``int``, *optional*)
+            Width of the animation.
+
+        animation_height (``int``, *optional*)
+            Height of the animation.
+
+        animation_duration (``int``, *optional*)
+            Duration of the animation in seconds.
+
+        thumb_url (``str``, *optional*):
+            URL of the static thumbnail for the result (jpeg or gif)
+            Defaults to the value passed in *animation_url*.
+
+        thumb_mime_type (``str``, *optional*)
+            MIME type of the thumbnail, must be one of "image/jpeg", "image/gif", or "video/mp4".
+            Defaults to "image/jpeg".
+
+        id (``str``, *optional*):
+            Unique identifier for this result, 1-64 bytes.
+            Defaults to a randomly generated UUID4.
+
+        title (``str``, *optional*):
+            Title for the result.
+
+        caption (``str``, *optional*):
+            Caption of the animation to be sent, 0-1024 characters.
+
+        parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+            By default, texts are parsed using both Markdown and HTML styles.
+            You can combine both syntaxes together.
+
+        caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
+            List of special entities that appear in the caption, which can be specified instead of *parse_mode*.
+
+        reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*):
+            An InlineKeyboardMarkup object.
+
+        input_message_content (:obj:`~pyrogram.types.InputMessageContent`):
+            Content of the message to be sent instead of the photo.
+    """
+
+    def __init__(
+        self,
+        animation_url: str,
+        animation_width: int = 0,
+        animation_height: int = 0,
+        animation_duration: int = 0,
+        thumb_url: str = None,
+        thumb_mime_type: str = "image/jpeg",
+        id: str = None,
+        title: str = None,
+        description: str = None,
+        caption: str = "",
+        parse_mode: Optional["enums.ParseMode"] = None,
+        caption_entities: List["types.MessageEntity"] = None,
+        reply_markup: "types.InlineKeyboardMarkup" = None,
+        input_message_content: "types.InputMessageContent" = None
+    ):
+        super().__init__("gif", id, input_message_content, reply_markup)
+
+        self.animation_url = animation_url
+        self.animation_width = animation_width
+        self.animation_height = animation_height
+        self.animation_duration = animation_duration
+        self.thumb_url = thumb_url
+        self.thumb_mime_type = thumb_mime_type
+        self.title = title
+        self.description = description
+        self.caption = caption
+        self.parse_mode = parse_mode
+        self.caption_entities = caption_entities
+        self.reply_markup = reply_markup
+        self.input_message_content = input_message_content
+
+    async def write(self, client: "pyrogram.Client"):
+        animation = raw.types.InputWebDocument(
+            url=self.animation_url,
+            size=0,
+            mime_type="image/gif",
+            attributes=[
+                raw.types.DocumentAttributeVideo(
+                    w=self.animation_width,
+                    h=self.animation_height,
+                    duration=self.animation_duration
+                )
+            ]
+        )
+
+        if self.thumb_url is None:
+            thumb = animation
+        else:
+            thumb = raw.types.InputWebDocument(
+                url=self.thumb_url,
+                size=0,
+                mime_type=self.thumb_mime_type,
+                attributes=[]
+            )
+
+        message, entities = (await utils.parse_text_entities(
+            client, self.caption, self.parse_mode, self.caption_entities
+        )).values()
+
+        return raw.types.InputBotInlineResult(
+            id=self.id,
+            type=self.type,
+            title=self.title,
+            thumb=thumb,
+            content=animation,
+            send_message=(
+                self.input_message_content.write(client, self.reply_markup)
+                if self.input_message_content
+                else raw.types.InputBotInlineMessageMediaAuto(
+                    reply_markup=await self.reply_markup.write(client) if self.reply_markup else None,
+                    message=message,
+                    entities=entities
+                )
+            )
+        )
diff --git a/pyrogram/types/inline_mode/inline_query_result_article.py b/pyrogram/types/inline_mode/inline_query_result_article.py
new file mode 100644
index 00000000000..096273f15bf
--- /dev/null
+++ b/pyrogram/types/inline_mode/inline_query_result_article.py
@@ -0,0 +1,99 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import pyrogram
+from pyrogram import raw
+from pyrogram import types
+
+from .inline_query_result import InlineQueryResult
+
+
+class InlineQueryResultArticle(InlineQueryResult):
+    """Link to an article or web page.
+
+    Parameters:
+        title (``str``):
+            Title for the result.
+
+        input_message_content (:obj:`~pyrogram.types.InputMessageContent`):
+            Content of the message to be sent.
+
+        id (``str``, *optional*):
+            Unique identifier for this result, 1-64 bytes.
+            Defaults to a randomly generated UUID4.
+
+        url (``str``, *optional*):
+            URL of the result.
+
+        description (``str``, *optional*):
+            Short description of the result.
+
+        reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*):
+            Inline keyboard attached to the message.
+
+        thumb_url (``str``, *optional*):
+            Url of the thumbnail for the result.
+
+        thumb_width (``int``, *optional*):
+            Thumbnail width.
+
+        thumb_height (``int``, *optional*):
+            Thumbnail height
+    """
+
+    def __init__(
+        self,
+        title: str,
+        input_message_content: "types.InputMessageContent",
+        id: str = None,
+        url: str = None,
+        description: str = None,
+        reply_markup: "types.InlineKeyboardMarkup" = None,
+        thumb_url: str = None,
+        thumb_width: int = 0,
+        thumb_height: int = 0
+    ):
+        super().__init__("article", id, input_message_content, reply_markup)
+
+        self.title = title
+        self.url = url
+        self.description = description
+        self.thumb_url = thumb_url
+        self.thumb_width = thumb_width
+        self.thumb_height = thumb_height
+
+    async def write(self, client: "pyrogram.Client"):
+        return raw.types.InputBotInlineResult(
+            id=self.id,
+            type=self.type,
+            send_message=await self.input_message_content.write(client, self.reply_markup),
+            title=self.title,
+            description=self.description,
+            url=self.url,
+            thumb=raw.types.InputWebDocument(
+                url=self.thumb_url,
+                size=0,
+                mime_type="image/jpeg",
+                attributes=[
+                    raw.types.DocumentAttributeImageSize(
+                        w=self.thumb_width,
+                        h=self.thumb_height
+                    )
+                ]
+            ) if self.thumb_url else None
+        )
diff --git a/pyrogram/types/inline_mode/inline_query_result_audio.py b/pyrogram/types/inline_mode/inline_query_result_audio.py
new file mode 100644
index 00000000000..a39021000fe
--- /dev/null
+++ b/pyrogram/types/inline_mode/inline_query_result_audio.py
@@ -0,0 +1,120 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import List, Optional
+
+import pyrogram
+from pyrogram import raw, types, utils, enums
+from .inline_query_result import InlineQueryResult
+
+
+class InlineQueryResultAudio(InlineQueryResult):
+    """Link to an audio file.
+    
+    By default, this audio file will be sent by the user with optional caption.
+    Alternatively, you can use *input_message_content* to send a message with the specified content instead of the
+    audio.
+    
+    Parameters:
+        audio_url (``str``):
+            A valid URL for the audio file.
+            
+        title (``str``):
+            Title for the result.
+            
+        id (``str``, *optional*):
+            Unique identifier for this result, 1-64 bytes.
+            Defaults to a randomly generated UUID4.
+            
+        performer (``str``, *optional*):
+            Audio performer.
+            
+        audio_duration (``int``, *optional*):
+            Audio duration in seconds.
+
+        caption (``str``, *optional*):
+            Caption of the audio to be sent, 0-1024 characters.
+            
+        parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+            By default, texts are parsed using both Markdown and HTML styles.
+            You can combine both syntaxes together.
+
+        caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
+            List of special entities that appear in the caption, which can be specified instead of *parse_mode*.
+            
+        reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*):
+            Inline keyboard attached to the message.
+            
+        input_message_content (:obj:`~pyrogram.types.InputMessageContent`, *optional*):
+            Content of the message to be sent instead of the audio.
+    """
+
+    def __init__(
+        self,
+        audio_url: str,
+        title: str,
+        id: str = None,
+        performer: str = "",
+        audio_duration: int = 0,
+        caption: str = "",
+        parse_mode: Optional["enums.ParseMode"] = None,
+        caption_entities: List["types.MessageEntity"] = None,
+        reply_markup: "types.InlineKeyboardMarkup" = None,
+        input_message_content: "types.InputMessageContent" = None
+    ):
+        super().__init__("audio", id, input_message_content, reply_markup)
+
+        self.audio_url = audio_url
+        self.title = title
+        self.performer = performer
+        self.audio_duration = audio_duration
+        self.caption = caption
+        self.parse_mode = parse_mode
+        self.caption_entities = caption_entities
+
+    async def write(self, client: "pyrogram.Client"):
+        audio = raw.types.InputWebDocument(
+            url=self.audio_url,
+            size=0,
+            mime_type="audio/mpeg",
+            attributes=[raw.types.DocumentAttributeAudio(
+                duration=self.audio_duration,
+                title=self.title,
+                performer=self.performer
+            )]
+        )
+
+        message, entities = (await utils.parse_text_entities(
+            client, self.caption, self.parse_mode, self.caption_entities
+        )).values()
+
+        return raw.types.InputBotInlineResult(
+            id=self.id,
+            type=self.type,
+            title=self.title,
+            content=audio,
+            send_message=(
+                await self.input_message_content.write(client, self.reply_markup)
+                if self.input_message_content
+                else raw.types.InputBotInlineMessageMediaAuto(
+                    reply_markup=await self.reply_markup.write(client) if self.reply_markup else None,
+                    message=message,
+                    entities=entities
+                )
+            )
+        )
diff --git a/pyrogram/types/inline_mode/inline_query_result_cached_animation.py b/pyrogram/types/inline_mode/inline_query_result_cached_animation.py
new file mode 100644
index 00000000000..63e58ca027d
--- /dev/null
+++ b/pyrogram/types/inline_mode/inline_query_result_cached_animation.py
@@ -0,0 +1,108 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import Optional, List
+
+import pyrogram
+from pyrogram import raw, types, utils, enums
+from .inline_query_result import InlineQueryResult
+from ...file_id import FileId
+
+
+class InlineQueryResultCachedAnimation(InlineQueryResult):
+    """A link to an animation file stored on the Telegram servers.
+
+    By default, this animation file will be sent by the user with an optional caption.
+    Alternatively, you can use *input_message_content* to send a message with specified content instead of the
+    animation.
+
+    Parameters:
+        animation_file_id (``str``):
+            A valid file identifier for the animation file.
+
+        id (``str``, *optional*):
+            Unique identifier for this result, 1-64 bytes.
+            Defaults to a randomly generated UUID4.
+
+        title (``str``, *optional*):
+            Title for the result.
+
+        caption (``str``, *optional*):
+            Caption of the photo to be sent, 0-1024 characters.
+
+        parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+            By default, texts are parsed using both Markdown and HTML styles.
+            You can combine both syntaxes together.
+
+        caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
+            List of special entities that appear in the caption, which can be specified instead of *parse_mode*.
+
+        reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*):
+            An InlineKeyboardMarkup object.
+
+        input_message_content (:obj:`~pyrogram.types.InputMessageContent`):
+            Content of the message to be sent instead of the photo.
+    """
+
+    def __init__(
+        self,
+        animation_file_id: str,
+        id: str = None,
+        title: str = None,
+        caption: str = "",
+        parse_mode: Optional["enums.ParseMode"] = None,
+        caption_entities: List["types.MessageEntity"] = None,
+        reply_markup: "types.InlineKeyboardMarkup" = None,
+        input_message_content: "types.InputMessageContent" = None
+    ):
+        super().__init__("gif", id, input_message_content, reply_markup)
+
+        self.animation_file_id = animation_file_id
+        self.title = title
+        self.caption = caption
+        self.parse_mode = parse_mode
+        self.caption_entities = caption_entities
+        self.reply_markup = reply_markup
+        self.input_message_content = input_message_content
+
+    async def write(self, client: "pyrogram.Client"):
+        message, entities = (await utils.parse_text_entities(
+            client, self.caption, self.parse_mode, self.caption_entities
+        )).values()
+
+        file_id = FileId.decode(self.animation_file_id)
+
+        return raw.types.InputBotInlineResultDocument(
+            id=self.id,
+            type=self.type,
+            title=self.title,
+            document=raw.types.InputDocument(
+                id=file_id.media_id,
+                access_hash=file_id.access_hash,
+                file_reference=file_id.file_reference,
+            ),
+            send_message=(
+                await self.input_message_content.write(client, self.reply_markup)
+                if self.input_message_content
+                else raw.types.InputBotInlineMessageMediaAuto(
+                    reply_markup=await self.reply_markup.write(client) if self.reply_markup else None,
+                    message=message,
+                    entities=entities
+                )
+            )
+        )
diff --git a/pyrogram/types/inline_mode/inline_query_result_cached_audio.py b/pyrogram/types/inline_mode/inline_query_result_cached_audio.py
new file mode 100644
index 00000000000..9535f633434
--- /dev/null
+++ b/pyrogram/types/inline_mode/inline_query_result_cached_audio.py
@@ -0,0 +1,101 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import Optional, List
+
+import pyrogram
+from pyrogram import raw, types, utils, enums
+from .inline_query_result import InlineQueryResult
+from ...file_id import FileId
+
+
+class InlineQueryResultCachedAudio(InlineQueryResult):
+    """A link to an MP3 audio file stored on the Telegram servers
+
+    By default, this audio file will be sent by the user. Alternatively, you can use *input_message_content* to send a
+    message with the specified content instead of the audio.
+
+    Parameters:
+        audio_file_id (``str``):
+            A valid file identifier for the audio file.
+
+        id (``str``, *optional*):
+            Unique identifier for this result, 1-64 bytes.
+            Defaults to a randomly generated UUID4.
+
+        caption (``str``, *optional*):
+            Caption of the photo to be sent, 0-1024 characters.
+
+        parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+            By default, texts are parsed using both Markdown and HTML styles.
+            You can combine both syntaxes together.
+
+        caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
+            List of special entities that appear in the caption, which can be specified instead of *parse_mode*.
+
+        reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*):
+            An InlineKeyboardMarkup object.
+
+        input_message_content (:obj:`~pyrogram.types.InputMessageContent`):
+            Content of the message to be sent instead of the photo.
+    """
+
+    def __init__(
+        self,
+        audio_file_id: str,
+        id: str = None,
+        caption: str = "",
+        parse_mode: Optional["enums.ParseMode"] = None,
+        caption_entities: List["types.MessageEntity"] = None,
+        reply_markup: "types.InlineKeyboardMarkup" = None,
+        input_message_content: "types.InputMessageContent" = None
+    ):
+        super().__init__("audio", id, input_message_content, reply_markup)
+
+        self.audio_file_id = audio_file_id
+        self.caption = caption
+        self.parse_mode = parse_mode
+        self.caption_entities = caption_entities
+        self.reply_markup = reply_markup
+        self.input_message_content = input_message_content
+
+    async def write(self, client: "pyrogram.Client"):
+        message, entities = (await utils.parse_text_entities(
+            client, self.caption, self.parse_mode, self.caption_entities
+        )).values()
+
+        file_id = FileId.decode(self.audio_file_id)
+
+        return raw.types.InputBotInlineResultDocument(
+            id=self.id,
+            type=self.type,
+            document=raw.types.InputDocument(
+                id=file_id.media_id,
+                access_hash=file_id.access_hash,
+                file_reference=file_id.file_reference,
+            ),
+            send_message=(
+                await self.input_message_content.write(client, self.reply_markup)
+                if self.input_message_content
+                else raw.types.InputBotInlineMessageMediaAuto(
+                    reply_markup=await self.reply_markup.write(client) if self.reply_markup else None,
+                    message=message,
+                    entities=entities
+                )
+            )
+        )
diff --git a/pyrogram/types/inline_mode/inline_query_result_cached_document.py b/pyrogram/types/inline_mode/inline_query_result_cached_document.py
new file mode 100644
index 00000000000..2ab190e7fff
--- /dev/null
+++ b/pyrogram/types/inline_mode/inline_query_result_cached_document.py
@@ -0,0 +1,113 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import Optional, List
+
+import pyrogram
+from pyrogram import raw, types, utils, enums
+from .inline_query_result import InlineQueryResult
+from ...file_id import FileId
+
+
+class InlineQueryResultCachedDocument(InlineQueryResult):
+    """A link to a file stored on the Telegram servers.
+
+    By default, this file will be sent by the user with an optional caption. Alternatively, you can use
+    *input_message_content* to send a message with the specified content instead of the file.
+
+    Parameters:
+        document_file_id (``str``):
+            A valid file identifier for the file.
+
+        title (``str``):
+            Title for the result.
+
+        id (``str``, *optional*):
+            Unique identifier for this result, 1-64 bytes.
+            Defaults to a randomly generated UUID4.
+
+        description (``str``, *optional*):
+            Short description of the result.
+
+        caption (``str``, *optional*):
+            Caption of the photo to be sent, 0-1024 characters.
+
+        parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+            By default, texts are parsed using both Markdown and HTML styles.
+            You can combine both syntaxes together.
+
+        caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
+            List of special entities that appear in the caption, which can be specified instead of *parse_mode*.
+
+        reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*):
+            An InlineKeyboardMarkup object.
+
+        input_message_content (:obj:`~pyrogram.types.InputMessageContent`):
+            Content of the message to be sent instead of the photo.
+    """
+
+    def __init__(
+        self,
+        document_file_id: str,
+        title: str,
+        id: str = None,
+        description: str = None,
+        caption: str = "",
+        parse_mode: Optional["enums.ParseMode"] = None,
+        caption_entities: List["types.MessageEntity"] = None,
+        reply_markup: "types.InlineKeyboardMarkup" = None,
+        input_message_content: "types.InputMessageContent" = None
+    ):
+        super().__init__("file", id, input_message_content, reply_markup)
+
+        self.document_file_id = document_file_id
+        self.title = title
+        self.description = description
+        self.caption = caption
+        self.parse_mode = parse_mode
+        self.caption_entities = caption_entities
+        self.reply_markup = reply_markup
+        self.input_message_content = input_message_content
+
+    async def write(self, client: "pyrogram.Client"):
+        message, entities = (await utils.parse_text_entities(
+            client, self.caption, self.parse_mode, self.caption_entities
+        )).values()
+
+        file_id = FileId.decode(self.document_file_id)
+
+        return raw.types.InputBotInlineResultDocument(
+            id=self.id,
+            type=self.type,
+            title=self.title,
+            description=self.description,
+            document=raw.types.InputDocument(
+                id=file_id.media_id,
+                access_hash=file_id.access_hash,
+                file_reference=file_id.file_reference,
+            ),
+            send_message=(
+                await self.input_message_content.write(client, self.reply_markup)
+                if self.input_message_content
+                else raw.types.InputBotInlineMessageMediaAuto(
+                    reply_markup=await self.reply_markup.write(client) if self.reply_markup else None,
+                    message=message,
+                    entities=entities
+                )
+            )
+        )
diff --git a/pyrogram/types/inline_mode/inline_query_result_cached_photo.py b/pyrogram/types/inline_mode/inline_query_result_cached_photo.py
new file mode 100644
index 00000000000..2e01d344ec7
--- /dev/null
+++ b/pyrogram/types/inline_mode/inline_query_result_cached_photo.py
@@ -0,0 +1,111 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import Optional, List
+
+import pyrogram
+from pyrogram import raw, types, utils, enums
+from .inline_query_result import InlineQueryResult
+from ...file_id import FileId
+
+
+class InlineQueryResultCachedPhoto(InlineQueryResult):
+    """A link to a photo stored on the Telegram servers.
+
+    By default, this photo will be sent by the user with an optional caption. Alternatively, you can use
+    *input_message_content* to send a message with the specified content instead of the photo.
+
+    Parameters:
+        photo_file_id (``str``):
+            A valid file identifier of the photo.
+
+        id (``str``, *optional*):
+            Unique identifier for this result, 1-64 bytes.
+            Defaults to a randomly generated UUID4.
+
+        title (``str``, *optional*):
+            Title for the result.
+
+        description (``str``, *optional*):
+            Short description of the result.
+
+        caption (``str``, *optional*):
+            Caption of the photo to be sent, 0-1024 characters.
+
+        parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+            By default, texts are parsed using both Markdown and HTML styles.
+            You can combine both syntaxes together.
+
+        caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
+            List of special entities that appear in the caption, which can be specified instead of *parse_mode*.
+
+        reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*):
+            An InlineKeyboardMarkup object.
+
+        input_message_content (:obj:`~pyrogram.types.InputMessageContent`):
+            Content of the message to be sent instead of the photo.
+    """
+
+    def __init__(
+        self,
+        photo_file_id: str,
+        id: str = None,
+        title: str = None,
+        description: str = None,
+        caption: str = "",
+        parse_mode: Optional["enums.ParseMode"] = None,
+        caption_entities: List["types.MessageEntity"] = None,
+        reply_markup: "types.InlineKeyboardMarkup" = None,
+        input_message_content: "types.InputMessageContent" = None
+    ):
+        super().__init__("photo", id, input_message_content, reply_markup)
+
+        self.photo_file_id = photo_file_id
+        self.title = title
+        self.description = description
+        self.caption = caption
+        self.parse_mode = parse_mode
+        self.caption_entities = caption_entities
+        self.reply_markup = reply_markup
+        self.input_message_content = input_message_content
+
+    async def write(self, client: "pyrogram.Client"):
+        message, entities = (await utils.parse_text_entities(
+            client, self.caption, self.parse_mode, self.caption_entities
+        )).values()
+
+        file_id = FileId.decode(self.photo_file_id)
+
+        return raw.types.InputBotInlineResultPhoto(
+            id=self.id,
+            type=self.type,
+            photo=raw.types.InputPhoto(
+                id=file_id.media_id,
+                access_hash=file_id.access_hash,
+                file_reference=file_id.file_reference,
+            ),
+            send_message=(
+                await self.input_message_content.write(client, self.reply_markup)
+                if self.input_message_content
+                else raw.types.InputBotInlineMessageMediaAuto(
+                    reply_markup=await self.reply_markup.write(client) if self.reply_markup else None,
+                    message=message,
+                    entities=entities
+                )
+            )
+        )
diff --git a/pyrogram/types/inline_mode/inline_query_result_cached_sticker.py b/pyrogram/types/inline_mode/inline_query_result_cached_sticker.py
new file mode 100644
index 00000000000..06d012fbe6e
--- /dev/null
+++ b/pyrogram/types/inline_mode/inline_query_result_cached_sticker.py
@@ -0,0 +1,78 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import pyrogram
+from pyrogram import raw, types
+from .inline_query_result import InlineQueryResult
+from ...file_id import FileId
+
+
+class InlineQueryResultCachedSticker(InlineQueryResult):
+    """A link to a sticker stored on the Telegram servers
+
+    By default, this sticker will be sent by the user. Alternatively, you can use *input_message_content* to send a
+    message with the specified content instead of the sticker.
+
+    Parameters:
+        sticker_file_id (``str``):
+            A valid file identifier of the sticker.
+
+        id (``str``, *optional*):
+            Unique identifier for this result, 1-64 bytes.
+            Defaults to a randomly generated UUID4.
+
+        reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*):
+            An InlineKeyboardMarkup object.
+
+        input_message_content (:obj:`~pyrogram.types.InputMessageContent`):
+            Content of the message to be sent instead of the photo.
+    """
+
+    def __init__(
+        self,
+        sticker_file_id: str,
+        id: str = None,
+        reply_markup: "types.InlineKeyboardMarkup" = None,
+        input_message_content: "types.InputMessageContent" = None
+    ):
+        super().__init__("sticker", id, input_message_content, reply_markup)
+
+        self.sticker_file_id = sticker_file_id
+        self.reply_markup = reply_markup
+        self.input_message_content = input_message_content
+
+    async def write(self, client: "pyrogram.Client"):
+        file_id = FileId.decode(self.sticker_file_id)
+
+        return raw.types.InputBotInlineResultDocument(
+            id=self.id,
+            type=self.type,
+            document=raw.types.InputDocument(
+                id=file_id.media_id,
+                access_hash=file_id.access_hash,
+                file_reference=file_id.file_reference,
+            ),
+            send_message=(
+                await self.input_message_content.write(client, self.reply_markup)
+                if self.input_message_content
+                else raw.types.InputBotInlineMessageMediaAuto(
+                    reply_markup=await self.reply_markup.write(client) if self.reply_markup else None,
+                    message="",
+                )
+            )
+        )
diff --git a/pyrogram/types/inline_mode/inline_query_result_cached_video.py b/pyrogram/types/inline_mode/inline_query_result_cached_video.py
new file mode 100644
index 00000000000..00ea32ecdb5
--- /dev/null
+++ b/pyrogram/types/inline_mode/inline_query_result_cached_video.py
@@ -0,0 +1,114 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import Optional, List
+
+import pyrogram
+from pyrogram import raw, types, utils, enums
+from .inline_query_result import InlineQueryResult
+from ...file_id import FileId
+
+
+class InlineQueryResultCachedVideo(InlineQueryResult):
+    """A link to a video file stored on the Telegram servers.
+
+    By default, this video file will be sent by the user with an optional caption.
+    Alternatively, you can use *input_message_content* to send a message with the specified content instead of the
+    video.
+
+    Parameters:
+        video_file_id (``str``):
+            A valid file identifier for the video file.
+
+        title (``str``):
+            Title for the result.
+
+        id (``str``, *optional*):
+            Unique identifier for this result, 1-64 bytes.
+            Defaults to a randomly generated UUID4.
+
+        description (``str``, *optional*):
+            Short description of the result.
+
+        caption (``str``, *optional*):
+            Caption of the photo to be sent, 0-1024 characters.
+
+        parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+            By default, texts are parsed using both Markdown and HTML styles.
+            You can combine both syntaxes together.
+
+        caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
+            List of special entities that appear in the caption, which can be specified instead of *parse_mode*.
+
+        reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*):
+            An InlineKeyboardMarkup object.
+
+        input_message_content (:obj:`~pyrogram.types.InputMessageContent`):
+            Content of the message to be sent instead of the photo.
+    """
+
+    def __init__(
+        self,
+        video_file_id: str,
+        title: str,
+        id: str = None,
+        description: str = None,
+        caption: str = "",
+        parse_mode: Optional["enums.ParseMode"] = None,
+        caption_entities: List["types.MessageEntity"] = None,
+        reply_markup: "types.InlineKeyboardMarkup" = None,
+        input_message_content: "types.InputMessageContent" = None
+    ):
+        super().__init__("video", id, input_message_content, reply_markup)
+
+        self.video_file_id = video_file_id
+        self.title = title
+        self.description = description
+        self.caption = caption
+        self.parse_mode = parse_mode
+        self.caption_entities = caption_entities
+        self.reply_markup = reply_markup
+        self.input_message_content = input_message_content
+
+    async def write(self, client: "pyrogram.Client"):
+        message, entities = (await utils.parse_text_entities(
+            client, self.caption, self.parse_mode, self.caption_entities
+        )).values()
+
+        file_id = FileId.decode(self.video_file_id)
+
+        return raw.types.InputBotInlineResultDocument(
+            id=self.id,
+            type=self.type,
+            title=self.title,
+            description=self.description,
+            document=raw.types.InputDocument(
+                id=file_id.media_id,
+                access_hash=file_id.access_hash,
+                file_reference=file_id.file_reference,
+            ),
+            send_message=(
+                await self.input_message_content.write(client, self.reply_markup)
+                if self.input_message_content
+                else raw.types.InputBotInlineMessageMediaAuto(
+                    reply_markup=await self.reply_markup.write(client) if self.reply_markup else None,
+                    message=message,
+                    entities=entities
+                )
+            )
+        )
diff --git a/pyrogram/types/inline_mode/inline_query_result_cached_voice.py b/pyrogram/types/inline_mode/inline_query_result_cached_voice.py
new file mode 100644
index 00000000000..cc2bd76855c
--- /dev/null
+++ b/pyrogram/types/inline_mode/inline_query_result_cached_voice.py
@@ -0,0 +1,108 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import Optional, List
+
+import pyrogram
+from pyrogram import raw, types, utils, enums
+from .inline_query_result import InlineQueryResult
+from ...file_id import FileId
+
+
+class InlineQueryResultCachedVoice(InlineQueryResult):
+    """A link to a voice message stored on the Telegram servers.
+
+    By default, this voice message will be sent by the user.
+    Alternatively, you can use *input_message_content* to send a message with the specified content instead of the voice
+    message.
+
+    Parameters:
+        voice_file_id (``str``):
+            A valid file identifier for the voice message.
+
+        id (``str``, *optional*):
+            Unique identifier for this result, 1-64 bytes.
+            Defaults to a randomly generated UUID4.
+
+        title (``str``, *optional*):
+            Title for the result.
+
+        caption (``str``, *optional*):
+            Caption of the photo to be sent, 0-1024 characters.
+
+        parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+            By default, texts are parsed using both Markdown and HTML styles.
+            You can combine both syntaxes together.
+
+        caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
+            List of special entities that appear in the caption, which can be specified instead of *parse_mode*.
+
+        reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*):
+            An InlineKeyboardMarkup object.
+
+        input_message_content (:obj:`~pyrogram.types.InputMessageContent`):
+            Content of the message to be sent instead of the photo.
+    """
+
+    def __init__(
+        self,
+        voice_file_id: str,
+        id: str = None,
+        title: str = None,
+        caption: str = "",
+        parse_mode: Optional["enums.ParseMode"] = None,
+        caption_entities: List["types.MessageEntity"] = None,
+        reply_markup: "types.InlineKeyboardMarkup" = None,
+        input_message_content: "types.InputMessageContent" = None
+    ):
+        super().__init__("voice", id, input_message_content, reply_markup)
+
+        self.voice_file_id = voice_file_id
+        self.title = title
+        self.caption = caption
+        self.parse_mode = parse_mode
+        self.caption_entities = caption_entities
+        self.reply_markup = reply_markup
+        self.input_message_content = input_message_content
+
+    async def write(self, client: "pyrogram.Client"):
+        message, entities = (await utils.parse_text_entities(
+            client, self.caption, self.parse_mode, self.caption_entities
+        )).values()
+
+        file_id = FileId.decode(self.voice_file_id)
+
+        return raw.types.InputBotInlineResultDocument(
+            id=self.id,
+            type=self.type,
+            title=self.title,
+            document=raw.types.InputDocument(
+                id=file_id.media_id,
+                access_hash=file_id.access_hash,
+                file_reference=file_id.file_reference,
+            ),
+            send_message=(
+                await self.input_message_content.write(client, self.reply_markup)
+                if self.input_message_content
+                else raw.types.InputBotInlineMessageMediaAuto(
+                    reply_markup=await self.reply_markup.write(client) if self.reply_markup else None,
+                    message=message,
+                    entities=entities
+                )
+            )
+        )
diff --git a/pyrogram/types/inline_mode/inline_query_result_contact.py b/pyrogram/types/inline_mode/inline_query_result_contact.py
new file mode 100644
index 00000000000..d55a624450f
--- /dev/null
+++ b/pyrogram/types/inline_mode/inline_query_result_contact.py
@@ -0,0 +1,114 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import pyrogram
+from pyrogram import raw, types
+from .inline_query_result import InlineQueryResult
+
+
+class InlineQueryResultContact(InlineQueryResult):
+    """Contact with a phone number
+    
+    By default, this contact will be sent by the user.
+    Alternatively, you can use *input_message_content* to send a message with the specified content instead of the
+    contact.
+    
+    Parameters:
+        phone_number (``str``):
+            Contact's phone number.
+
+        first_name (``str``):
+            Contact's first name.
+
+        last_name (``str``, *optional*):
+            Contact's last name.
+
+        vcard (``str``, *optional*):
+            Additional data about the contact in the form of a `vCard `_.
+
+        id (``str``, *optional*):
+            Unique identifier for this result, 1-64 bytes.
+            Defaults to a randomly generated UUID4.
+
+        reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*):
+            Inline keyboard attached to the message.
+            
+        input_message_content (:obj:`~pyrogram.types.InputMessageContent`, *optional*):
+            Content of the message to be sent instead of the contact.
+
+        thumb_url (``str``, *optional*):
+            Url of the thumbnail for the result.
+
+        thumb_width (``int``, *optional*):
+            Thumbnail width.
+
+        thumb_height (``int``, *optional*):
+            Thumbnail height.
+    """
+
+    def __init__(
+        self,
+        phone_number: str,
+        first_name: str,
+        last_name: str = "",
+        vcard: str = "",
+        id: str = None,
+        reply_markup: "types.InlineKeyboardMarkup" = None,
+        input_message_content: "types.InputMessageContent" = None,
+        thumb_url: str = None,
+        thumb_width: int = 0,
+        thumb_height: int = 0
+    ):
+        super().__init__("contact", id, input_message_content, reply_markup)
+
+        self.phone_number = phone_number
+        self.first_name = first_name
+        self.last_name = last_name
+        self.vcard = vcard
+        self.thumb_url = thumb_url
+        self.thumb_width = thumb_width
+        self.thumb_height = thumb_height
+
+    async def write(self, client: "pyrogram.Client"):
+        return raw.types.InputBotInlineResult(
+            id=self.id,
+            type=self.type,
+            title=self.first_name,
+            send_message=(
+                await self.input_message_content.write(client, self.reply_markup)
+                if self.input_message_content
+                else raw.types.InputBotInlineMessageMediaContact(
+                    phone_number=self.phone_number,
+                    first_name=self.first_name,
+                    last_name=self.last_name,
+                    vcard=self.vcard,
+                    reply_markup=await self.reply_markup.write(client) if self.reply_markup else None,
+                )
+            ),
+            thumb=raw.types.InputWebDocument(
+                url=self.thumb_url,
+                size=0,
+                mime_type="image/jpg",
+                attributes=[
+                    raw.types.DocumentAttributeImageSize(
+                        w=self.thumb_width,
+                        h=self.thumb_height
+                    )
+                ]
+            ) if self.thumb_url else None
+        )
diff --git a/pyrogram/types/inline_mode/inline_query_result_document.py b/pyrogram/types/inline_mode/inline_query_result_document.py
new file mode 100644
index 00000000000..eac7901b146
--- /dev/null
+++ b/pyrogram/types/inline_mode/inline_query_result_document.py
@@ -0,0 +1,145 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import Optional, List
+
+import pyrogram
+from pyrogram import raw, types, utils, enums
+from .inline_query_result import InlineQueryResult
+
+
+class InlineQueryResultDocument(InlineQueryResult):
+    """Link to a file.
+
+    By default, this file will be sent by the user with an optional caption.
+    Alternatively, you can use *input_message_content* to send a message with the specified content instead of the file.
+
+    Parameters:
+        document_url (``str``):
+            A valid URL for the file.
+
+        title (``str``):
+            Title for the result.
+
+        mime_type (``str``, *optional*):
+            Mime type of the content of the file, either “application/pdf” or “application/zip”.
+            Defaults to "application/zip".
+
+        id (``str``, *optional*):
+            Unique identifier for this result, 1-64 bytes.
+            Defaults to a randomly generated UUID4.
+
+        caption (``str``, *optional*):
+            Caption of the video to be sent, 0-1024 characters.
+
+        parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+            By default, texts are parsed using both Markdown and HTML styles.
+            You can combine both syntaxes together.
+
+        caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
+            List of special entities that appear in the caption, which can be specified instead of *parse_mode*.
+
+        description (``str``, *optional*):
+            Short description of the result.
+
+        reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*):
+            Inline keyboard attached to the message.
+
+        input_message_content (:obj:`~pyrogram.types.InputMessageContent`):
+            Content of the message to be sent instead of the file.
+
+        thumb_url (``str``, *optional*):
+            Url of the thumbnail for the result.
+
+        thumb_width (``int``, *optional*):
+            Thumbnail width.
+
+        thumb_height (``int``, *optional*):
+            Thumbnail height.
+    """
+
+    def __init__(
+        self,
+        document_url: str,
+        title: str,
+        mime_type: str = "application/zip",
+        id: str = None,
+        caption: str = "",
+        parse_mode: Optional["enums.ParseMode"] = None,
+        caption_entities: List["types.MessageEntity"] = None,
+        description: str = "",
+        reply_markup: "types.InlineKeyboardMarkup" = None,
+        input_message_content: "types.InputMessageContent" = None,
+        thumb_url: str = None,
+        thumb_width: int = 0,
+        thumb_height: int = 0
+    ):
+        super().__init__("file", id, input_message_content, reply_markup)
+
+        self.document_url = document_url
+        self.title = title
+        self.mime_type = mime_type
+        self.caption = caption
+        self.parse_mode = parse_mode
+        self.caption_entities = caption_entities
+        self.description = description
+        self.thumb_url = thumb_url
+        self.thumb_width = thumb_width
+        self.thumb_height = thumb_height
+
+    async def write(self, client: "pyrogram.Client"):
+        document = raw.types.InputWebDocument(
+            url=self.document_url,
+            size=0,
+            mime_type=self.mime_type,
+            attributes=[]
+        )
+
+        thumb = raw.types.InputWebDocument(
+            url=self.thumb_url,
+            size=0,
+            mime_type="image/jpeg",
+            attributes=[
+                raw.types.DocumentAttributeImageSize(
+                    w=self.thumb_width,
+                    h=self.thumb_height
+                )
+            ]
+        ) if self.thumb_url else None
+
+        message, entities = (await utils.parse_text_entities(
+            client, self.caption, self.parse_mode, self.caption_entities
+        )).values()
+
+        return raw.types.InputBotInlineResult(
+            id=self.id,
+            type=self.type,
+            title=self.title,
+            description=self.description,
+            thumb=thumb,
+            content=document,
+            send_message=(
+                await self.input_message_content.write(client, self.reply_markup)
+                if self.input_message_content
+                else raw.types.InputBotInlineMessageMediaAuto(
+                    reply_markup=await self.reply_markup.write(client) if self.reply_markup else None,
+                    message=message,
+                    entities=entities
+                )
+            )
+        )
diff --git a/pyrogram/types/inline_mode/inline_query_result_location.py b/pyrogram/types/inline_mode/inline_query_result_location.py
new file mode 100644
index 00000000000..236f39a6245
--- /dev/null
+++ b/pyrogram/types/inline_mode/inline_query_result_location.py
@@ -0,0 +1,122 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import pyrogram
+from pyrogram import raw, types
+from .inline_query_result import InlineQueryResult
+
+
+class InlineQueryResultLocation(InlineQueryResult):
+    """A location on a map.
+
+    By default, the location will be sent by the user. Alternatively, you can use *input_message_content* to send a
+    message with the specified content instead of the location.
+
+    Parameters:
+        title (``str``):
+            Title for the result.
+
+        latitude (``float``):
+            Location latitude in degrees.
+
+        longitude (``float``):
+            Location longitude in degrees.
+
+        id (``str``, *optional*):
+            Unique identifier for this result, 1-64 bytes.
+            Defaults to a randomly generated UUID4.
+
+        horizontal_accuracy (``float``, *optional*)
+            The radius of uncertainty for the location, measured in meters; 0-1500.
+
+        live_period (``int``, *optional*):
+            Period in seconds for which the location can be updated, should be between 60 and 86400.
+
+        heading (``int``, *optional*):
+            For live locations, a direction in which the user is moving, in degrees.
+            Must be between 1 and 360 if specified.
+
+        proximity_alert_radius (``int``, *optional*):
+            For live locations, a maximum distance for proximity alerts about approaching another chat member,
+            in meters. Must be between 1 and 100000 if specified.
+
+        reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*):
+            Inline keyboard attached to the message.
+
+        input_message_content (:obj:`~pyrogram.types.InputMessageContent`):
+            Content of the message to be sent instead of the file.
+
+        thumb_url (``str``, *optional*):
+            Url of the thumbnail for the result.
+
+        thumb_width (``int``, *optional*):
+            Thumbnail width.
+
+        thumb_height (``int``, *optional*):
+            Thumbnail height.
+    """
+
+    def __init__(
+        self,
+        title: str,
+        latitude: float,
+        longitude: float,
+        horizontal_accuracy: float = None,
+        live_period: int = None,
+        heading: int = None,
+        proximity_alert_radius: int = None,
+        id: str = None,
+        reply_markup: "types.InlineKeyboardMarkup" = None,
+        input_message_content: "types.InputMessageContent" = None,
+        thumb_url: str = None,
+        thumb_width: int = 0,
+        thumb_height: int = 0
+    ):
+        super().__init__("location", id, input_message_content, reply_markup)
+
+        self.title = title
+        self.latitude = latitude
+        self.longitude = longitude
+        self.horizontal_accuracy = horizontal_accuracy
+        self.live_period = live_period
+        self.heading = heading
+        self.proximity_alert_radius = proximity_alert_radius
+        self.thumb_url = thumb_url
+        self.thumb_width = thumb_width
+        self.thumb_height = thumb_height
+
+    async def write(self, client: "pyrogram.Client"):
+        return raw.types.InputBotInlineResult(
+            id=self.id,
+            type=self.type,
+            title=self.title,
+            send_message=(
+                await self.input_message_content.write(client, self.reply_markup)
+                if self.input_message_content
+                else raw.types.InputBotInlineMessageMediaGeo(
+                    geo_point=raw.types.InputGeoPoint(
+                        lat=self.latitude,
+                        long=self.longitude
+                    ),
+                    heading=self.heading,
+                    period=self.live_period,
+                    proximity_notification_radius=self.proximity_alert_radius,
+                    reply_markup=await self.reply_markup.write(client) if self.reply_markup else None
+                )
+            )
+        )
diff --git a/pyrogram/types/inline_mode/inline_query_result_photo.py b/pyrogram/types/inline_mode/inline_query_result_photo.py
new file mode 100644
index 00000000000..d75ccac2a52
--- /dev/null
+++ b/pyrogram/types/inline_mode/inline_query_result_photo.py
@@ -0,0 +1,147 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import Optional, List
+
+import pyrogram
+from pyrogram import raw, types, utils, enums
+from .inline_query_result import InlineQueryResult
+
+
+class InlineQueryResultPhoto(InlineQueryResult):
+    """Link to a photo.
+
+    By default, this photo will be sent by the user with optional caption.
+    Alternatively, you can use *input_message_content* to send a message with the specified content instead of the
+    photo.
+
+    Parameters:
+        photo_url (``str``):
+            A valid URL of the photo.
+            Photo must be in jpeg format an must not exceed 5 MB.
+
+        thumb_url (``str``, *optional*):
+            URL of the thumbnail for the photo.
+            Defaults to the value passed in *photo_url*.
+
+        photo_width (``int``, *optional*):
+            Width of the photo.
+
+        photo_height (``int``, *optional*):
+            Height of the photo
+
+        id (``str``, *optional*):
+            Unique identifier for this result, 1-64 bytes.
+            Defaults to a randomly generated UUID4.
+
+        title (``str``, *optional*):
+            Title for the result.
+
+        description (``str``, *optional*):
+            Short description of the result.
+
+        caption (``str``, *optional*):
+            Caption of the photo to be sent, 0-1024 characters.
+
+        parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+            By default, texts are parsed using both Markdown and HTML styles.
+            You can combine both syntaxes together.
+
+        caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
+            List of special entities that appear in the caption, which can be specified instead of *parse_mode*.
+
+        reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*):
+            An InlineKeyboardMarkup object.
+
+        input_message_content (:obj:`~pyrogram.types.InputMessageContent`):
+            Content of the message to be sent instead of the photo.
+    """
+
+    def __init__(
+        self,
+        photo_url: str,
+        thumb_url: str = None,
+        photo_width: int = 0,
+        photo_height: int = 0,
+        id: str = None,
+        title: str = None,
+        description: str = None,
+        caption: str = "",
+        parse_mode: Optional["enums.ParseMode"] = None,
+        caption_entities: List["types.MessageEntity"] = None,
+        reply_markup: "types.InlineKeyboardMarkup" = None,
+        input_message_content: "types.InputMessageContent" = None
+    ):
+        super().__init__("photo", id, input_message_content, reply_markup)
+
+        self.photo_url = photo_url
+        self.thumb_url = thumb_url
+        self.photo_width = photo_width
+        self.photo_height = photo_height
+        self.title = title
+        self.description = description
+        self.caption = caption
+        self.parse_mode = parse_mode
+        self.caption_entities = caption_entities
+        self.reply_markup = reply_markup
+        self.input_message_content = input_message_content
+
+    async def write(self, client: "pyrogram.Client"):
+        photo = raw.types.InputWebDocument(
+            url=self.photo_url,
+            size=0,
+            mime_type="image/jpeg",
+            attributes=[
+                raw.types.DocumentAttributeImageSize(
+                    w=self.photo_width,
+                    h=self.photo_height
+                )
+            ]
+        )
+
+        if self.thumb_url is None:
+            thumb = photo
+        else:
+            thumb = raw.types.InputWebDocument(
+                url=self.thumb_url,
+                size=0,
+                mime_type="image/jpeg",
+                attributes=[]
+            )
+
+        message, entities = (await utils.parse_text_entities(
+            client, self.caption, self.parse_mode, self.caption_entities
+        )).values()
+
+        return raw.types.InputBotInlineResult(
+            id=self.id,
+            type=self.type,
+            title=self.title,
+            description=self.description,
+            thumb=thumb,
+            content=photo,
+            send_message=(
+                await self.input_message_content.write(client, self.reply_markup)
+                if self.input_message_content
+                else raw.types.InputBotInlineMessageMediaAuto(
+                    reply_markup=await self.reply_markup.write(client) if self.reply_markup else None,
+                    message=message,
+                    entities=entities
+                )
+            )
+        )
diff --git a/pyrogram/types/inline_mode/inline_query_result_venue.py b/pyrogram/types/inline_mode/inline_query_result_venue.py
new file mode 100644
index 00000000000..b3b513a55d7
--- /dev/null
+++ b/pyrogram/types/inline_mode/inline_query_result_venue.py
@@ -0,0 +1,131 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import pyrogram
+from pyrogram import raw, types
+from .inline_query_result import InlineQueryResult
+
+
+class InlineQueryResultVenue(InlineQueryResult):
+    """A venue.
+
+    By default, the venue will be sent by the user. Alternatively, you can use *input_message_content* to send a message
+    with the specified content instead of the venue.
+
+    Parameters:
+        title (``str``):
+            Title for the result.
+
+        address (``str``):
+            Address of the venue.
+
+        latitude (``float``):
+            Location latitude in degrees.
+
+        longitude (``float``):
+            Location longitude in degrees.
+
+        id (``str``, *optional*):
+            Unique identifier for this result, 1-64 bytes.
+            Defaults to a randomly generated UUID4.
+
+        foursquare_id (``str``, *optional*):
+            Foursquare identifier of the venue if known.
+
+        foursquare_type (``str``, *optional*):
+            Foursquare type of the venue, if known.
+
+        google_place_id (``str``, *optional*):
+            Google Places identifier of the venue.
+
+        google_place_type (``str``, *optional*):
+            Google Places type of the venue.
+
+        reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*):
+            Inline keyboard attached to the message.
+
+        input_message_content (:obj:`~pyrogram.types.InputMessageContent`):
+            Content of the message to be sent instead of the file.
+
+        thumb_url (``str``, *optional*):
+            Url of the thumbnail for the result.
+
+        thumb_width (``int``, *optional*):
+            Thumbnail width.
+
+        thumb_height (``int``, *optional*):
+            Thumbnail height.
+    """
+
+    def __init__(
+        self,
+        title: str,
+        address: str,
+        latitude: float,
+        longitude: float,
+        id: str = None,
+        foursquare_id: str = None,
+        foursquare_type: str = None,
+        google_place_id: str = None,
+        google_place_type: str = None,
+        reply_markup: "types.InlineKeyboardMarkup" = None,
+        input_message_content: "types.InputMessageContent" = None,
+        thumb_url: str = None,
+        thumb_width: int = 0,
+        thumb_height: int = 0
+    ):
+        super().__init__("venue", id, input_message_content, reply_markup)
+
+        self.title = title
+        self.address = address
+        self.latitude = latitude
+        self.longitude = longitude
+        self.foursquare_id = foursquare_id
+        self.foursquare_type = foursquare_type
+        self.google_place_id = google_place_id
+        self.google_place_type = google_place_type
+        self.thumb_url = thumb_url
+        self.thumb_width = thumb_width
+        self.thumb_height = thumb_height
+
+    async def write(self, client: "pyrogram.Client"):
+        return raw.types.InputBotInlineResult(
+            id=self.id,
+            type=self.type,
+            title=self.title,
+            send_message=(
+                await self.input_message_content.write(client, self.reply_markup)
+                if self.input_message_content
+                else raw.types.InputBotInlineMessageMediaVenue(
+                    geo_point=raw.types.InputGeoPoint(
+                        lat=self.latitude,
+                        long=self.longitude
+                    ),
+                    title=self.title,
+                    address=self.address,
+                    provider=(
+                        "foursquare" if self.foursquare_id or self.foursquare_type
+                        else "google" if self.google_place_id or self.google_place_type
+                        else ""
+                    ),
+                    venue_id=self.foursquare_id or self.google_place_id or "",
+                    venue_type=self.foursquare_type or self.google_place_type or "",
+                    reply_markup=await self.reply_markup.write(client) if self.reply_markup else None
+                )
+            )
+        )
diff --git a/pyrogram/types/inline_mode/inline_query_result_video.py b/pyrogram/types/inline_mode/inline_query_result_video.py
new file mode 100644
index 00000000000..5f71111f4f8
--- /dev/null
+++ b/pyrogram/types/inline_mode/inline_query_result_video.py
@@ -0,0 +1,151 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import Optional, List
+
+import pyrogram
+from pyrogram import raw, types, utils, enums
+from .inline_query_result import InlineQueryResult
+
+
+class InlineQueryResultVideo(InlineQueryResult):
+    """Link to a page containing an embedded video player or a video file.
+
+    By default, this video file will be sent by the user with an optional caption.
+    Alternatively, you can use *input_message_content* to send a message with the specified content instead of the
+    video.
+
+    Parameters:
+        video_url (``str``):
+            A valid URL for the embedded video player or video file.
+
+        thumb_url (``str``):
+            URL of the thumbnail (jpeg only) for the video.
+
+        title (``str``):
+            Title for the result.
+
+        id (``str``, *optional*):
+            Unique identifier for this result, 1-64 bytes.
+            Defaults to a randomly generated UUID4.
+
+        mime_type (``str``):
+            Mime type of the content of video url, "text/html" or "video/mp4".
+            Defaults to "video/mp4".
+
+        video_width (``int``):
+            Video width.
+
+        video_height (``int``):
+            Video height.
+
+        video_duration (``int``):
+            Video duration in seconds.
+
+        description (``str``, *optional*):
+            Short description of the result.
+
+        caption (``str``, *optional*):
+            Caption of the video to be sent, 0-1024 characters.
+
+        parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+            By default, texts are parsed using both Markdown and HTML styles.
+            You can combine both syntaxes together.
+
+        caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
+            List of special entities that appear in the caption, which can be specified instead of *parse_mode*.
+
+        reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*):
+            Inline keyboard attached to the message
+
+        input_message_content (:obj:`~pyrogram.types.InputMessageContent`):
+            Content of the message to be sent instead of the video. This field is required if InlineQueryResultVideo is
+            used to send an HTML-page as a result (e.g., a YouTube video).
+    """
+
+    def __init__(
+        self,
+        video_url: str,
+        thumb_url: str,
+        title: str,
+        id: str = None,
+        mime_type: str = "video/mp4",
+        video_width: int = 0,
+        video_height: int = 0,
+        video_duration: int = 0,
+        description: str = None,
+        caption: str = "",
+        parse_mode: Optional["enums.ParseMode"] = None,
+        caption_entities: List["types.MessageEntity"] = None,
+        reply_markup: "types.InlineKeyboardMarkup" = None,
+        input_message_content: "types.InputMessageContent" = None
+    ):
+        super().__init__("video", id, input_message_content, reply_markup)
+
+        self.video_url = video_url
+        self.thumb_url = thumb_url
+        self.title = title
+        self.video_width = video_width
+        self.video_height = video_height
+        self.video_duration = video_duration
+        self.description = description
+        self.caption = caption
+        self.parse_mode = parse_mode
+        self.caption_entities = caption_entities
+        self.mime_type = mime_type
+
+    async def write(self, client: "pyrogram.Client"):
+        video = raw.types.InputWebDocument(
+            url=self.video_url,
+            size=0,
+            mime_type=self.mime_type,
+            attributes=[raw.types.DocumentAttributeVideo(
+                duration=self.video_duration,
+                w=self.video_width,
+                h=self.video_height
+            )]
+        )
+
+        thumb = raw.types.InputWebDocument(
+            url=self.thumb_url,
+            size=0,
+            mime_type="image/jpeg",
+            attributes=[]
+        )
+
+        message, entities = (await utils.parse_text_entities(
+            client, self.caption, self.parse_mode, self.caption_entities
+        )).values()
+
+        return raw.types.InputBotInlineResult(
+            id=self.id,
+            type=self.type,
+            title=self.title,
+            description=self.description,
+            thumb=thumb,
+            content=video,
+            send_message=(
+                await self.input_message_content.write(client, self.reply_markup)
+                if self.input_message_content
+                else raw.types.InputBotInlineMessageMediaAuto(
+                    reply_markup=await self.reply_markup.write(client) if self.reply_markup else None,
+                    message=message,
+                    entities=entities
+                )
+            )
+        )
diff --git a/pyrogram/types/inline_mode/inline_query_result_voice.py b/pyrogram/types/inline_mode/inline_query_result_voice.py
new file mode 100644
index 00000000000..31b422f8d30
--- /dev/null
+++ b/pyrogram/types/inline_mode/inline_query_result_voice.py
@@ -0,0 +1,114 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import List, Optional
+
+import pyrogram
+from pyrogram import raw, types, utils, enums
+from .inline_query_result import InlineQueryResult
+
+
+class InlineQueryResultVoice(InlineQueryResult):
+    """Link to a voice recording in an .OGG container encoded with OPUS.
+    
+    By default, this voice recording will be sent by the user.
+    Alternatively, you can use *input_message_content* to send a message with the specified content instead of the
+    voice message.
+    
+    Parameters:
+        voice_url (``str``):
+            A valid URL for the voice recording.
+            
+        title (``str``):
+            Title for the result.
+            
+        id (``str``, *optional*):
+            Unique identifier for this result, 1-64 bytes.
+            Defaults to a randomly generated UUID4.
+            
+        voice_duration (``int``, *optional*):
+            Recording duration in seconds.
+
+        caption (``str``, *optional*):
+            Caption of the audio to be sent, 0-1024 characters.
+            
+        parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+            By default, texts are parsed using both Markdown and HTML styles.
+            You can combine both syntaxes together.
+
+        caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
+            List of special entities that appear in the caption, which can be specified instead of *parse_mode*.
+            
+        reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*):
+            Inline keyboard attached to the message.
+            
+        input_message_content (:obj:`~pyrogram.types.InputMessageContent`, *optional*):
+            Content of the message to be sent instead of the audio.
+    """
+
+    def __init__(
+        self,
+        voice_url: str,
+        title: str,
+        id: str = None,
+        voice_duration: int = 0,
+        caption: str = "",
+        parse_mode: Optional["enums.ParseMode"] = None,
+        caption_entities: List["types.MessageEntity"] = None,
+        reply_markup: "types.InlineKeyboardMarkup" = None,
+        input_message_content: "types.InputMessageContent" = None
+    ):
+        super().__init__("voice", id, input_message_content, reply_markup)
+
+        self.voice_url = voice_url
+        self.title = title
+        self.voice_duration = voice_duration
+        self.caption = caption
+        self.parse_mode = parse_mode
+        self.caption_entities = caption_entities
+
+    async def write(self, client: "pyrogram.Client"):
+        audio = raw.types.InputWebDocument(
+            url=self.voice_url,
+            size=0,
+            mime_type="audio/mpeg",
+            attributes=[raw.types.DocumentAttributeAudio(
+                duration=self.voice_duration,
+                title=self.title,
+            )]
+        )
+
+        message, entities = (await utils.parse_text_entities(
+            client, self.caption, self.parse_mode, self.caption_entities
+        )).values()
+
+        return raw.types.InputBotInlineResult(
+            id=self.id,
+            type=self.type,
+            title=self.title,
+            content=audio,
+            send_message=(
+                await self.input_message_content.write(client, self.reply_markup)
+                if self.input_message_content
+                else raw.types.InputBotInlineMessageMediaAuto(
+                    reply_markup=await self.reply_markup.write(client) if self.reply_markup else None,
+                    message=message,
+                    entities=entities
+                )
+            )
+        )
diff --git a/pyrogram/types/input_media/__init__.py b/pyrogram/types/input_media/__init__.py
new file mode 100644
index 00000000000..a03d1e21f48
--- /dev/null
+++ b/pyrogram/types/input_media/__init__.py
@@ -0,0 +1,30 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .input_media import InputMedia
+from .input_media_animation import InputMediaAnimation
+from .input_media_audio import InputMediaAudio
+from .input_media_document import InputMediaDocument
+from .input_media_photo import InputMediaPhoto
+from .input_media_video import InputMediaVideo
+from .input_phone_contact import InputPhoneContact
+
+__all__ = [
+    "InputMedia", "InputMediaAnimation", "InputMediaAudio", "InputMediaDocument", "InputMediaPhoto", "InputMediaVideo",
+    "InputPhoneContact"
+]
diff --git a/pyrogram/types/input_media/input_media.py b/pyrogram/types/input_media/input_media.py
new file mode 100644
index 00000000000..bd60aeb3e17
--- /dev/null
+++ b/pyrogram/types/input_media/input_media.py
@@ -0,0 +1,49 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import List, Union, BinaryIO
+
+from ..messages_and_media import MessageEntity
+from ..object import Object
+
+
+class InputMedia(Object):
+    """Content of a media message to be sent.
+
+    It should be one of:
+
+    - :obj:`~pyrogram.types.InputMediaAnimation`
+    - :obj:`~pyrogram.types.InputMediaDocument`
+    - :obj:`~pyrogram.types.InputMediaAudio`
+    - :obj:`~pyrogram.types.InputMediaPhoto`
+    - :obj:`~pyrogram.types.InputMediaVideo`
+    """
+
+    def __init__(
+        self,
+        media: Union[str, BinaryIO],
+        caption: str = "",
+        parse_mode: str = None,
+        caption_entities: List[MessageEntity] = None
+    ):
+        super().__init__()
+
+        self.media = media
+        self.caption = caption
+        self.parse_mode = parse_mode
+        self.caption_entities = caption_entities
diff --git a/pyrogram/types/input_media/input_media_animation.py b/pyrogram/types/input_media/input_media_animation.py
new file mode 100644
index 00000000000..2e91a2147b3
--- /dev/null
+++ b/pyrogram/types/input_media/input_media_animation.py
@@ -0,0 +1,85 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import Optional, List, Union, BinaryIO
+
+from .input_media import InputMedia
+from ..messages_and_media import MessageEntity
+from ... import enums
+
+
+class InputMediaAnimation(InputMedia):
+    """An animation file (GIF or H.264/MPEG-4 AVC video without sound) to be sent inside an album.
+
+    Parameters:
+        media (``str`` | ``BinaryIO``):
+            Animation to send.
+            Pass a file_id as string to send a file that exists on the Telegram servers or
+            pass a file path as string to upload a new file that exists on your local machine or
+            pass a binary file-like object with its attribute “.name” set for in-memory uploads or
+            pass an HTTP URL as a string for Telegram to get an animation from the Internet.
+
+        thumb (``str``, *optional*):
+            Thumbnail of the animation file sent.
+            The thumbnail should be in JPEG format and less than 200 KB in size.
+            A thumbnail's width and height should not exceed 320 pixels.
+            Thumbnails can't be reused and can be only uploaded as a new file.
+
+        caption (``str``, *optional*):
+            Caption of the animation to be sent, 0-1024 characters.
+            If not specified, the original caption is kept. Pass "" (empty string) to remove the caption.
+
+        parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+            By default, texts are parsed using both Markdown and HTML styles.
+            You can combine both syntaxes together.
+
+        caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
+            List of special entities that appear in the caption, which can be specified instead of *parse_mode*.
+
+        width (``int``, *optional*):
+            Animation width.
+
+        height (``int``, *optional*):
+            Animation height.
+
+        duration (``int``, *optional*):
+            Animation duration.
+
+        has_spoiler (``bool``, *optional*):
+            Pass True if the photo needs to be covered with a spoiler animation.
+    """
+
+    def __init__(
+        self,
+        media: Union[str, BinaryIO],
+        thumb: str = None,
+        caption: str = "",
+        parse_mode: Optional["enums.ParseMode"] = None,
+        caption_entities: List[MessageEntity] = None,
+        width: int = 0,
+        height: int = 0,
+        duration: int = 0,
+        has_spoiler: bool = None
+    ):
+        super().__init__(media, caption, parse_mode, caption_entities)
+
+        self.thumb = thumb
+        self.width = width
+        self.height = height
+        self.duration = duration
+        self.has_spoiler = has_spoiler
diff --git a/pyrogram/types/input_media/input_media_audio.py b/pyrogram/types/input_media/input_media_audio.py
new file mode 100644
index 00000000000..cc91e7bd5cc
--- /dev/null
+++ b/pyrogram/types/input_media/input_media_audio.py
@@ -0,0 +1,82 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import Optional, List, BinaryIO, Union
+
+from .input_media import InputMedia
+from ..messages_and_media import MessageEntity
+from ... import enums
+
+
+class InputMediaAudio(InputMedia):
+    """An audio to be sent inside an album.
+
+    It is intended to be used with :meth:`~pyrogram.Client.send_media_group`.
+
+    Parameters:
+        media (``str`` | ``BinaryIO``):
+            Audio to send.
+            Pass a file_id as string to send an audio that exists on the Telegram servers or
+            pass a file path as string to upload a new audio that exists on your local machine or
+            pass a binary file-like object with its attribute “.name” set for in-memory uploads or
+            pass an HTTP URL as a string for Telegram to get an audio file from the Internet.
+
+        thumb (``str``, *optional*):
+            Thumbnail of the music file album cover.
+            The thumbnail should be in JPEG format and less than 200 KB in size.
+            A thumbnail's width and height should not exceed 320 pixels.
+            Thumbnails can't be reused and can be only uploaded as a new file.
+
+        caption (``str``, *optional*):
+            Caption of the audio to be sent, 0-1024 characters.
+            If not specified, the original caption is kept. Pass "" (empty string) to remove the caption.
+
+        parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+            By default, texts are parsed using both Markdown and HTML styles.
+            You can combine both syntaxes together.
+
+        caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
+            List of special entities that appear in the caption, which can be specified instead of *parse_mode*.
+
+        duration (``int``, *optional*):
+            Duration of the audio in seconds
+
+        performer (``str``, *optional*):
+            Performer of the audio
+
+        title (``str``, *optional*):
+            Title of the audio
+    """
+
+    def __init__(
+        self,
+        media: Union[str, BinaryIO],
+        thumb: str = None,
+        caption: str = "",
+        parse_mode: Optional["enums.ParseMode"] = None,
+        caption_entities: List[MessageEntity] = None,
+        duration: int = 0,
+        performer: str = "",
+        title: str = ""
+    ):
+        super().__init__(media, caption, parse_mode, caption_entities)
+
+        self.thumb = thumb
+        self.duration = duration
+        self.performer = performer
+        self.title = title
diff --git a/pyrogram/types/input_media/input_media_document.py b/pyrogram/types/input_media/input_media_document.py
new file mode 100644
index 00000000000..3e4d510b950
--- /dev/null
+++ b/pyrogram/types/input_media/input_media_document.py
@@ -0,0 +1,65 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import Optional, List, Union, BinaryIO
+
+from .input_media import InputMedia
+from ..messages_and_media import MessageEntity
+from ... import enums
+
+
+class InputMediaDocument(InputMedia):
+    """A generic file to be sent inside an album.
+
+    Parameters:
+        media (``str`` | ``BinaryIO``):
+            File to send.
+            Pass a file_id as string to send a file that exists on the Telegram servers or
+            pass a file path as string to upload a new file that exists on your local machine or
+            pass a binary file-like object with its attribute “.name” set for in-memory uploads or
+            pass an HTTP URL as a string for Telegram to get a file from the Internet.
+
+        thumb (``str``):
+            Thumbnail of the file sent.
+            The thumbnail should be in JPEG format and less than 200 KB in size.
+            A thumbnail's width and height should not exceed 320 pixels.
+            Thumbnails can't be reused and can be only uploaded as a new file.
+
+        caption (``str``, *optional*):
+            Caption of the document to be sent, 0-1024 characters.
+            If not specified, the original caption is kept. Pass "" (empty string) to remove the caption.
+
+        parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+            By default, texts are parsed using both Markdown and HTML styles.
+            You can combine both syntaxes together.
+
+        caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
+            List of special entities that appear in the caption, which can be specified instead of *parse_mode*.
+    """
+
+    def __init__(
+        self,
+        media: Union[str, BinaryIO],
+        thumb: str = None,
+        caption: str = "",
+        parse_mode: Optional["enums.ParseMode"] = None,
+        caption_entities: List[MessageEntity] = None
+    ):
+        super().__init__(media, caption, parse_mode, caption_entities)
+
+        self.thumb = thumb
diff --git a/pyrogram/types/input_media/input_media_photo.py b/pyrogram/types/input_media/input_media_photo.py
new file mode 100644
index 00000000000..f4fd0e03054
--- /dev/null
+++ b/pyrogram/types/input_media/input_media_photo.py
@@ -0,0 +1,63 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import Optional, List, Union, BinaryIO
+
+from .input_media import InputMedia
+from ..messages_and_media import MessageEntity
+from ... import enums
+
+
+class InputMediaPhoto(InputMedia):
+    """A photo to be sent inside an album.
+    It is intended to be used with :obj:`~pyrogram.Client.send_media_group`.
+
+    Parameters:
+        media (``str`` | ``BinaryIO``):
+            Photo to send.
+            Pass a file_id as string to send a photo that exists on the Telegram servers or
+            pass a file path as string to upload a new photo that exists on your local machine or
+            pass a binary file-like object with its attribute “.name” set for in-memory uploads or
+            pass an HTTP URL as a string for Telegram to get a photo from the Internet.
+
+        caption (``str``, *optional*):
+            Caption of the photo to be sent, 0-1024 characters.
+            If not specified, the original caption is kept. Pass "" (empty string) to remove the caption.
+
+        parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+            By default, texts are parsed using both Markdown and HTML styles.
+            You can combine both syntaxes together.
+
+        caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
+            List of special entities that appear in the caption, which can be specified instead of *parse_mode*.
+
+        has_spoiler (``bool``, *optional*):
+            Pass True if the photo needs to be covered with a spoiler animation.
+    """
+
+    def __init__(
+        self,
+        media: Union[str, BinaryIO],
+        caption: str = "",
+        parse_mode: Optional["enums.ParseMode"] = None,
+        caption_entities: List[MessageEntity] = None,
+        has_spoiler: bool = None
+    ):
+        super().__init__(media, caption, parse_mode, caption_entities)
+
+        self.has_spoiler = has_spoiler
diff --git a/pyrogram/types/input_media/input_media_video.py b/pyrogram/types/input_media/input_media_video.py
new file mode 100644
index 00000000000..ab1823d3395
--- /dev/null
+++ b/pyrogram/types/input_media/input_media_video.py
@@ -0,0 +1,91 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import Optional, List, Union, BinaryIO
+
+from .input_media import InputMedia
+from ..messages_and_media import MessageEntity
+from ... import enums
+
+
+class InputMediaVideo(InputMedia):
+    """A video to be sent inside an album.
+    It is intended to be used with :obj:`~pyrogram.Client.send_media_group`.
+
+    Parameters:
+        media (``str`` | ``BinaryIO``):
+            Video to send.
+            Pass a file_id as string to send a video that exists on the Telegram servers or
+            pass a file path as string to upload a new video that exists on your local machine or
+            pass a binary file-like object with its attribute “.name” set for in-memory uploads or
+            pass an HTTP URL as a string for Telegram to get a video from the Internet.
+
+        thumb (``str``):
+            Thumbnail of the video sent.
+            The thumbnail should be in JPEG format and less than 200 KB in size.
+            A thumbnail's width and height should not exceed 320 pixels.
+            Thumbnails can't be reused and can be only uploaded as a new file.
+
+        caption (``str``, *optional*):
+            Caption of the video to be sent, 0-1024 characters.
+            If not specified, the original caption is kept. Pass "" (empty string) to remove the caption.
+
+        parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+            By default, texts are parsed using both Markdown and HTML styles.
+            You can combine both syntaxes together.
+
+        caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
+            List of special entities that appear in the caption, which can be specified instead of *parse_mode*.
+
+        width (``int``, *optional*):
+            Video width.
+
+        height (``int``, *optional*):
+            Video height.
+
+        duration (``int``, *optional*):
+            Video duration.
+
+        supports_streaming (``bool``, *optional*):
+            Pass True, if the uploaded video is suitable for streaming.
+
+        has_spoiler (``bool``, *optional*):
+            Pass True if the photo needs to be covered with a spoiler animation.
+    """
+
+    def __init__(
+        self,
+        media: Union[str, BinaryIO],
+        thumb: str = None,
+        caption: str = "",
+        parse_mode: Optional["enums.ParseMode"] = None,
+        caption_entities: List[MessageEntity] = None,
+        width: int = 0,
+        height: int = 0,
+        duration: int = 0,
+        supports_streaming: bool = True,
+        has_spoiler: bool = None,
+    ):
+        super().__init__(media, caption, parse_mode, caption_entities)
+
+        self.thumb = thumb
+        self.width = width
+        self.height = height
+        self.duration = duration
+        self.supports_streaming = supports_streaming
+        self.has_spoiler = has_spoiler
diff --git a/pyrogram/types/input_media/input_phone_contact.py b/pyrogram/types/input_media/input_phone_contact.py
new file mode 100644
index 00000000000..0608cf21799
--- /dev/null
+++ b/pyrogram/types/input_media/input_phone_contact.py
@@ -0,0 +1,51 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 pyrogram import raw
+from pyrogram.session.internals import MsgId
+from ..object import Object
+
+
+class InputPhoneContact(Object):
+    """A Phone Contact to be added in your Telegram address book.
+    It is intended to be used with :meth:`~pyrogram.Client.add_contacts()`
+
+    Parameters:
+        phone (``str``):
+            Contact's phone number
+
+        first_name (``str``):
+            Contact's first name
+
+        last_name (``str``, *optional*):
+            Contact's last name
+    """
+
+    def __init__(self, phone: str, first_name: str, last_name: str = ""):
+        super().__init__(None)
+
+    def __new__(cls,
+                phone: str,
+                first_name: str,
+                last_name: str = ""):
+        return raw.types.InputPhoneContact(
+            client_id=MsgId(),
+            phone="+" + phone.strip("+"),
+            first_name=first_name,
+            last_name=last_name
+        )
diff --git a/pyrogram/types/input_message_content/__init__.py b/pyrogram/types/input_message_content/__init__.py
new file mode 100644
index 00000000000..b445f3ba6aa
--- /dev/null
+++ b/pyrogram/types/input_message_content/__init__.py
@@ -0,0 +1,24 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .input_message_content import InputMessageContent
+from .input_text_message_content import InputTextMessageContent
+
+__all__ = [
+    "InputMessageContent", "InputTextMessageContent"
+]
diff --git a/pyrogram/types/input_message_content/input_message_content.py b/pyrogram/types/input_message_content/input_message_content.py
new file mode 100644
index 00000000000..2b463932595
--- /dev/null
+++ b/pyrogram/types/input_message_content/input_message_content.py
@@ -0,0 +1,40 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import pyrogram
+
+from ..object import Object
+
+"""- :obj:`~pyrogram.types.InputLocationMessageContent`
+    - :obj:`~pyrogram.types.InputVenueMessageContent`
+    - :obj:`~pyrogram.types.InputContactMessageContent`"""
+
+
+class InputMessageContent(Object):
+    """Content of a message to be sent as a result of an inline query.
+
+    Pyrogram currently supports the following types:
+
+    - :obj:`~pyrogram.types.InputTextMessageContent`
+    """
+
+    def __init__(self):
+        super().__init__()
+
+    async def write(self, client: "pyrogram.Client", reply_markup):
+        raise NotImplementedError
diff --git a/pyrogram/types/input_message_content/input_text_message_content.py b/pyrogram/types/input_message_content/input_text_message_content.py
new file mode 100644
index 00000000000..7c88f996bac
--- /dev/null
+++ b/pyrogram/types/input_message_content/input_text_message_content.py
@@ -0,0 +1,68 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import Optional, List
+
+import pyrogram
+from pyrogram import raw, types, utils, enums
+from .input_message_content import InputMessageContent
+
+
+class InputTextMessageContent(InputMessageContent):
+    """Content of a text message to be sent as the result of an inline query.
+
+    Parameters:
+        message_text (``str``):
+            Text of the message to be sent, 1-4096 characters.
+
+        parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+            By default, texts are parsed using both Markdown and HTML styles.
+            You can combine both syntaxes together.
+
+        entities (List of :obj:`~pyrogram.types.MessageEntity`):
+            List of special entities that appear in message text, which can be specified instead of *parse_mode*.
+
+        disable_web_page_preview (``bool``, *optional*):
+            Disables link previews for links in this message.
+    """
+
+    def __init__(
+        self,
+        message_text: str,
+        parse_mode: Optional["enums.ParseMode"] = None,
+        entities: List["types.MessageEntity"] = None,
+        disable_web_page_preview: bool = None
+    ):
+        super().__init__()
+
+        self.message_text = message_text
+        self.parse_mode = parse_mode
+        self.entities = entities
+        self.disable_web_page_preview = disable_web_page_preview
+
+    async def write(self, client: "pyrogram.Client", reply_markup):
+        message, entities = (await utils.parse_text_entities(
+            client, self.message_text, self.parse_mode, self.entities
+        )).values()
+
+        return raw.types.InputBotInlineMessageText(
+            no_webpage=self.disable_web_page_preview or None,
+            reply_markup=await reply_markup.write(client) if reply_markup else None,
+            message=message,
+            entities=entities
+        )
diff --git a/pyrogram/types/list.py b/pyrogram/types/list.py
new file mode 100644
index 00000000000..da87d5275f6
--- /dev/null
+++ b/pyrogram/types/list.py
@@ -0,0 +1,30 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .object import Object
+
+
+class List(list):
+    __slots__ = []
+
+    def __str__(self):
+        # noinspection PyCallByClass
+        return Object.__str__(self)
+
+    def __repr__(self):
+        return f"pyrogram.types.List([{','.join(Object.__repr__(i) for i in self)}])"
diff --git a/pyrogram/types/messages_and_media/__init__.py b/pyrogram/types/messages_and_media/__init__.py
new file mode 100644
index 00000000000..54dfd560438
--- /dev/null
+++ b/pyrogram/types/messages_and_media/__init__.py
@@ -0,0 +1,47 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .animation import Animation
+from .audio import Audio
+from .contact import Contact
+from .dice import Dice
+from .document import Document
+from .game import Game
+from .location import Location
+from .message import Message
+from .message_entity import MessageEntity
+from .photo import Photo
+from .poll import Poll
+from .poll_option import PollOption
+from .reaction import Reaction
+from .sticker import Sticker
+from .stripped_thumbnail import StrippedThumbnail
+from .thumbnail import Thumbnail
+from .venue import Venue
+from .video import Video
+from .video_note import VideoNote
+from .voice import Voice
+from .web_app_data import WebAppData
+from .web_page import WebPage
+from .message_reactions import MessageReactions
+
+__all__ = [
+    "Animation", "Audio", "Contact", "Document", "Game", "Location", "Message", "MessageEntity", "Photo", "Thumbnail",
+    "StrippedThumbnail", "Poll", "PollOption", "Sticker", "Venue", "Video", "VideoNote", "Voice", "WebPage", "Dice",
+    "Reaction", "WebAppData", "MessageReactions"
+]
diff --git a/pyrogram/types/messages_and_media/animation.py b/pyrogram/types/messages_and_media/animation.py
new file mode 100644
index 00000000000..1e7bf4cf395
--- /dev/null
+++ b/pyrogram/types/messages_and_media/animation.py
@@ -0,0 +1,121 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 datetime import datetime
+from typing import List
+
+import pyrogram
+from pyrogram import raw, utils
+from pyrogram import types
+from pyrogram.file_id import FileId, FileType, FileUniqueId, FileUniqueType
+from ..object import Object
+
+
+class Animation(Object):
+    """An animation file (GIF or H.264/MPEG-4 AVC video without sound).
+
+    Parameters:
+        file_id (``str``):
+            Identifier for this file, which can be used to download or reuse the file.
+
+        file_unique_id (``str``):
+            Unique identifier for this file, which is supposed to be the same over time and for different accounts.
+            Can't be used to download or reuse the file.
+
+        width (``int``):
+            Animation width as defined by sender.
+
+        height (``int``):
+            Animation height as defined by sender.
+
+        duration (``int``):
+            Duration of the animation in seconds as defined by sender.
+
+        file_name (``str``, *optional*):
+            Animation file name.
+
+        mime_type (``str``, *optional*):
+            Mime type of a file as defined by sender.
+
+        file_size (``int``, *optional*):
+            File size.
+
+        date (:py:obj:`~datetime.datetime`, *optional*):
+            Date the animation was sent.
+
+        thumbs (List of :obj:`~pyrogram.types.Thumbnail`, *optional*):
+            Animation thumbnails.
+    """
+
+    def __init__(
+        self,
+        *,
+        client: "pyrogram.Client" = None,
+        file_id: str,
+        file_unique_id: str,
+        width: int,
+        height: int,
+        duration: int,
+        file_name: str = None,
+        mime_type: str = None,
+        file_size: int = None,
+        date: datetime = None,
+        thumbs: List["types.Thumbnail"] = None
+    ):
+        super().__init__(client)
+
+        self.file_id = file_id
+        self.file_unique_id = file_unique_id
+        self.file_name = file_name
+        self.mime_type = mime_type
+        self.file_size = file_size
+        self.date = date
+        self.width = width
+        self.height = height
+        self.duration = duration
+        self.thumbs = thumbs
+
+    @staticmethod
+    def _parse(
+        client,
+        animation: "raw.types.Document",
+        video_attributes: "raw.types.DocumentAttributeVideo",
+        file_name: str
+    ) -> "Animation":
+        return Animation(
+            file_id=FileId(
+                file_type=FileType.ANIMATION,
+                dc_id=animation.dc_id,
+                media_id=animation.id,
+                access_hash=animation.access_hash,
+                file_reference=animation.file_reference
+            ).encode(),
+            file_unique_id=FileUniqueId(
+                file_unique_type=FileUniqueType.DOCUMENT,
+                media_id=animation.id
+            ).encode(),
+            width=getattr(video_attributes, "w", 0),
+            height=getattr(video_attributes, "h", 0),
+            duration=getattr(video_attributes, "duration", 0),
+            mime_type=animation.mime_type,
+            file_size=animation.size,
+            file_name=file_name,
+            date=utils.timestamp_to_datetime(animation.date),
+            thumbs=types.Thumbnail._parse(client, animation),
+            client=client
+        )
diff --git a/pyrogram/types/messages_and_media/audio.py b/pyrogram/types/messages_and_media/audio.py
new file mode 100644
index 00000000000..e474282f0b9
--- /dev/null
+++ b/pyrogram/types/messages_and_media/audio.py
@@ -0,0 +1,121 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 datetime import datetime
+from typing import List
+
+import pyrogram
+from pyrogram import raw, utils
+from pyrogram import types
+from pyrogram.file_id import FileId, FileType, FileUniqueId, FileUniqueType
+from ..object import Object
+
+
+class Audio(Object):
+    """An audio file to be treated as music by the Telegram clients.
+
+    Parameters:
+        file_id (``str``):
+            Identifier for this file, which can be used to download or reuse the file.
+
+        file_unique_id (``str``):
+            Unique identifier for this file, which is supposed to be the same over time and for different accounts.
+            Can't be used to download or reuse the file.
+
+        duration (``int``):
+            Duration of the audio in seconds as defined by sender.
+
+        performer (``str``, *optional*):
+            Performer of the audio as defined by sender or by audio tags.
+
+        title (``str``, *optional*):
+            Title of the audio as defined by sender or by audio tags.
+
+        file_name (``str``, *optional*):
+            Audio file name.
+
+        mime_type (``str``, *optional*):
+            MIME type of the file as defined by sender.
+
+        file_size (``int``, *optional*):
+            File size.
+
+        date (:py:obj:`~datetime.datetime`, *optional*):
+            Date the audio was originally sent.
+
+        thumbs (List of :obj:`~pyrogram.types.Thumbnail`, *optional*):
+            Thumbnails of the music file album cover.
+    """
+
+    def __init__(
+        self,
+        *,
+        client: "pyrogram.Client" = None,
+        file_id: str,
+        file_unique_id: str,
+        duration: int,
+        performer: str = None,
+        title: str = None,
+        file_name: str = None,
+        mime_type: str = None,
+        file_size: int = None,
+        date: datetime = None,
+        thumbs: List["types.Thumbnail"] = None
+    ):
+        super().__init__(client)
+
+        self.file_id = file_id
+        self.file_unique_id = file_unique_id
+        self.duration = duration
+        self.performer = performer
+        self.title = title
+        self.file_name = file_name
+        self.mime_type = mime_type
+        self.file_size = file_size
+        self.date = date
+        self.thumbs = thumbs
+
+    @staticmethod
+    def _parse(
+        client,
+        audio: "raw.types.Document",
+        audio_attributes: "raw.types.DocumentAttributeAudio",
+        file_name: str
+    ) -> "Audio":
+        return Audio(
+            file_id=FileId(
+                file_type=FileType.AUDIO,
+                dc_id=audio.dc_id,
+                media_id=audio.id,
+                access_hash=audio.access_hash,
+                file_reference=audio.file_reference
+            ).encode(),
+            file_unique_id=FileUniqueId(
+                file_unique_type=FileUniqueType.DOCUMENT,
+                media_id=audio.id
+            ).encode(),
+            duration=audio_attributes.duration,
+            performer=audio_attributes.performer,
+            title=audio_attributes.title,
+            mime_type=audio.mime_type,
+            file_size=audio.size,
+            file_name=file_name,
+            date=utils.timestamp_to_datetime(audio.date),
+            thumbs=types.Thumbnail._parse(client, audio),
+            client=client
+        )
diff --git a/pyrogram/client/types/messages_and_media/contact.py b/pyrogram/types/messages_and_media/contact.py
similarity index 50%
rename from pyrogram/client/types/messages_and_media/contact.py
rename to pyrogram/types/messages_and_media/contact.py
index e2fba707652..cec03329a5c 100644
--- a/pyrogram/client/types/messages_and_media/contact.py
+++ b/pyrogram/types/messages_and_media/contact.py
@@ -1,29 +1,28 @@
-# Pyrogram - Telegram MTProto API Client Library for Python
-# Copyright (C) 2017-2019 Dan Tès 
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
 #
-# This file is part of Pyrogram.
+#  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 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.
+#  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 .
+#  You should have received a copy of the GNU Lesser General Public License
+#  along with Pyrogram.  If not, see .
 
 import pyrogram
+from pyrogram import raw
+from ..object import Object
 
-from pyrogram.api import types
-from ..pyrogram_type import PyrogramType
 
-
-class Contact(PyrogramType):
-    """This object represents a phone contact.
+class Contact(Object):
+    """A phone contact.
 
     Parameters:
         phone_number (``str``):
@@ -42,12 +41,10 @@ class Contact(PyrogramType):
             Additional data about the contact in the form of a vCard.
     """
 
-    __slots__ = ["phone_number", "first_name", "last_name", "user_id", "vcard"]
-
     def __init__(
         self,
         *,
-        client: "pyrogram.client.ext.BaseClient",
+        client: "pyrogram.Client" = None,
         phone_number: str,
         first_name: str,
         last_name: str = None,
@@ -63,7 +60,7 @@ def __init__(
         self.vcard = vcard
 
     @staticmethod
-    def _parse(client, contact: types.MessageMediaContact) -> "Contact":
+    def _parse(client: "pyrogram.Client", contact: "raw.types.MessageMediaContact") -> "Contact":
         return Contact(
             phone_number=contact.phone_number,
             first_name=contact.first_name,
diff --git a/pyrogram/types/messages_and_media/dice.py b/pyrogram/types/messages_and_media/dice.py
new file mode 100644
index 00000000000..2c683ec8287
--- /dev/null
+++ b/pyrogram/types/messages_and_media/dice.py
@@ -0,0 +1,47 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import pyrogram
+from pyrogram import raw
+from ..object import Object
+
+
+class Dice(Object):
+    """A dice with a random value from 1 to 6 for currently supported base emoji.
+
+    Parameters:
+        emoji (``string``):
+            Emoji on which the dice throw animation is based.
+
+        value (``int``):
+            Value of the dice, 1-6 for currently supported base emoji.
+    """
+
+    def __init__(self, *, client: "pyrogram.Client" = None, emoji: str, value: int):
+        super().__init__(client)
+
+        self.emoji = emoji
+        self.value = value
+
+    @staticmethod
+    def _parse(client, dice: "raw.types.MessageMediaDice") -> "Dice":
+        return Dice(
+            emoji=dice.emoticon,
+            value=dice.value,
+            client=client
+        )
diff --git a/pyrogram/types/messages_and_media/document.py b/pyrogram/types/messages_and_media/document.py
new file mode 100644
index 00000000000..95ebe3f2891
--- /dev/null
+++ b/pyrogram/types/messages_and_media/document.py
@@ -0,0 +1,98 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 datetime import datetime
+from typing import List
+
+import pyrogram
+from pyrogram import raw, utils
+from pyrogram import types
+from pyrogram.file_id import FileId, FileType, FileUniqueId, FileUniqueType
+from ..object import Object
+
+
+class Document(Object):
+    """A generic file (as opposed to photos, voice messages, audio files, ...).
+
+    Parameters:
+        file_id (``str``):
+            Identifier for this file, which can be used to download or reuse the file.
+
+        file_unique_id (``str``):
+            Unique identifier for this file, which is supposed to be the same over time and for different accounts.
+            Can't be used to download or reuse the file.
+
+        file_name (``str``, *optional*):
+            Original filename as defined by sender.
+
+        mime_type (``str``, *optional*):
+            MIME type of the file as defined by sender.
+
+        file_size (``int``, *optional*):
+            File size.
+
+        date (:py:obj:`~datetime.datetime`, *optional*):
+            Date the document was sent.
+
+        thumbs (List of :obj:`~pyrogram.types.Thumbnail`, *optional*):
+            Document thumbnails as defined by sender.
+    """
+
+    def __init__(
+        self,
+        *,
+        client: "pyrogram.Client" = None,
+        file_id: str,
+        file_unique_id: str,
+        file_name: str = None,
+        mime_type: str = None,
+        file_size: int = None,
+        date: datetime = None,
+        thumbs: List["types.Thumbnail"] = None
+    ):
+        super().__init__(client)
+
+        self.file_id = file_id
+        self.file_unique_id = file_unique_id
+        self.file_name = file_name
+        self.mime_type = mime_type
+        self.file_size = file_size
+        self.date = date
+        self.thumbs = thumbs
+
+    @staticmethod
+    def _parse(client, document: "raw.types.Document", file_name: str) -> "Document":
+        return Document(
+            file_id=FileId(
+                file_type=FileType.DOCUMENT,
+                dc_id=document.dc_id,
+                media_id=document.id,
+                access_hash=document.access_hash,
+                file_reference=document.file_reference
+            ).encode(),
+            file_unique_id=FileUniqueId(
+                file_unique_type=FileUniqueType.DOCUMENT,
+                media_id=document.id
+            ).encode(),
+            file_name=file_name,
+            mime_type=document.mime_type,
+            file_size=document.size,
+            date=utils.timestamp_to_datetime(document.date),
+            thumbs=types.Thumbnail._parse(client, document),
+            client=client
+        )
diff --git a/pyrogram/types/messages_and_media/game.py b/pyrogram/types/messages_and_media/game.py
new file mode 100644
index 00000000000..1452d79eb32
--- /dev/null
+++ b/pyrogram/types/messages_and_media/game.py
@@ -0,0 +1,99 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import pyrogram
+from pyrogram import raw
+from pyrogram import types
+from ..object import Object
+
+
+class Game(Object):
+    """A game.
+    Use BotFather to create and edit games, their short names will act as unique identifiers.
+
+    Parameters:
+        id (``int``):
+            Unique identifier of the game.
+
+        title (``str``):
+            Title of the game.
+
+        short_name (``str``):
+            Unique short name of the game.
+
+        description (``str``):
+            Description of the game.
+
+        photo (:obj:`~pyrogram.types.Photo`):
+            Photo that will be displayed in the game message in chats.
+
+        animation (:obj:`~pyrogram.types.Animation`, *optional*):
+            Animation that will be displayed in the game message in chats.
+            Upload via BotFather.
+    """
+
+    def __init__(
+        self,
+        *,
+        client: "pyrogram.Client" = None,
+        id: int,
+        title: str,
+        short_name: str,
+        description: str,
+        photo: "types.Photo",
+        animation: "types.Animation" = None
+    ):
+        super().__init__(client)
+
+        self.id = id
+        self.title = title
+        self.short_name = short_name
+        self.description = description
+        self.photo = photo
+        self.animation = animation
+
+    @staticmethod
+    def _parse(client, message: "raw.types.Message") -> "Game":
+        game: "raw.types.Game" = message.media.game
+        animation = None
+
+        if game.document:
+            attributes = {type(i): i for i in game.document.attributes}
+
+            file_name = getattr(
+                attributes.get(
+                    raw.types.DocumentAttributeFilename, None
+                ), "file_name", None
+            )
+
+            animation = types.Animation._parse(
+                client,
+                game.document,
+                attributes.get(raw.types.DocumentAttributeVideo, None),
+                file_name
+            )
+
+        return Game(
+            id=game.id,
+            title=game.title,
+            short_name=game.short_name,
+            description=game.description,
+            photo=types.Photo._parse(client, game.photo),
+            animation=animation,
+            client=client
+        )
diff --git a/pyrogram/types/messages_and_media/location.py b/pyrogram/types/messages_and_media/location.py
new file mode 100644
index 00000000000..664890cb763
--- /dev/null
+++ b/pyrogram/types/messages_and_media/location.py
@@ -0,0 +1,55 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import pyrogram
+
+from pyrogram import raw
+from ..object import Object
+
+
+class Location(Object):
+    """A point on the map.
+
+    Parameters:
+        longitude (``float``):
+            Longitude as defined by sender.
+
+        latitude (``float``):
+            Latitude as defined by sender.
+    """
+
+    def __init__(
+        self,
+        *,
+        client: "pyrogram.Client" = None,
+        longitude: float,
+        latitude: float
+    ):
+        super().__init__(client)
+
+        self.longitude = longitude
+        self.latitude = latitude
+
+    @staticmethod
+    def _parse(client, geo_point: "raw.types.GeoPoint") -> "Location":
+        if isinstance(geo_point, raw.types.GeoPoint):
+            return Location(
+                longitude=geo_point.long,
+                latitude=geo_point.lat,
+                client=client
+            )
diff --git a/pyrogram/types/messages_and_media/message.py b/pyrogram/types/messages_and_media/message.py
new file mode 100644
index 00000000000..62a85b75930
--- /dev/null
+++ b/pyrogram/types/messages_and_media/message.py
@@ -0,0 +1,3590 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import logging
+from datetime import datetime
+from functools import partial
+from typing import List, Match, Union, BinaryIO, Optional, Callable
+
+import pyrogram
+from pyrogram import raw, enums
+from pyrogram import types
+from pyrogram import utils
+from pyrogram.errors import MessageIdsEmpty, PeerIdInvalid
+from pyrogram.parser import utils as parser_utils, Parser
+from ..object import Object
+from ..update import Update
+
+log = logging.getLogger(__name__)
+
+
+class Str(str):
+    def __init__(self, *args):
+        super().__init__()
+
+        self.entities = None
+
+    def init(self, entities):
+        self.entities = entities
+
+        return self
+
+    @property
+    def markdown(self):
+        return Parser.unparse(self, self.entities, False)
+
+    @property
+    def html(self):
+        return Parser.unparse(self, self.entities, True)
+
+    def __getitem__(self, item):
+        return parser_utils.remove_surrogates(parser_utils.add_surrogates(self)[item])
+
+
+class Message(Object, Update):
+    """A message.
+
+    Parameters:
+        id (``int``):
+            Unique message identifier inside this chat.
+
+        from_user (:obj:`~pyrogram.types.User`, *optional*):
+            Sender, empty for messages sent to channels.
+
+        sender_chat (:obj:`~pyrogram.types.Chat`, *optional*):
+            Sender of the message, sent on behalf of a chat.
+            The channel itself for channel messages.
+            The supergroup itself for messages from anonymous group administrators.
+            The linked channel for messages automatically forwarded to the discussion group.
+
+        date (:py:obj:`~datetime.datetime`, *optional*):
+            Date the message was sent.
+
+        chat (:obj:`~pyrogram.types.Chat`, *optional*):
+            Conversation the message belongs to.
+
+        forward_from (:obj:`~pyrogram.types.User`, *optional*):
+            For forwarded messages, sender of the original message.
+
+        forward_sender_name (``str``, *optional*):
+            For messages forwarded from users who have hidden their accounts, name of the user.
+
+        forward_from_chat (:obj:`~pyrogram.types.Chat`, *optional*):
+            For messages forwarded from channels, information about the original channel. For messages forwarded from anonymous group administrators, information about the original supergroup.
+
+        forward_from_message_id (``int``, *optional*):
+            For messages forwarded from channels, identifier of the original message in the channel.
+
+        forward_signature (``str``, *optional*):
+            For messages forwarded from channels, signature of the post author if present.
+
+        forward_date (:py:obj:`~datetime.datetime`, *optional*):
+            For forwarded messages, date the original message was sent.
+
+        reply_to_message_id (``int``, *optional*):
+            The id of the message which this message directly replied to.
+
+        reply_to_top_message_id (``int``, *optional*):
+            The id of the first message which started this message thread.
+
+        reply_to_message (:obj:`~pyrogram.types.Message`, *optional*):
+            For replies, the original message. Note that the Message object in this field will not contain
+            further reply_to_message fields even if it itself is a reply.
+
+        mentioned (``bool``, *optional*):
+            The message contains a mention.
+
+        empty (``bool``, *optional*):
+            The message is empty.
+            A message can be empty in case it was deleted or you tried to retrieve a message that doesn't exist yet.
+
+        service (:obj:`~pyrogram.enums.MessageServiceType`, *optional*):
+            The message is a service message.
+            This field will contain the enumeration type of the service message.
+            You can use ``service = getattr(message, message.service.value)`` to access the service message.
+
+        media (:obj:`~pyrogram.enums.MessageMediaType`, *optional*):
+            The message is a media message.
+            This field will contain the enumeration type of the media message.
+            You can use ``media = getattr(message, message.media.value)`` to access the media message.
+
+        edit_date (:py:obj:`~datetime.datetime`, *optional*):
+            Date the message was last edited.
+
+        media_group_id (``str``, *optional*):
+            The unique identifier of a media message group this message belongs to.
+
+        author_signature (``str``, *optional*):
+            Signature of the post author for messages in channels, or the custom title of an anonymous group
+            administrator.
+
+        has_protected_content (``bool``, *optional*):
+            True, if the message can't be forwarded.
+
+        has_media_spoiler (``bool``, *optional*):
+            True, if the message media is covered by a spoiler animation.
+
+        text (``str``, *optional*):
+            For text messages, the actual UTF-8 text of the message, 0-4096 characters.
+            If the message contains entities (bold, italic, ...) you can access *text.markdown* or
+            *text.html* to get the marked up message text. In case there is no entity, the fields
+            will contain the same text as *text*.
+
+        entities (List of :obj:`~pyrogram.types.MessageEntity`, *optional*):
+            For text messages, special entities like usernames, URLs, bot commands, etc. that appear in the text.
+
+        caption_entities (List of :obj:`~pyrogram.types.MessageEntity`, *optional*):
+            For messages with a caption, special entities like usernames, URLs, bot commands, etc. that appear
+            in the caption.
+
+        audio (:obj:`~pyrogram.types.Audio`, *optional*):
+            Message is an audio file, information about the file.
+
+        document (:obj:`~pyrogram.types.Document`, *optional*):
+            Message is a general file, information about the file.
+
+        photo (:obj:`~pyrogram.types.Photo`, *optional*):
+            Message is a photo, information about the photo.
+
+        sticker (:obj:`~pyrogram.types.Sticker`, *optional*):
+            Message is a sticker, information about the sticker.
+
+        animation (:obj:`~pyrogram.types.Animation`, *optional*):
+            Message is an animation, information about the animation.
+
+        game (:obj:`~pyrogram.types.Game`, *optional*):
+            Message is a game, information about the game.
+
+        video (:obj:`~pyrogram.types.Video`, *optional*):
+            Message is a video, information about the video.
+
+        voice (:obj:`~pyrogram.types.Voice`, *optional*):
+            Message is a voice message, information about the file.
+
+        video_note (:obj:`~pyrogram.types.VideoNote`, *optional*):
+            Message is a video note, information about the video message.
+
+        caption (``str``, *optional*):
+            Caption for the audio, document, photo, video or voice, 0-1024 characters.
+            If the message contains caption entities (bold, italic, ...) you can access *caption.markdown* or
+            *caption.html* to get the marked up caption text. In case there is no caption entity, the fields
+            will contain the same text as *caption*.
+
+        contact (:obj:`~pyrogram.types.Contact`, *optional*):
+            Message is a shared contact, information about the contact.
+
+        location (:obj:`~pyrogram.types.Location`, *optional*):
+            Message is a shared location, information about the location.
+
+        venue (:obj:`~pyrogram.types.Venue`, *optional*):
+            Message is a venue, information about the venue.
+
+        web_page (:obj:`~pyrogram.types.WebPage`, *optional*):
+            Message was sent with a webpage preview.
+
+        poll (:obj:`~pyrogram.types.Poll`, *optional*):
+            Message is a native poll, information about the poll.
+
+        dice (:obj:`~pyrogram.types.Dice`, *optional*):
+            A dice containing a value that is randomly generated by Telegram.
+
+        new_chat_members (List of :obj:`~pyrogram.types.User`, *optional*):
+            New members that were added to the group or supergroup and information about them
+            (the bot itself may be one of these members).
+
+        left_chat_member (:obj:`~pyrogram.types.User`, *optional*):
+            A member was removed from the group, information about them (this member may be the bot itself).
+
+        new_chat_title (``str``, *optional*):
+            A chat title was changed to this value.
+
+        new_chat_photo (:obj:`~pyrogram.types.Photo`, *optional*):
+            A chat photo was change to this value.
+
+        delete_chat_photo (``bool``, *optional*):
+            Service message: the chat photo was deleted.
+
+        group_chat_created (``bool``, *optional*):
+            Service message: the group has been created.
+
+        supergroup_chat_created (``bool``, *optional*):
+            Service message: the supergroup has been created.
+            This field can't be received in a message coming through updates, because bot can't be a member of a
+            supergroup when it is created. It can only be found in reply_to_message if someone replies to a very
+            first message in a directly created supergroup.
+
+        channel_chat_created (``bool``, *optional*):
+            Service message: the channel has been created.
+            This field can't be received in a message coming through updates, because bot can't be a member of a
+            channel when it is created. It can only be found in reply_to_message if someone replies to a very
+            first message in a channel.
+
+        migrate_to_chat_id (``int``, *optional*):
+            The group has been migrated to a supergroup with the specified identifier.
+            This number may be greater than 32 bits and some programming languages may have difficulty/silent defects
+            in interpreting it. But it is smaller than 52 bits, so a signed 64 bit integer or double-precision float
+            type are safe for storing this identifier.
+
+        migrate_from_chat_id (``int``, *optional*):
+            The supergroup has been migrated from a group with the specified identifier.
+            This number may be greater than 32 bits and some programming languages may have difficulty/silent defects
+            in interpreting it. But it is smaller than 52 bits, so a signed 64 bit integer or double-precision float
+            type are safe for storing this identifier.
+
+        pinned_message (:obj:`~pyrogram.types.Message`, *optional*):
+            Specified message was pinned.
+            Note that the Message object in this field will not contain further reply_to_message fields even if it
+            is itself a reply.
+
+        game_high_score (:obj:`~pyrogram.types.GameHighScore`, *optional*):
+            The game score for a user.
+            The reply_to_message field will contain the game Message.
+
+        views (``int``, *optional*):
+            Channel post views.
+	    
+	forwards (``int``, *optional*):
+            Channel post forwards.
+
+        via_bot (:obj:`~pyrogram.types.User`):
+            The information of the bot that generated the message from an inline query of a user.
+
+        outgoing (``bool``, *optional*):
+            Whether the message is incoming or outgoing.
+            Messages received from other chats are incoming (*outgoing* is False).
+            Messages sent from yourself to other chats are outgoing (*outgoing* is True).
+            An exception is made for your own personal chat; messages sent there will be incoming.
+
+        matches (List of regex Matches, *optional*):
+            A list containing all `Match Objects `_ that match
+            the text of this message. Only applicable when using :obj:`Filters.regex `.
+
+        command (List of ``str``, *optional*):
+            A list containing the command and its arguments, if any.
+            E.g.: "/start 1 2 3" would produce ["start", "1", "2", "3"].
+            Only applicable when using :obj:`~pyrogram.filters.command`.
+
+        video_chat_scheduled (:obj:`~pyrogram.types.VideoChatScheduled`, *optional*):
+            Service message: voice chat scheduled.
+
+        video_chat_started (:obj:`~pyrogram.types.VideoChatStarted`, *optional*):
+            Service message: the voice chat started.
+
+        video_chat_ended (:obj:`~pyrogram.types.VideoChatEnded`, *optional*):
+            Service message: the voice chat has ended.
+
+        video_chat_members_invited (:obj:`~pyrogram.types.VoiceChatParticipantsInvited`, *optional*):
+            Service message: new members were invited to the voice chat.
+
+        web_app_data (:obj:`~pyrogram.types.WebAppData`, *optional*):
+            Service message: web app data sent to the bot.
+
+        reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*):
+            Additional interface options. An object for an inline keyboard, custom reply keyboard,
+            instructions to remove reply keyboard or to force a reply from the user.
+
+        reactions (List of :obj:`~pyrogram.types.Reaction`):
+            List of the reactions to this message.
+
+        link (``str``, *property*):
+            Generate a link to this message, only for groups and channels.
+    """
+
+    # TODO: Add game missing field. Also invoice, successful_payment, connected_website
+
+    def __init__(
+        self,
+        *,
+        client: "pyrogram.Client" = None,
+        id: int,
+        from_user: "types.User" = None,
+        sender_chat: "types.Chat" = None,
+        date: datetime = None,
+        chat: "types.Chat" = None,
+        forward_from: "types.User" = None,
+        forward_sender_name: str = None,
+        forward_from_chat: "types.Chat" = None,
+        forward_from_message_id: int = None,
+        forward_signature: str = None,
+        forward_date: datetime = None,
+        reply_to_message_id: int = None,
+        reply_to_top_message_id: int = None,
+        reply_to_message: "Message" = None,
+        mentioned: bool = None,
+        empty: bool = None,
+        service: "enums.MessageServiceType" = None,
+        scheduled: bool = None,
+        from_scheduled: bool = None,
+        media: "enums.MessageMediaType" = None,
+        edit_date: datetime = None,
+        media_group_id: str = None,
+        author_signature: str = None,
+        has_protected_content: bool = None,
+        has_media_spoiler: bool = None,
+        text: Str = None,
+        entities: List["types.MessageEntity"] = None,
+        caption_entities: List["types.MessageEntity"] = None,
+        audio: "types.Audio" = None,
+        document: "types.Document" = None,
+        photo: "types.Photo" = None,
+        sticker: "types.Sticker" = None,
+        animation: "types.Animation" = None,
+        game: "types.Game" = None,
+        video: "types.Video" = None,
+        voice: "types.Voice" = None,
+        video_note: "types.VideoNote" = None,
+        caption: Str = None,
+        contact: "types.Contact" = None,
+        location: "types.Location" = None,
+        venue: "types.Venue" = None,
+        web_page: "types.WebPage" = None,
+        poll: "types.Poll" = None,
+        dice: "types.Dice" = None,
+        new_chat_members: List["types.User"] = None,
+        left_chat_member: "types.User" = None,
+        new_chat_title: str = None,
+        new_chat_photo: "types.Photo" = None,
+        delete_chat_photo: bool = None,
+        group_chat_created: bool = None,
+        supergroup_chat_created: bool = None,
+        channel_chat_created: bool = None,
+        migrate_to_chat_id: int = None,
+        migrate_from_chat_id: int = None,
+        pinned_message: "Message" = None,
+        game_high_score: int = None,
+        views: int = None,
+        forwards: int = None,
+        via_bot: "types.User" = None,
+        outgoing: bool = None,
+        matches: List[Match] = None,
+        command: List[str] = None,
+        video_chat_scheduled: "types.VideoChatScheduled" = None,
+        video_chat_started: "types.VideoChatStarted" = None,
+        video_chat_ended: "types.VideoChatEnded" = None,
+        video_chat_members_invited: "types.VideoChatMembersInvited" = None,
+        web_app_data: "types.WebAppData" = None,
+        reply_markup: Union[
+            "types.InlineKeyboardMarkup",
+            "types.ReplyKeyboardMarkup",
+            "types.ReplyKeyboardRemove",
+            "types.ForceReply"
+        ] = None,
+        reactions: List["types.Reaction"] = None
+    ):
+        super().__init__(client)
+
+        self.id = id
+        self.from_user = from_user
+        self.sender_chat = sender_chat
+        self.date = date
+        self.chat = chat
+        self.forward_from = forward_from
+        self.forward_sender_name = forward_sender_name
+        self.forward_from_chat = forward_from_chat
+        self.forward_from_message_id = forward_from_message_id
+        self.forward_signature = forward_signature
+        self.forward_date = forward_date
+        self.reply_to_message_id = reply_to_message_id
+        self.reply_to_top_message_id = reply_to_top_message_id
+        self.reply_to_message = reply_to_message
+        self.mentioned = mentioned
+        self.empty = empty
+        self.service = service
+        self.scheduled = scheduled
+        self.from_scheduled = from_scheduled
+        self.media = media
+        self.edit_date = edit_date
+        self.media_group_id = media_group_id
+        self.author_signature = author_signature
+        self.has_protected_content = has_protected_content
+        self.has_media_spoiler = has_media_spoiler
+        self.text = text
+        self.entities = entities
+        self.caption_entities = caption_entities
+        self.audio = audio
+        self.document = document
+        self.photo = photo
+        self.sticker = sticker
+        self.animation = animation
+        self.game = game
+        self.video = video
+        self.voice = voice
+        self.video_note = video_note
+        self.caption = caption
+        self.contact = contact
+        self.location = location
+        self.venue = venue
+        self.web_page = web_page
+        self.poll = poll
+        self.dice = dice
+        self.new_chat_members = new_chat_members
+        self.left_chat_member = left_chat_member
+        self.new_chat_title = new_chat_title
+        self.new_chat_photo = new_chat_photo
+        self.delete_chat_photo = delete_chat_photo
+        self.group_chat_created = group_chat_created
+        self.supergroup_chat_created = supergroup_chat_created
+        self.channel_chat_created = channel_chat_created
+        self.migrate_to_chat_id = migrate_to_chat_id
+        self.migrate_from_chat_id = migrate_from_chat_id
+        self.pinned_message = pinned_message
+        self.game_high_score = game_high_score
+        self.views = views
+        self.forwards = forwards
+        self.via_bot = via_bot
+        self.outgoing = outgoing
+        self.matches = matches
+        self.command = command
+        self.reply_markup = reply_markup
+        self.video_chat_scheduled = video_chat_scheduled
+        self.video_chat_started = video_chat_started
+        self.video_chat_ended = video_chat_ended
+        self.video_chat_members_invited = video_chat_members_invited
+        self.web_app_data = web_app_data
+        self.reactions = reactions
+
+    @staticmethod
+    async def _parse(
+        client: "pyrogram.Client",
+        message: raw.base.Message,
+        users: dict,
+        chats: dict,
+        is_scheduled: bool = False,
+        replies: int = 1
+    ):
+        if isinstance(message, raw.types.MessageEmpty):
+            return Message(id=message.id, empty=True, client=client)
+
+        from_id = utils.get_raw_peer_id(message.from_id)
+        peer_id = utils.get_raw_peer_id(message.peer_id)
+        user_id = from_id or peer_id
+
+        if isinstance(message.from_id, raw.types.PeerUser) and isinstance(message.peer_id, raw.types.PeerUser):
+            if from_id not in users or peer_id not in users:
+                try:
+                    r = await client.invoke(
+                        raw.functions.users.GetUsers(
+                            id=[
+                                await client.resolve_peer(from_id),
+                                await client.resolve_peer(peer_id)
+                            ]
+                        )
+                    )
+                except PeerIdInvalid:
+                    pass
+                else:
+                    users.update({i.id: i for i in r})
+
+        if isinstance(message, raw.types.MessageService):
+            action = message.action
+
+            new_chat_members = None
+            left_chat_member = None
+            new_chat_title = None
+            delete_chat_photo = None
+            migrate_to_chat_id = None
+            migrate_from_chat_id = None
+            group_chat_created = None
+            channel_chat_created = None
+            new_chat_photo = None
+            video_chat_scheduled = None
+            video_chat_started = None
+            video_chat_ended = None
+            video_chat_members_invited = None
+            web_app_data = None
+
+            service_type = None
+
+            if isinstance(action, raw.types.MessageActionChatAddUser):
+                new_chat_members = [types.User._parse(client, users[i]) for i in action.users]
+                service_type = enums.MessageServiceType.NEW_CHAT_MEMBERS
+            elif isinstance(action, raw.types.MessageActionChatJoinedByLink):
+                new_chat_members = [types.User._parse(client, users[utils.get_raw_peer_id(message.from_id)])]
+                service_type = enums.MessageServiceType.NEW_CHAT_MEMBERS
+            elif isinstance(action, raw.types.MessageActionChatDeleteUser):
+                left_chat_member = types.User._parse(client, users[action.user_id])
+                service_type = enums.MessageServiceType.LEFT_CHAT_MEMBERS
+            elif isinstance(action, raw.types.MessageActionChatEditTitle):
+                new_chat_title = action.title
+                service_type = enums.MessageServiceType.NEW_CHAT_TITLE
+            elif isinstance(action, raw.types.MessageActionChatDeletePhoto):
+                delete_chat_photo = True
+                service_type = enums.MessageServiceType.DELETE_CHAT_PHOTO
+            elif isinstance(action, raw.types.MessageActionChatMigrateTo):
+                migrate_to_chat_id = action.channel_id
+                service_type = enums.MessageServiceType.MIGRATE_TO_CHAT_ID
+            elif isinstance(action, raw.types.MessageActionChannelMigrateFrom):
+                migrate_from_chat_id = action.chat_id
+                service_type = enums.MessageServiceType.MIGRATE_FROM_CHAT_ID
+            elif isinstance(action, raw.types.MessageActionChatCreate):
+                group_chat_created = True
+                service_type = enums.MessageServiceType.GROUP_CHAT_CREATED
+            elif isinstance(action, raw.types.MessageActionChannelCreate):
+                channel_chat_created = True
+                service_type = enums.MessageServiceType.CHANNEL_CHAT_CREATED
+            elif isinstance(action, raw.types.MessageActionChatEditPhoto):
+                new_chat_photo = types.Photo._parse(client, action.photo)
+                service_type = enums.MessageServiceType.NEW_CHAT_PHOTO
+            elif isinstance(action, raw.types.MessageActionGroupCallScheduled):
+                video_chat_scheduled = types.VideoChatScheduled._parse(action)
+                service_type = enums.MessageServiceType.VIDEO_CHAT_SCHEDULED
+            elif isinstance(action, raw.types.MessageActionGroupCall):
+                if action.duration:
+                    video_chat_ended = types.VideoChatEnded._parse(action)
+                    service_type = enums.MessageServiceType.VIDEO_CHAT_ENDED
+                else:
+                    video_chat_started = types.VideoChatStarted()
+                    service_type = enums.MessageServiceType.VIDEO_CHAT_STARTED
+            elif isinstance(action, raw.types.MessageActionInviteToGroupCall):
+                video_chat_members_invited = types.VideoChatMembersInvited._parse(client, action, users)
+                service_type = enums.MessageServiceType.VIDEO_CHAT_MEMBERS_INVITED
+            elif isinstance(action, raw.types.MessageActionWebViewDataSentMe):
+                web_app_data = types.WebAppData._parse(action)
+                service_type = enums.MessageServiceType.WEB_APP_DATA
+
+            from_user = types.User._parse(client, users.get(user_id, None))
+            sender_chat = types.Chat._parse(client, message, users, chats, is_chat=False) if not from_user else None
+
+            parsed_message = Message(
+                id=message.id,
+                date=utils.timestamp_to_datetime(message.date),
+                chat=types.Chat._parse(client, message, users, chats, is_chat=True),
+                from_user=from_user,
+                sender_chat=sender_chat,
+                service=service_type,
+                new_chat_members=new_chat_members,
+                left_chat_member=left_chat_member,
+                new_chat_title=new_chat_title,
+                new_chat_photo=new_chat_photo,
+                delete_chat_photo=delete_chat_photo,
+                migrate_to_chat_id=utils.get_channel_id(migrate_to_chat_id) if migrate_to_chat_id else None,
+                migrate_from_chat_id=-migrate_from_chat_id if migrate_from_chat_id else None,
+                group_chat_created=group_chat_created,
+                channel_chat_created=channel_chat_created,
+                video_chat_scheduled=video_chat_scheduled,
+                video_chat_started=video_chat_started,
+                video_chat_ended=video_chat_ended,
+                video_chat_members_invited=video_chat_members_invited,
+                web_app_data=web_app_data,
+                client=client
+                # TODO: supergroup_chat_created
+            )
+
+            if isinstance(action, raw.types.MessageActionPinMessage):
+                try:
+                    parsed_message.pinned_message = await client.get_messages(
+                        parsed_message.chat.id,
+                        reply_to_message_ids=message.id,
+                        replies=0
+                    )
+
+                    parsed_message.service = enums.MessageServiceType.PINNED_MESSAGE
+                except MessageIdsEmpty:
+                    pass
+
+            if isinstance(action, raw.types.MessageActionGameScore):
+                parsed_message.game_high_score = types.GameHighScore._parse_action(client, message, users)
+
+                if message.reply_to and replies:
+                    try:
+                        parsed_message.reply_to_message = await client.get_messages(
+                            parsed_message.chat.id,
+                            reply_to_message_ids=message.id,
+                            replies=0
+                        )
+
+                        parsed_message.service = enums.MessageServiceType.GAME_HIGH_SCORE
+                    except MessageIdsEmpty:
+                        pass
+
+            client.message_cache[(parsed_message.chat.id, parsed_message.id)] = parsed_message
+
+            return parsed_message
+
+        if isinstance(message, raw.types.Message):
+            entities = [types.MessageEntity._parse(client, entity, users) for entity in message.entities]
+            entities = types.List(filter(lambda x: x is not None, entities))
+
+            forward_from = None
+            forward_sender_name = None
+            forward_from_chat = None
+            forward_from_message_id = None
+            forward_signature = None
+            forward_date = None
+
+            forward_header = message.fwd_from  # type: raw.types.MessageFwdHeader
+
+            if forward_header:
+                forward_date = utils.timestamp_to_datetime(forward_header.date)
+
+                if forward_header.from_id:
+                    raw_peer_id = utils.get_raw_peer_id(forward_header.from_id)
+                    peer_id = utils.get_peer_id(forward_header.from_id)
+
+                    if peer_id > 0:
+                        forward_from = types.User._parse(client, users[raw_peer_id])
+                    else:
+                        forward_from_chat = types.Chat._parse_channel_chat(client, chats[raw_peer_id])
+                        forward_from_message_id = forward_header.channel_post
+                        forward_signature = forward_header.post_author
+                elif forward_header.from_name:
+                    forward_sender_name = forward_header.from_name
+
+            photo = None
+            location = None
+            contact = None
+            venue = None
+            game = None
+            audio = None
+            voice = None
+            animation = None
+            video = None
+            video_note = None
+            sticker = None
+            document = None
+            web_page = None
+            poll = None
+            dice = None
+
+            media = message.media
+            media_type = None
+            has_media_spoiler = None
+
+            if media:
+                if isinstance(media, raw.types.MessageMediaPhoto):
+                    photo = types.Photo._parse(client, media.photo, media.ttl_seconds)
+                    media_type = enums.MessageMediaType.PHOTO
+                    has_media_spoiler = media.spoiler
+                elif isinstance(media, raw.types.MessageMediaGeo):
+                    location = types.Location._parse(client, media.geo)
+                    media_type = enums.MessageMediaType.LOCATION
+                elif isinstance(media, raw.types.MessageMediaContact):
+                    contact = types.Contact._parse(client, media)
+                    media_type = enums.MessageMediaType.CONTACT
+                elif isinstance(media, raw.types.MessageMediaVenue):
+                    venue = types.Venue._parse(client, media)
+                    media_type = enums.MessageMediaType.VENUE
+                elif isinstance(media, raw.types.MessageMediaGame):
+                    game = types.Game._parse(client, message)
+                    media_type = enums.MessageMediaType.GAME
+                elif isinstance(media, raw.types.MessageMediaDocument):
+                    doc = media.document
+
+                    if isinstance(doc, raw.types.Document):
+                        attributes = {type(i): i for i in doc.attributes}
+
+                        file_name = getattr(
+                            attributes.get(
+                                raw.types.DocumentAttributeFilename, None
+                            ), "file_name", None
+                        )
+
+                        if raw.types.DocumentAttributeAnimated in attributes:
+                            video_attributes = attributes.get(raw.types.DocumentAttributeVideo, None)
+                            animation = types.Animation._parse(client, doc, video_attributes, file_name)
+                            media_type = enums.MessageMediaType.ANIMATION
+                            has_media_spoiler = media.spoiler
+                        elif raw.types.DocumentAttributeSticker in attributes:
+                            sticker = await types.Sticker._parse(client, doc, attributes)
+                            media_type = enums.MessageMediaType.STICKER
+                        elif raw.types.DocumentAttributeVideo in attributes:
+                            video_attributes = attributes[raw.types.DocumentAttributeVideo]
+
+                            if video_attributes.round_message:
+                                video_note = types.VideoNote._parse(client, doc, video_attributes)
+                                media_type = enums.MessageMediaType.VIDEO_NOTE
+                            else:
+                                video = types.Video._parse(client, doc, video_attributes, file_name, media.ttl_seconds)
+                                media_type = enums.MessageMediaType.VIDEO
+                                has_media_spoiler = media.spoiler
+                        elif raw.types.DocumentAttributeAudio in attributes:
+                            audio_attributes = attributes[raw.types.DocumentAttributeAudio]
+
+                            if audio_attributes.voice:
+                                voice = types.Voice._parse(client, doc, audio_attributes)
+                                media_type = enums.MessageMediaType.VOICE
+                            else:
+                                audio = types.Audio._parse(client, doc, audio_attributes, file_name)
+                                media_type = enums.MessageMediaType.AUDIO
+                        else:
+                            document = types.Document._parse(client, doc, file_name)
+                            media_type = enums.MessageMediaType.DOCUMENT
+                elif isinstance(media, raw.types.MessageMediaWebPage):
+                    if isinstance(media.webpage, raw.types.WebPage):
+                        web_page = types.WebPage._parse(client, media.webpage)
+                        media_type = enums.MessageMediaType.WEB_PAGE
+                    else:
+                        media = None
+                elif isinstance(media, raw.types.MessageMediaPoll):
+                    poll = types.Poll._parse(client, media)
+                    media_type = enums.MessageMediaType.POLL
+                elif isinstance(media, raw.types.MessageMediaDice):
+                    dice = types.Dice._parse(client, media)
+                    media_type = enums.MessageMediaType.DICE
+                else:
+                    media = None
+
+            reply_markup = message.reply_markup
+
+            if reply_markup:
+                if isinstance(reply_markup, raw.types.ReplyKeyboardForceReply):
+                    reply_markup = types.ForceReply.read(reply_markup)
+                elif isinstance(reply_markup, raw.types.ReplyKeyboardMarkup):
+                    reply_markup = types.ReplyKeyboardMarkup.read(reply_markup)
+                elif isinstance(reply_markup, raw.types.ReplyInlineMarkup):
+                    reply_markup = types.InlineKeyboardMarkup.read(reply_markup)
+                elif isinstance(reply_markup, raw.types.ReplyKeyboardHide):
+                    reply_markup = types.ReplyKeyboardRemove.read(reply_markup)
+                else:
+                    reply_markup = None
+
+            from_user = types.User._parse(client, users.get(user_id, None))
+            sender_chat = types.Chat._parse(client, message, users, chats, is_chat=False) if not from_user else None
+
+            reactions = types.MessageReactions._parse(client, message.reactions)
+
+            parsed_message = Message(
+                id=message.id,
+                date=utils.timestamp_to_datetime(message.date),
+                chat=types.Chat._parse(client, message, users, chats, is_chat=True),
+                from_user=from_user,
+                sender_chat=sender_chat,
+                text=(
+                    Str(message.message).init(entities) or None
+                    if media is None or web_page is not None
+                    else None
+                ),
+                caption=(
+                    Str(message.message).init(entities) or None
+                    if media is not None and web_page is None
+                    else None
+                ),
+                entities=(
+                    entities or None
+                    if media is None or web_page is not None
+                    else None
+                ),
+                caption_entities=(
+                    entities or None
+                    if media is not None and web_page is None
+                    else None
+                ),
+                author_signature=message.post_author,
+                has_protected_content=message.noforwards,
+                has_media_spoiler=has_media_spoiler,
+                forward_from=forward_from,
+                forward_sender_name=forward_sender_name,
+                forward_from_chat=forward_from_chat,
+                forward_from_message_id=forward_from_message_id,
+                forward_signature=forward_signature,
+                forward_date=forward_date,
+                mentioned=message.mentioned,
+                scheduled=is_scheduled,
+                from_scheduled=message.from_scheduled,
+                media=media_type,
+                edit_date=utils.timestamp_to_datetime(message.edit_date),
+                media_group_id=message.grouped_id,
+                photo=photo,
+                location=location,
+                contact=contact,
+                venue=venue,
+                audio=audio,
+                voice=voice,
+                animation=animation,
+                game=game,
+                video=video,
+                video_note=video_note,
+                sticker=sticker,
+                document=document,
+                web_page=web_page,
+                poll=poll,
+                dice=dice,
+                views=message.views,
+                forwards=message.forwards,
+                via_bot=types.User._parse(client, users.get(message.via_bot_id, None)),
+                outgoing=message.out,
+                reply_markup=reply_markup,
+                reactions=reactions,
+                client=client
+            )
+
+            if message.reply_to:
+                parsed_message.reply_to_message_id = message.reply_to.reply_to_msg_id
+                parsed_message.reply_to_top_message_id = message.reply_to.reply_to_top_id
+
+                if replies:
+                    try:
+                        key = (parsed_message.chat.id, parsed_message.reply_to_message_id)
+                        reply_to_message = client.message_cache[key]
+
+                        if not reply_to_message:
+                            reply_to_message = await client.get_messages(
+                                parsed_message.chat.id,
+                                reply_to_message_ids=message.id,
+                                replies=replies - 1
+                            )
+
+                        parsed_message.reply_to_message = reply_to_message
+                    except MessageIdsEmpty:
+                        pass
+
+            if not parsed_message.poll:  # Do not cache poll messages
+                client.message_cache[(parsed_message.chat.id, parsed_message.id)] = parsed_message
+
+            return parsed_message
+
+    @property
+    def link(self) -> str:
+        if (
+            self.chat.type in (enums.ChatType.GROUP, enums.ChatType.SUPERGROUP, enums.ChatType.CHANNEL)
+            and self.chat.username
+        ):
+            return f"https://t.me/{self.chat.username}/{self.id}"
+        else:
+            return f"https://t.me/c/{utils.get_channel_id(self.chat.id)}/{self.id}"
+
+    async def get_media_group(self) -> List["types.Message"]:
+        """Bound method *get_media_group* of :obj:`~pyrogram.types.Message`.
+        
+        Use as a shortcut for:
+        
+        .. code-block:: python
+
+            await client.get_media_group(
+                chat_id=message.chat.id,
+                message_id=message.id
+            )
+            
+        Example:
+            .. code-block:: python
+
+                await message.get_media_group()
+                
+        Returns:
+            List of :obj:`~pyrogram.types.Message`: On success, a list of messages of the media group is returned.
+            
+        Raises:
+            ValueError: In case the passed message id doesn't belong to a media group.
+        """
+
+        return await self._client.get_media_group(
+            chat_id=self.chat.id,
+            message_id=self.id
+        )
+
+    async def reply_text(
+        self,
+        text: str,
+        quote: bool = None,
+        parse_mode: Optional["enums.ParseMode"] = None,
+        entities: List["types.MessageEntity"] = None,
+        disable_web_page_preview: bool = None,
+        disable_notification: bool = None,
+        reply_to_message_id: int = None,
+        schedule_date: datetime = None,
+        protect_content: bool = None,
+        reply_markup=None
+    ) -> "Message":
+        """Bound method *reply_text* of :obj:`~pyrogram.types.Message`.
+
+        An alias exists as *reply*.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.send_message(
+                chat_id=message.chat.id,
+                text="hello",
+                reply_to_message_id=message.id
+            )
+
+        Example:
+            .. code-block:: python
+
+                await message.reply_text("hello", quote=True)
+
+        Parameters:
+            text (``str``):
+                Text of the message to be sent.
+
+            quote (``bool``, *optional*):
+                If ``True``, the message will be sent as a reply to this message.
+                If *reply_to_message_id* is passed, this parameter will be ignored.
+                Defaults to ``True`` in group chats and ``False`` in private chats.
+
+            parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+                By default, texts are parsed using both Markdown and HTML styles.
+                You can combine both syntaxes together.
+
+            entities (List of :obj:`~pyrogram.types.MessageEntity`):
+                List of special entities that appear in message text, which can be specified instead of *parse_mode*.
+
+            disable_web_page_preview (``bool``, *optional*):
+                Disables link previews for links in this message.
+
+            disable_notification (``bool``, *optional*):
+                Sends the message silently.
+                Users will receive a notification with no sound.
+
+            reply_to_message_id (``int``, *optional*):
+                If the message is a reply, ID of the original message.
+
+            schedule_date (:py:obj:`~datetime.datetime`, *optional*):
+                Date when the message will be automatically sent.
+
+            protect_content (``bool``, *optional*):
+                Protects the contents of the sent message from forwarding and saving.
+
+            reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*):
+                Additional interface options. An object for an inline keyboard, custom reply keyboard,
+                instructions to remove reply keyboard or to force a reply from the user.
+
+        Returns:
+            On success, the sent Message is returned.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+        if quote is None:
+            quote = self.chat.type != enums.ChatType.PRIVATE
+
+        if reply_to_message_id is None and quote:
+            reply_to_message_id = self.id
+
+        return await self._client.send_message(
+            chat_id=self.chat.id,
+            text=text,
+            parse_mode=parse_mode,
+            entities=entities,
+            disable_web_page_preview=disable_web_page_preview,
+            disable_notification=disable_notification,
+            reply_to_message_id=reply_to_message_id,
+            schedule_date=schedule_date,
+            protect_content=protect_content,
+            reply_markup=reply_markup
+        )
+
+    reply = reply_text
+
+    async def reply_animation(
+        self,
+        animation: Union[str, BinaryIO],
+        quote: bool = None,
+        caption: str = "",
+        parse_mode: Optional["enums.ParseMode"] = None,
+        caption_entities: List["types.MessageEntity"] = None,
+        has_spoiler: bool = None,
+        duration: int = 0,
+        width: int = 0,
+        height: int = 0,
+        thumb: str = None,
+        disable_notification: bool = None,
+        reply_markup: Union[
+            "types.InlineKeyboardMarkup",
+            "types.ReplyKeyboardMarkup",
+            "types.ReplyKeyboardRemove",
+            "types.ForceReply"
+        ] = None,
+        reply_to_message_id: int = None,
+        progress: Callable = None,
+        progress_args: tuple = ()
+    ) -> "Message":
+        """Bound method *reply_animation* :obj:`~pyrogram.types.Message`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.send_animation(
+                chat_id=message.chat.id,
+                animation=animation
+            )
+
+        Example:
+            .. code-block:: python
+
+                await message.reply_animation(animation)
+
+        Parameters:
+            animation (``str``):
+                Animation to send.
+                Pass a file_id as string to send an animation that exists on the Telegram servers,
+                pass an HTTP URL as a string for Telegram to get an animation from the Internet, or
+                pass a file path as string to upload a new animation that exists on your local machine.
+
+            quote (``bool``, *optional*):
+                If ``True``, the message will be sent as a reply to this message.
+                If *reply_to_message_id* is passed, this parameter will be ignored.
+                Defaults to ``True`` in group chats and ``False`` in private chats.
+
+            caption (``str``, *optional*):
+                Animation caption, 0-1024 characters.
+
+            parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+                By default, texts are parsed using both Markdown and HTML styles.
+                You can combine both syntaxes together.
+
+            caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
+                List of special entities that appear in the caption, which can be specified instead of *parse_mode*.
+
+            has_spoiler (``bool``, *optional*):
+                Pass True if the animation needs to be covered with a spoiler animation.
+
+            duration (``int``, *optional*):
+                Duration of sent animation in seconds.
+
+            width (``int``, *optional*):
+                Animation width.
+
+            height (``int``, *optional*):
+                Animation height.
+
+            thumb (``str``, *optional*):
+                Thumbnail of the animation file sent.
+                The thumbnail should be in JPEG format and less than 200 KB in size.
+                A thumbnail's width and height should not exceed 320 pixels.
+                Thumbnails can't be reused and can be only uploaded as a new file.
+
+            disable_notification (``bool``, *optional*):
+                Sends the message silently.
+                Users will receive a notification with no sound.
+
+            reply_to_message_id (``int``, *optional*):
+                If the message is a reply, ID of the original message.
+
+            reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*):
+                Additional interface options. An object for an inline keyboard, custom reply keyboard,
+                instructions to remove reply keyboard or to force a reply from the user.
+
+            progress (``Callable``, *optional*):
+                Pass a callback function to view the file transmission progress.
+                The function must take *(current, total)* as positional arguments (look at Other Parameters below for a
+                detailed description) and will be called back each time a new file chunk has been successfully
+                transmitted.
+
+            progress_args (``tuple``, *optional*):
+                Extra custom arguments for the progress callback function.
+                You can pass anything you need to be available in the progress callback scope; for example, a Message
+                object or a Client instance in order to edit the message with the updated progress status.
+
+        Other Parameters:
+            current (``int``):
+                The amount of bytes transmitted so far.
+
+            total (``int``):
+                The total size of the file.
+
+            *args (``tuple``, *optional*):
+                Extra custom arguments as defined in the ``progress_args`` parameter.
+                You can either keep ``*args`` or add every single extra argument in your function signature.
+
+        Returns:
+            On success, the sent :obj:`~pyrogram.types.Message` is returned.
+            In case the upload is deliberately stopped with :meth:`~pyrogram.Client.stop_transmission`, None is returned
+            instead.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+        if quote is None:
+            quote = self.chat.type != enums.ChatType.PRIVATE
+
+        if reply_to_message_id is None and quote:
+            reply_to_message_id = self.id
+
+        return await self._client.send_animation(
+            chat_id=self.chat.id,
+            animation=animation,
+            caption=caption,
+            parse_mode=parse_mode,
+            caption_entities=caption_entities,
+            has_spoiler=has_spoiler,
+            duration=duration,
+            width=width,
+            height=height,
+            thumb=thumb,
+            disable_notification=disable_notification,
+            reply_to_message_id=reply_to_message_id,
+            reply_markup=reply_markup,
+            progress=progress,
+            progress_args=progress_args
+        )
+
+    async def reply_audio(
+        self,
+        audio: Union[str, BinaryIO],
+        quote: bool = None,
+        caption: str = "",
+        parse_mode: Optional["enums.ParseMode"] = None,
+        caption_entities: List["types.MessageEntity"] = None,
+        duration: int = 0,
+        performer: str = None,
+        title: str = None,
+        thumb: str = None,
+        disable_notification: bool = None,
+        reply_to_message_id: int = None,
+        reply_markup: Union[
+            "types.InlineKeyboardMarkup",
+            "types.ReplyKeyboardMarkup",
+            "types.ReplyKeyboardRemove",
+            "types.ForceReply"
+        ] = None,
+        progress: Callable = None,
+        progress_args: tuple = ()
+    ) -> "Message":
+        """Bound method *reply_audio* of :obj:`~pyrogram.types.Message`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.send_audio(
+                chat_id=message.chat.id,
+                audio=audio
+            )
+
+        Example:
+            .. code-block:: python
+
+                await message.reply_audio(audio)
+
+        Parameters:
+            audio (``str``):
+                Audio file to send.
+                Pass a file_id as string to send an audio file that exists on the Telegram servers,
+                pass an HTTP URL as a string for Telegram to get an audio file from the Internet, or
+                pass a file path as string to upload a new audio file that exists on your local machine.
+
+            quote (``bool``, *optional*):
+                If ``True``, the message will be sent as a reply to this message.
+                If *reply_to_message_id* is passed, this parameter will be ignored.
+                Defaults to ``True`` in group chats and ``False`` in private chats.
+
+            caption (``str``, *optional*):
+                Audio caption, 0-1024 characters.
+
+            parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+                By default, texts are parsed using both Markdown and HTML styles.
+                You can combine both syntaxes together.
+
+            caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
+                List of special entities that appear in the caption, which can be specified instead of *parse_mode*.
+
+            duration (``int``, *optional*):
+                Duration of the audio in seconds.
+
+            performer (``str``, *optional*):
+                Performer.
+
+            title (``str``, *optional*):
+                Track name.
+
+            thumb (``str``, *optional*):
+                Thumbnail of the music file album cover.
+                The thumbnail should be in JPEG format and less than 200 KB in size.
+                A thumbnail's width and height should not exceed 320 pixels.
+                Thumbnails can't be reused and can be only uploaded as a new file.
+
+            disable_notification (``bool``, *optional*):
+                Sends the message silently.
+                Users will receive a notification with no sound.
+
+            reply_to_message_id (``int``, *optional*):
+                If the message is a reply, ID of the original message.
+
+            reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*):
+                Additional interface options. An object for an inline keyboard, custom reply keyboard,
+                instructions to remove reply keyboard or to force a reply from the user.
+
+            progress (``Callable``, *optional*):
+                Pass a callback function to view the file transmission progress.
+                The function must take *(current, total)* as positional arguments (look at Other Parameters below for a
+                detailed description) and will be called back each time a new file chunk has been successfully
+                transmitted.
+
+            progress_args (``tuple``, *optional*):
+                Extra custom arguments for the progress callback function.
+                You can pass anything you need to be available in the progress callback scope; for example, a Message
+                object or a Client instance in order to edit the message with the updated progress status.
+
+        Other Parameters:
+            current (``int``):
+                The amount of bytes transmitted so far.
+
+            total (``int``):
+                The total size of the file.
+
+            *args (``tuple``, *optional*):
+                Extra custom arguments as defined in the ``progress_args`` parameter.
+                You can either keep ``*args`` or add every single extra argument in your function signature.
+
+        Returns:
+            On success, the sent :obj:`~pyrogram.types.Message` is returned.
+            In case the upload is deliberately stopped with :meth:`~pyrogram.Client.stop_transmission`, None is returned
+            instead.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+        if quote is None:
+            quote = self.chat.type != enums.ChatType.PRIVATE
+
+        if reply_to_message_id is None and quote:
+            reply_to_message_id = self.id
+
+        return await self._client.send_audio(
+            chat_id=self.chat.id,
+            audio=audio,
+            caption=caption,
+            parse_mode=parse_mode,
+            caption_entities=caption_entities,
+            duration=duration,
+            performer=performer,
+            title=title,
+            thumb=thumb,
+            disable_notification=disable_notification,
+            reply_to_message_id=reply_to_message_id,
+            reply_markup=reply_markup,
+            progress=progress,
+            progress_args=progress_args
+        )
+
+    async def reply_cached_media(
+        self,
+        file_id: str,
+        quote: bool = None,
+        caption: str = "",
+        parse_mode: Optional["enums.ParseMode"] = None,
+        caption_entities: List["types.MessageEntity"] = None,
+        disable_notification: bool = None,
+        reply_to_message_id: int = None,
+        reply_markup: Union[
+            "types.InlineKeyboardMarkup",
+            "types.ReplyKeyboardMarkup",
+            "types.ReplyKeyboardRemove",
+            "types.ForceReply"
+        ] = None
+    ) -> "Message":
+        """Bound method *reply_cached_media* of :obj:`~pyrogram.types.Message`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.send_cached_media(
+                chat_id=message.chat.id,
+                file_id=file_id
+            )
+
+        Example:
+            .. code-block:: python
+
+                await message.reply_cached_media(file_id)
+
+        Parameters:
+            file_id (``str``):
+                Media to send.
+                Pass a file_id as string to send a media that exists on the Telegram servers.
+
+            quote (``bool``, *optional*):
+                If ``True``, the message will be sent as a reply to this message.
+                If *reply_to_message_id* is passed, this parameter will be ignored.
+                Defaults to ``True`` in group chats and ``False`` in private chats.
+
+            caption (``bool``, *optional*):
+                Media caption, 0-1024 characters.
+
+            parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+                By default, texts are parsed using both Markdown and HTML styles.
+                You can combine both syntaxes together.
+
+            caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
+                List of special entities that appear in the caption, which can be specified instead of *parse_mode*.
+
+            disable_notification (``bool``, *optional*):
+                Sends the message silently.
+                Users will receive a notification with no sound.
+
+            reply_to_message_id (``int``, *optional*):
+                If the message is a reply, ID of the original message.
+
+            reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*):
+                Additional interface options. An object for an inline keyboard, custom reply keyboard,
+                instructions to remove reply keyboard or to force a reply from the user.
+
+        Returns:
+            On success, the sent :obj:`~pyrogram.types.Message` is returned.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+        if quote is None:
+            quote = self.chat.type != enums.ChatType.PRIVATE
+
+        if reply_to_message_id is None and quote:
+            reply_to_message_id = self.id
+
+        return await self._client.send_cached_media(
+            chat_id=self.chat.id,
+            file_id=file_id,
+            caption=caption,
+            parse_mode=parse_mode,
+            caption_entities=caption_entities,
+            disable_notification=disable_notification,
+            reply_to_message_id=reply_to_message_id,
+            reply_markup=reply_markup
+        )
+
+    async def reply_chat_action(self, action: "enums.ChatAction") -> bool:
+        """Bound method *reply_chat_action* of :obj:`~pyrogram.types.Message`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            from pyrogram import enums
+
+            await client.send_chat_action(
+                chat_id=message.chat.id,
+                action=enums.ChatAction.TYPING
+            )
+
+        Example:
+            .. code-block:: python
+
+                from pyrogram import enums
+
+                await message.reply_chat_action(enums.ChatAction.TYPING)
+
+        Parameters:
+            action (:obj:`~pyrogram.enums.ChatAction`):
+                Type of action to broadcast.
+
+        Returns:
+            ``bool``: On success, True is returned.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+            ValueError: In case the provided string is not a valid chat action.
+        """
+        return await self._client.send_chat_action(
+            chat_id=self.chat.id,
+            action=action
+        )
+
+    async def reply_contact(
+        self,
+        phone_number: str,
+        first_name: str,
+        quote: bool = None,
+        last_name: str = "",
+        vcard: str = "",
+        disable_notification: bool = None,
+        reply_to_message_id: int = None,
+        reply_markup: Union[
+            "types.InlineKeyboardMarkup",
+            "types.ReplyKeyboardMarkup",
+            "types.ReplyKeyboardRemove",
+            "types.ForceReply"
+        ] = None
+    ) -> "Message":
+        """Bound method *reply_contact* of :obj:`~pyrogram.types.Message`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.send_contact(
+                chat_id=message.chat.id,
+                phone_number=phone_number,
+                first_name=first_name
+            )
+
+        Example:
+            .. code-block:: python
+
+                await message.reply_contact("+1-123-456-7890", "Name")
+
+        Parameters:
+            phone_number (``str``):
+                Contact's phone number.
+
+            first_name (``str``):
+                Contact's first name.
+
+            quote (``bool``, *optional*):
+                If ``True``, the message will be sent as a reply to this message.
+                If *reply_to_message_id* is passed, this parameter will be ignored.
+                Defaults to ``True`` in group chats and ``False`` in private chats.
+
+            last_name (``str``, *optional*):
+                Contact's last name.
+
+            vcard (``str``, *optional*):
+                Additional data about the contact in the form of a vCard, 0-2048 bytes
+
+            disable_notification (``bool``, *optional*):
+                Sends the message silently.
+                Users will receive a notification with no sound.
+
+            reply_to_message_id (``int``, *optional*):
+                If the message is a reply, ID of the original message.
+
+            reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*):
+                Additional interface options. An object for an inline keyboard, custom reply keyboard,
+                instructions to remove reply keyboard or to force a reply from the user.
+
+        Returns:
+            On success, the sent :obj:`~pyrogram.types.Message` is returned.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+        if quote is None:
+            quote = self.chat.type != enums.ChatType.PRIVATE
+
+        if reply_to_message_id is None and quote:
+            reply_to_message_id = self.id
+
+        return await self._client.send_contact(
+            chat_id=self.chat.id,
+            phone_number=phone_number,
+            first_name=first_name,
+            last_name=last_name,
+            vcard=vcard,
+            disable_notification=disable_notification,
+            reply_to_message_id=reply_to_message_id,
+            reply_markup=reply_markup
+        )
+
+    async def reply_document(
+        self,
+        document: Union[str, BinaryIO],
+        quote: bool = None,
+        thumb: str = None,
+        caption: str = "",
+        parse_mode: Optional["enums.ParseMode"] = None,
+        caption_entities: List["types.MessageEntity"] = None,
+        file_name: str = None,
+        force_document: bool = None,
+        disable_notification: bool = None,
+        reply_to_message_id: int = None,
+        schedule_date: datetime = None,
+        reply_markup: Union[
+            "types.InlineKeyboardMarkup",
+            "types.ReplyKeyboardMarkup",
+            "types.ReplyKeyboardRemove",
+            "types.ForceReply"
+        ] = None,
+        progress: Callable = None,
+        progress_args: tuple = ()
+    ) -> "Message":
+        """Bound method *reply_document* of :obj:`~pyrogram.types.Message`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.send_document(
+                chat_id=message.chat.id,
+                document=document
+            )
+
+        Example:
+            .. code-block:: python
+
+                await message.reply_document(document)
+
+        Parameters:
+            document (``str``):
+                File to send.
+                Pass a file_id as string to send a file that exists on the Telegram servers,
+                pass an HTTP URL as a string for Telegram to get a file from the Internet, or
+                pass a file path as string to upload a new file that exists on your local machine.
+
+            quote (``bool``, *optional*):
+                If ``True``, the message will be sent as a reply to this message.
+                If *reply_to_message_id* is passed, this parameter will be ignored.
+                Defaults to ``True`` in group chats and ``False`` in private chats.
+
+            thumb (``str``, *optional*):
+                Thumbnail of the file sent.
+                The thumbnail should be in JPEG format and less than 200 KB in size.
+                A thumbnail's width and height should not exceed 320 pixels.
+                Thumbnails can't be reused and can be only uploaded as a new file.
+
+            caption (``str``, *optional*):
+                Document caption, 0-1024 characters.
+
+            parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+                By default, texts are parsed using both Markdown and HTML styles.
+                You can combine both syntaxes together.
+
+            caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
+                List of special entities that appear in the caption, which can be specified instead of *parse_mode*.
+            
+            file_name (``str``, *optional*):
+                File name of the document sent.
+                Defaults to file's path basename.
+
+            force_document (``bool``, *optional*):
+                Pass True to force sending files as document. Useful for video files that need to be sent as
+                document messages instead of video messages.
+                Defaults to False.
+
+            disable_notification (``bool``, *optional*):
+                Sends the message silently.
+                Users will receive a notification with no sound.
+
+            reply_to_message_id (``int``, *optional*):
+                If the message is a reply, ID of the original message.
+            
+            schedule_date (:py:obj:`~datetime.datetime`, *optional*):
+                Date when the message will be automatically sent.
+
+            reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*):
+                Additional interface options. An object for an inline keyboard, custom reply keyboard,
+                instructions to remove reply keyboard or to force a reply from the user.
+
+            progress (``Callable``, *optional*):
+                Pass a callback function to view the file transmission progress.
+                The function must take *(current, total)* as positional arguments (look at Other Parameters below for a
+                detailed description) and will be called back each time a new file chunk has been successfully
+                transmitted.
+
+            progress_args (``tuple``, *optional*):
+                Extra custom arguments for the progress callback function.
+                You can pass anything you need to be available in the progress callback scope; for example, a Message
+                object or a Client instance in order to edit the message with the updated progress status.
+
+        Other Parameters:
+            current (``int``):
+                The amount of bytes transmitted so far.
+
+            total (``int``):
+                The total size of the file.
+
+            *args (``tuple``, *optional*):
+                Extra custom arguments as defined in the ``progress_args`` parameter.
+                You can either keep ``*args`` or add every single extra argument in your function signature.
+
+        Returns:
+            On success, the sent :obj:`~pyrogram.types.Message` is returned.
+            In case the upload is deliberately stopped with :meth:`~pyrogram.Client.stop_transmission`, None is returned
+            instead.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+        if quote is None:
+            quote = self.chat.type != enums.ChatType.PRIVATE
+
+        if reply_to_message_id is None and quote:
+            reply_to_message_id = self.id
+
+        return await self._client.send_document(
+            chat_id=self.chat.id,
+            document=document,
+            thumb=thumb,
+            caption=caption,
+            parse_mode=parse_mode,
+            caption_entities=caption_entities,
+            file_name=file_name,
+            force_document=force_document,
+            disable_notification=disable_notification,
+            reply_to_message_id=reply_to_message_id,
+            schedule_date=schedule_date,
+            reply_markup=reply_markup,
+            progress=progress,
+            progress_args=progress_args
+        )
+
+    async def reply_game(
+        self,
+        game_short_name: str,
+        quote: bool = None,
+        disable_notification: bool = None,
+        reply_to_message_id: int = None,
+        reply_markup: Union[
+            "types.InlineKeyboardMarkup",
+            "types.ReplyKeyboardMarkup",
+            "types.ReplyKeyboardRemove",
+            "types.ForceReply"
+        ] = None
+    ) -> "Message":
+        """Bound method *reply_game* of :obj:`~pyrogram.types.Message`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.send_game(
+                chat_id=message.chat.id,
+                game_short_name="lumberjack"
+            )
+
+        Example:
+            .. code-block:: python
+
+                await message.reply_game("lumberjack")
+
+        Parameters:
+            game_short_name (``str``):
+                Short name of the game, serves as the unique identifier for the game. Set up your games via Botfather.
+
+            quote (``bool``, *optional*):
+                If ``True``, the message will be sent as a reply to this message.
+                If *reply_to_message_id* is passed, this parameter will be ignored.
+                Defaults to ``True`` in group chats and ``False`` in private chats.
+
+            disable_notification (``bool``, *optional*):
+                Sends the message silently.
+                Users will receive a notification with no sound.
+
+            reply_to_message_id (``int``, *optional*):
+                If the message is a reply, ID of the original message.
+
+            reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*):
+                An object for an inline keyboard. If empty, one ‘Play game_title’ button will be shown automatically.
+                If not empty, the first button must launch the game.
+
+        Returns:
+            On success, the sent :obj:`~pyrogram.types.Message` is returned.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+        if quote is None:
+            quote = self.chat.type != enums.ChatType.PRIVATE
+
+        if reply_to_message_id is None and quote:
+            reply_to_message_id = self.id
+
+        return await self._client.send_game(
+            chat_id=self.chat.id,
+            game_short_name=game_short_name,
+            disable_notification=disable_notification,
+            reply_to_message_id=reply_to_message_id,
+            reply_markup=reply_markup
+        )
+
+    async def reply_inline_bot_result(
+        self,
+        query_id: int,
+        result_id: str,
+        quote: bool = None,
+        disable_notification: bool = None,
+        reply_to_message_id: int = None
+    ) -> "Message":
+        """Bound method *reply_inline_bot_result* of :obj:`~pyrogram.types.Message`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.send_inline_bot_result(
+                chat_id=message.chat.id,
+                query_id=query_id,
+                result_id=result_id
+            )
+
+        Example:
+            .. code-block:: python
+
+                await message.reply_inline_bot_result(query_id, result_id)
+
+        Parameters:
+            query_id (``int``):
+                Unique identifier for the answered query.
+
+            result_id (``str``):
+                Unique identifier for the result that was chosen.
+
+            quote (``bool``, *optional*):
+                If ``True``, the message will be sent as a reply to this message.
+                If *reply_to_message_id* is passed, this parameter will be ignored.
+                Defaults to ``True`` in group chats and ``False`` in private chats.
+
+            disable_notification (``bool``, *optional*):
+                Sends the message silently.
+                Users will receive a notification with no sound.
+
+            reply_to_message_id (``bool``, *optional*):
+                If the message is a reply, ID of the original message.
+
+        Returns:
+            On success, the sent Message is returned.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+        if quote is None:
+            quote = self.chat.type != enums.ChatType.PRIVATE
+
+        if reply_to_message_id is None and quote:
+            reply_to_message_id = self.id
+
+        return await self._client.send_inline_bot_result(
+            chat_id=self.chat.id,
+            query_id=query_id,
+            result_id=result_id,
+            disable_notification=disable_notification,
+            reply_to_message_id=reply_to_message_id
+        )
+
+    async def reply_location(
+        self,
+        latitude: float,
+        longitude: float,
+        quote: bool = None,
+        disable_notification: bool = None,
+        reply_to_message_id: int = None,
+        reply_markup: Union[
+            "types.InlineKeyboardMarkup",
+            "types.ReplyKeyboardMarkup",
+            "types.ReplyKeyboardRemove",
+            "types.ForceReply"
+        ] = None
+    ) -> "Message":
+        """Bound method *reply_location* of :obj:`~pyrogram.types.Message`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.send_location(
+                chat_id=message.chat.id,
+                latitude=latitude,
+                longitude=longitude
+            )
+
+        Example:
+            .. code-block:: python
+
+                await message.reply_location(latitude, longitude)
+
+        Parameters:
+            latitude (``float``):
+                Latitude of the location.
+
+            longitude (``float``):
+                Longitude of the location.
+
+            quote (``bool``, *optional*):
+                If ``True``, the message will be sent as a reply to this message.
+                If *reply_to_message_id* is passed, this parameter will be ignored.
+                Defaults to ``True`` in group chats and ``False`` in private chats.
+
+            disable_notification (``bool``, *optional*):
+                Sends the message silently.
+                Users will receive a notification with no sound.
+
+            reply_to_message_id (``int``, *optional*):
+                If the message is a reply, ID of the original message
+
+            reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*):
+                Additional interface options. An object for an inline keyboard, custom reply keyboard,
+                instructions to remove reply keyboard or to force a reply from the user.
+
+        Returns:
+            On success, the sent :obj:`~pyrogram.types.Message` is returned.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+        if quote is None:
+            quote = self.chat.type != enums.ChatType.PRIVATE
+
+        if reply_to_message_id is None and quote:
+            reply_to_message_id = self.id
+
+        return await self._client.send_location(
+            chat_id=self.chat.id,
+            latitude=latitude,
+            longitude=longitude,
+            disable_notification=disable_notification,
+            reply_to_message_id=reply_to_message_id,
+            reply_markup=reply_markup
+        )
+
+    async def reply_media_group(
+        self,
+        media: List[Union["types.InputMediaPhoto", "types.InputMediaVideo"]],
+        quote: bool = None,
+        disable_notification: bool = None,
+        reply_to_message_id: int = None
+    ) -> List["types.Message"]:
+        """Bound method *reply_media_group* of :obj:`~pyrogram.types.Message`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.send_media_group(
+                chat_id=message.chat.id,
+                media=list_of_media
+            )
+
+        Example:
+            .. code-block:: python
+
+                await message.reply_media_group(list_of_media)
+
+        Parameters:
+            media (``list``):
+                A list containing either :obj:`~pyrogram.types.InputMediaPhoto` or
+                :obj:`~pyrogram.types.InputMediaVideo` objects
+                describing photos and videos to be sent, must include 2–10 items.
+
+            quote (``bool``, *optional*):
+                If ``True``, the message will be sent as a reply to this message.
+                If *reply_to_message_id* is passed, this parameter will be ignored.
+                Defaults to ``True`` in group chats and ``False`` in private chats.
+
+            disable_notification (``bool``, *optional*):
+                Sends the message silently.
+                Users will receive a notification with no sound.
+
+            reply_to_message_id (``int``, *optional*):
+                If the message is a reply, ID of the original message.
+
+        Returns:
+            On success, a :obj:`~pyrogram.types.Messages` object is returned containing all the
+            single messages sent.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+        if quote is None:
+            quote = self.chat.type != enums.ChatType.PRIVATE
+
+        if reply_to_message_id is None and quote:
+            reply_to_message_id = self.id
+
+        return await self._client.send_media_group(
+            chat_id=self.chat.id,
+            media=media,
+            disable_notification=disable_notification,
+            reply_to_message_id=reply_to_message_id
+        )
+
+    async def reply_photo(
+        self,
+        photo: Union[str, BinaryIO],
+        quote: bool = None,
+        caption: str = "",
+        parse_mode: Optional["enums.ParseMode"] = None,
+        caption_entities: List["types.MessageEntity"] = None,
+        has_spoiler: bool = None,
+        ttl_seconds: int = None,
+        disable_notification: bool = None,
+        reply_to_message_id: int = None,
+        reply_markup: Union[
+            "types.InlineKeyboardMarkup",
+            "types.ReplyKeyboardMarkup",
+            "types.ReplyKeyboardRemove",
+            "types.ForceReply"
+        ] = None,
+        progress: Callable = None,
+        progress_args: tuple = ()
+    ) -> "Message":
+        """Bound method *reply_photo* of :obj:`~pyrogram.types.Message`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.send_photo(
+                chat_id=message.chat.id,
+                photo=photo
+            )
+
+        Example:
+            .. code-block:: python
+
+                await message.reply_photo(photo)
+
+        Parameters:
+            photo (``str``):
+                Photo to send.
+                Pass a file_id as string to send a photo that exists on the Telegram servers,
+                pass an HTTP URL as a string for Telegram to get a photo from the Internet, or
+                pass a file path as string to upload a new photo that exists on your local machine.
+
+            quote (``bool``, *optional*):
+                If ``True``, the message will be sent as a reply to this message.
+                If *reply_to_message_id* is passed, this parameter will be ignored.
+                Defaults to ``True`` in group chats and ``False`` in private chats.
+
+            caption (``str``, *optional*):
+                Photo caption, 0-1024 characters.
+
+            parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+                By default, texts are parsed using both Markdown and HTML styles.
+                You can combine both syntaxes together.
+
+            caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
+                List of special entities that appear in the caption, which can be specified instead of *parse_mode*.
+
+            has_spoiler (``bool``, *optional*):
+                Pass True if the photo needs to be covered with a spoiler animation.
+
+            ttl_seconds (``int``, *optional*):
+                Self-Destruct Timer.
+                If you set a timer, the photo will self-destruct in *ttl_seconds*
+                seconds after it was viewed.
+
+            disable_notification (``bool``, *optional*):
+                Sends the message silently.
+                Users will receive a notification with no sound.
+
+            reply_to_message_id (``int``, *optional*):
+                If the message is a reply, ID of the original message.
+
+            reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*):
+                Additional interface options. An object for an inline keyboard, custom reply keyboard,
+                instructions to remove reply keyboard or to force a reply from the user.
+
+            progress (``Callable``, *optional*):
+                Pass a callback function to view the file transmission progress.
+                The function must take *(current, total)* as positional arguments (look at Other Parameters below for a
+                detailed description) and will be called back each time a new file chunk has been successfully
+                transmitted.
+
+            progress_args (``tuple``, *optional*):
+                Extra custom arguments for the progress callback function.
+                You can pass anything you need to be available in the progress callback scope; for example, a Message
+                object or a Client instance in order to edit the message with the updated progress status.
+
+        Other Parameters:
+            current (``int``):
+                The amount of bytes transmitted so far.
+
+            total (``int``):
+                The total size of the file.
+
+            *args (``tuple``, *optional*):
+                Extra custom arguments as defined in the ``progress_args`` parameter.
+                You can either keep ``*args`` or add every single extra argument in your function signature.
+
+        Returns:
+            On success, the sent :obj:`~pyrogram.types.Message` is returned.
+            In case the upload is deliberately stopped with :meth:`~pyrogram.Client.stop_transmission`, None is returned
+            instead.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+        if quote is None:
+            quote = self.chat.type != enums.ChatType.PRIVATE
+
+        if reply_to_message_id is None and quote:
+            reply_to_message_id = self.id
+
+        return await self._client.send_photo(
+            chat_id=self.chat.id,
+            photo=photo,
+            caption=caption,
+            parse_mode=parse_mode,
+            caption_entities=caption_entities,
+            has_spoiler=has_spoiler,
+            ttl_seconds=ttl_seconds,
+            disable_notification=disable_notification,
+            reply_to_message_id=reply_to_message_id,
+            reply_markup=reply_markup,
+            progress=progress,
+            progress_args=progress_args
+        )
+
+    async def reply_poll(
+        self,
+        question: str,
+        options: List[str],
+        is_anonymous: bool = True,
+        type: "enums.PollType" = enums.PollType.REGULAR,
+        allows_multiple_answers: bool = None,
+        correct_option_id: int = None,
+        explanation: str = None,
+        explanation_parse_mode: "enums.ParseMode" = None,
+        explanation_entities: List["types.MessageEntity"] = None,
+        open_period: int = None,
+        close_date: datetime = None,
+        is_closed: bool = None,
+        quote: bool = None,
+        disable_notification: bool = None,
+        protect_content: bool = None,
+        reply_to_message_id: int = None,
+        schedule_date: datetime = None,
+        reply_markup: Union[
+            "types.InlineKeyboardMarkup",
+            "types.ReplyKeyboardMarkup",
+            "types.ReplyKeyboardRemove",
+            "types.ForceReply"
+        ] = None
+    ) -> "Message":
+        """Bound method *reply_poll* of :obj:`~pyrogram.types.Message`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.send_poll(
+                chat_id=message.chat.id,
+                question="This is a poll",
+                options=["A", "B", "C]
+            )
+
+        Example:
+            .. code-block:: python
+
+                await message.reply_poll("This is a poll", ["A", "B", "C"])
+
+        Parameters:
+            question (``str``):
+                Poll question, 1-255 characters.
+
+            options (List of ``str``):
+                List of answer options, 2-10 strings 1-100 characters each.
+
+            is_anonymous (``bool``, *optional*):
+                True, if the poll needs to be anonymous.
+                Defaults to True.
+
+            type (:obj`~pyrogram.enums.PollType`, *optional*):
+                Poll type, :obj:`~pyrogram.enums.PollType.QUIZ` or :obj:`~pyrogram.enums.PollType.REGULAR`.
+                Defaults to :obj:`~pyrogram.enums.PollType.REGULAR`.
+
+            allows_multiple_answers (``bool``, *optional*):
+                True, if the poll allows multiple answers, ignored for polls in quiz mode.
+                Defaults to False.
+
+            correct_option_id (``int``, *optional*):
+                0-based identifier of the correct answer option, required for polls in quiz mode.
+
+            explanation (``str``, *optional*):
+                Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style
+                poll, 0-200 characters with at most 2 line feeds after entities parsing.
+
+            explanation_parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+                By default, texts are parsed using both Markdown and HTML styles.
+                You can combine both syntaxes together.
+
+            explanation_entities (List of :obj:`~pyrogram.types.MessageEntity`):
+                List of special entities that appear in the poll explanation, which can be specified instead of
+                *parse_mode*.
+
+            open_period (``int``, *optional*):
+                Amount of time in seconds the poll will be active after creation, 5-600.
+                Can't be used together with *close_date*.
+
+            close_date (:py:obj:`~datetime.datetime`, *optional*):
+                Point in time when the poll will be automatically closed.
+                Must be at least 5 and no more than 600 seconds in the future.
+                Can't be used together with *open_period*.
+
+            is_closed (``bool``, *optional*):
+                Pass True, if the poll needs to be immediately closed.
+                This can be useful for poll preview.
+
+            quote (``bool``, *optional*):
+                If ``True``, the message will be sent as a reply to this message.
+                If *reply_to_message_id* is passed, this parameter will be ignored.
+                Defaults to ``True`` in group chats and ``False`` in private chats.
+
+            disable_notification (``bool``, *optional*):
+                Sends the message silently.
+                Users will receive a notification with no sound.
+
+            protect_content (``bool``, *optional*):
+                Protects the contents of the sent message from forwarding and saving.
+
+            reply_to_message_id (``int``, *optional*):
+                If the message is a reply, ID of the original message.
+
+            schedule_date (:py:obj:`~datetime.datetime`, *optional*):
+                Date when the message will be automatically sent.
+
+            reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*):
+                Additional interface options. An object for an inline keyboard, custom reply keyboard,
+                instructions to remove reply keyboard or to force a reply from the user.
+
+        Returns:
+            On success, the sent :obj:`~pyrogram.types.Message` is returned.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+        if quote is None:
+            quote = self.chat.type != enums.ChatType.PRIVATE
+
+        if reply_to_message_id is None and quote:
+            reply_to_message_id = self.id
+
+        return await self._client.send_poll(
+            chat_id=self.chat.id,
+            question=question,
+            options=options,
+            is_anonymous=is_anonymous,
+            type=type,
+            allows_multiple_answers=allows_multiple_answers,
+            correct_option_id=correct_option_id,
+            explanation=explanation,
+            explanation_parse_mode=explanation_parse_mode,
+            explanation_entities=explanation_entities,
+            open_period=open_period,
+            close_date=close_date,
+            is_closed=is_closed,
+            disable_notification=disable_notification,
+            protect_content=protect_content,
+            reply_to_message_id=reply_to_message_id,
+            schedule_date=schedule_date,
+            reply_markup=reply_markup
+        )
+
+    async def reply_sticker(
+        self,
+        sticker: Union[str, BinaryIO],
+        quote: bool = None,
+        disable_notification: bool = None,
+        reply_to_message_id: int = None,
+        reply_markup: Union[
+            "types.InlineKeyboardMarkup",
+            "types.ReplyKeyboardMarkup",
+            "types.ReplyKeyboardRemove",
+            "types.ForceReply"
+        ] = None,
+        progress: Callable = None,
+        progress_args: tuple = ()
+    ) -> "Message":
+        """Bound method *reply_sticker* of :obj:`~pyrogram.types.Message`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.send_sticker(
+                chat_id=message.chat.id,
+                sticker=sticker
+            )
+
+        Example:
+            .. code-block:: python
+
+                await message.reply_sticker(sticker)
+
+        Parameters:
+            sticker (``str``):
+                Sticker to send.
+                Pass a file_id as string to send a sticker that exists on the Telegram servers,
+                pass an HTTP URL as a string for Telegram to get a .webp sticker file from the Internet, or
+                pass a file path as string to upload a new sticker that exists on your local machine.
+
+            quote (``bool``, *optional*):
+                If ``True``, the message will be sent as a reply to this message.
+                If *reply_to_message_id* is passed, this parameter will be ignored.
+                Defaults to ``True`` in group chats and ``False`` in private chats.
+
+            disable_notification (``bool``, *optional*):
+                Sends the message silently.
+                Users will receive a notification with no sound.
+
+            reply_to_message_id (``int``, *optional*):
+                If the message is a reply, ID of the original message.
+
+            reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*):
+                Additional interface options. An object for an inline keyboard, custom reply keyboard,
+                instructions to remove reply keyboard or to force a reply from the user.
+
+            progress (``Callable``, *optional*):
+                Pass a callback function to view the file transmission progress.
+                The function must take *(current, total)* as positional arguments (look at Other Parameters below for a
+                detailed description) and will be called back each time a new file chunk has been successfully
+                transmitted.
+
+            progress_args (``tuple``, *optional*):
+                Extra custom arguments for the progress callback function.
+                You can pass anything you need to be available in the progress callback scope; for example, a Message
+                object or a Client instance in order to edit the message with the updated progress status.
+
+        Other Parameters:
+            current (``int``):
+                The amount of bytes transmitted so far.
+
+            total (``int``):
+                The total size of the file.
+
+            *args (``tuple``, *optional*):
+                Extra custom arguments as defined in the ``progress_args`` parameter.
+                You can either keep ``*args`` or add every single extra argument in your function signature.
+
+        Returns:
+            On success, the sent :obj:`~pyrogram.types.Message` is returned.
+            In case the upload is deliberately stopped with :meth:`~pyrogram.Client.stop_transmission`, None is returned
+            instead.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+        if quote is None:
+            quote = self.chat.type != enums.ChatType.PRIVATE
+
+        if reply_to_message_id is None and quote:
+            reply_to_message_id = self.id
+
+        return await self._client.send_sticker(
+            chat_id=self.chat.id,
+            sticker=sticker,
+            disable_notification=disable_notification,
+            reply_to_message_id=reply_to_message_id,
+            reply_markup=reply_markup,
+            progress=progress,
+            progress_args=progress_args
+        )
+
+    async def reply_venue(
+        self,
+        latitude: float,
+        longitude: float,
+        title: str,
+        address: str,
+        quote: bool = None,
+        foursquare_id: str = "",
+        foursquare_type: str = "",
+        disable_notification: bool = None,
+        reply_to_message_id: int = None,
+        reply_markup: Union[
+            "types.InlineKeyboardMarkup",
+            "types.ReplyKeyboardMarkup",
+            "types.ReplyKeyboardRemove",
+            "types.ForceReply"
+        ] = None
+    ) -> "Message":
+        """Bound method *reply_venue* of :obj:`~pyrogram.types.Message`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.send_venue(
+                chat_id=message.chat.id,
+                latitude=latitude,
+                longitude=longitude,
+                title="Venue title",
+                address="Venue address"
+            )
+
+        Example:
+            .. code-block:: python
+
+                await message.reply_venue(latitude, longitude, "Venue title", "Venue address")
+
+        Parameters:
+            latitude (``float``):
+                Latitude of the venue.
+
+            longitude (``float``):
+                Longitude of the venue.
+
+            title (``str``):
+                Name of the venue.
+
+            address (``str``):
+                Address of the venue.
+
+            quote (``bool``, *optional*):
+                If ``True``, the message will be sent as a reply to this message.
+                If *reply_to_message_id* is passed, this parameter will be ignored.
+                Defaults to ``True`` in group chats and ``False`` in private chats.
+
+            foursquare_id (``str``, *optional*):
+                Foursquare identifier of the venue.
+
+            foursquare_type (``str``, *optional*):
+                Foursquare type of the venue, if known.
+                (For example, "arts_entertainment/default", "arts_entertainment/aquarium" or "food/icecream".)
+
+            disable_notification (``bool``, *optional*):
+                Sends the message silently.
+                Users will receive a notification with no sound.
+
+            reply_to_message_id (``int``, *optional*):
+                If the message is a reply, ID of the original message
+
+            reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*):
+                Additional interface options. An object for an inline keyboard, custom reply keyboard,
+                instructions to remove reply keyboard or to force a reply from the user.
+
+        Returns:
+            On success, the sent :obj:`~pyrogram.types.Message` is returned.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+        if quote is None:
+            quote = self.chat.type != enums.ChatType.PRIVATE
+
+        if reply_to_message_id is None and quote:
+            reply_to_message_id = self.id
+
+        return await self._client.send_venue(
+            chat_id=self.chat.id,
+            latitude=latitude,
+            longitude=longitude,
+            title=title,
+            address=address,
+            foursquare_id=foursquare_id,
+            foursquare_type=foursquare_type,
+            disable_notification=disable_notification,
+            reply_to_message_id=reply_to_message_id,
+            reply_markup=reply_markup
+        )
+
+    async def reply_video(
+        self,
+        video: Union[str, BinaryIO],
+        quote: bool = None,
+        caption: str = "",
+        parse_mode: Optional["enums.ParseMode"] = None,
+        caption_entities: List["types.MessageEntity"] = None,
+        has_spoiler: bool = None,
+        ttl_seconds: int = None,
+        duration: int = 0,
+        width: int = 0,
+        height: int = 0,
+        thumb: str = None,
+        supports_streaming: bool = True,
+        disable_notification: bool = None,
+        reply_to_message_id: int = None,
+        reply_markup: Union[
+            "types.InlineKeyboardMarkup",
+            "types.ReplyKeyboardMarkup",
+            "types.ReplyKeyboardRemove",
+            "types.ForceReply"
+        ] = None,
+        progress: Callable = None,
+        progress_args: tuple = ()
+    ) -> "Message":
+        """Bound method *reply_video* of :obj:`~pyrogram.types.Message`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.send_video(
+                chat_id=message.chat.id,
+                video=video
+            )
+
+        Example:
+            .. code-block:: python
+
+                await message.reply_video(video)
+
+        Parameters:
+            video (``str``):
+                Video to send.
+                Pass a file_id as string to send a video that exists on the Telegram servers,
+                pass an HTTP URL as a string for Telegram to get a video from the Internet, or
+                pass a file path as string to upload a new video that exists on your local machine.
+
+            quote (``bool``, *optional*):
+                If ``True``, the message will be sent as a reply to this message.
+                If *reply_to_message_id* is passed, this parameter will be ignored.
+                Defaults to ``True`` in group chats and ``False`` in private chats.
+
+            caption (``str``, *optional*):
+                Video caption, 0-1024 characters.
+
+            parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+                By default, texts are parsed using both Markdown and HTML styles.
+                You can combine both syntaxes together.
+
+            caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
+                List of special entities that appear in the caption, which can be specified instead of *parse_mode*.
+
+            has_spoiler (``bool``, *optional*):
+                Pass True if the video needs to be covered with a spoiler animation.
+
+            ttl_seconds (``int``, *optional*):
+                Self-Destruct Timer.
+                If you set a timer, the video will self-destruct in *ttl_seconds*
+                seconds after it was viewed.
+
+            duration (``int``, *optional*):
+                Duration of sent video in seconds.
+
+            width (``int``, *optional*):
+                Video width.
+
+            height (``int``, *optional*):
+                Video height.
+
+            thumb (``str``, *optional*):
+                Thumbnail of the video sent.
+                The thumbnail should be in JPEG format and less than 200 KB in size.
+                A thumbnail's width and height should not exceed 320 pixels.
+                Thumbnails can't be reused and can be only uploaded as a new file.
+
+            supports_streaming (``bool``, *optional*):
+                Pass True, if the uploaded video is suitable for streaming.
+
+            disable_notification (``bool``, *optional*):
+                Sends the message silently.
+                Users will receive a notification with no sound.
+
+            reply_to_message_id (``int``, *optional*):
+                If the message is a reply, ID of the original message.
+
+            reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*):
+                Additional interface options. An object for an inline keyboard, custom reply keyboard,
+                instructions to remove reply keyboard or to force a reply from the user.
+
+            progress (``Callable``, *optional*):
+                Pass a callback function to view the file transmission progress.
+                The function must take *(current, total)* as positional arguments (look at Other Parameters below for a
+                detailed description) and will be called back each time a new file chunk has been successfully
+                transmitted.
+
+            progress_args (``tuple``, *optional*):
+                Extra custom arguments for the progress callback function.
+                You can pass anything you need to be available in the progress callback scope; for example, a Message
+                object or a Client instance in order to edit the message with the updated progress status.
+
+        Other Parameters:
+            current (``int``):
+                The amount of bytes transmitted so far.
+
+            total (``int``):
+                The total size of the file.
+
+            *args (``tuple``, *optional*):
+                Extra custom arguments as defined in the ``progress_args`` parameter.
+                You can either keep ``*args`` or add every single extra argument in your function signature.
+
+        Returns:
+            On success, the sent :obj:`~pyrogram.types.Message` is returned.
+            In case the upload is deliberately stopped with :meth:`~pyrogram.Client.stop_transmission`, None is returned
+            instead.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+        if quote is None:
+            quote = self.chat.type != enums.ChatType.PRIVATE
+
+        if reply_to_message_id is None and quote:
+            reply_to_message_id = self.id
+
+        return await self._client.send_video(
+            chat_id=self.chat.id,
+            video=video,
+            caption=caption,
+            parse_mode=parse_mode,
+            caption_entities=caption_entities,
+            has_spoiler=has_spoiler,
+            ttl_seconds=ttl_seconds,
+            duration=duration,
+            width=width,
+            height=height,
+            thumb=thumb,
+            supports_streaming=supports_streaming,
+            disable_notification=disable_notification,
+            reply_to_message_id=reply_to_message_id,
+            reply_markup=reply_markup,
+            progress=progress,
+            progress_args=progress_args
+        )
+
+    async def reply_video_note(
+        self,
+        video_note: Union[str, BinaryIO],
+        quote: bool = None,
+        duration: int = 0,
+        length: int = 1,
+        thumb: str = None,
+        disable_notification: bool = None,
+        reply_to_message_id: int = None,
+        reply_markup: Union[
+            "types.InlineKeyboardMarkup",
+            "types.ReplyKeyboardMarkup",
+            "types.ReplyKeyboardRemove",
+            "types.ForceReply"
+        ] = None,
+        progress: Callable = None,
+        progress_args: tuple = ()
+    ) -> "Message":
+        """Bound method *reply_video_note* of :obj:`~pyrogram.types.Message`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.send_video_note(
+                chat_id=message.chat.id,
+                video_note=video_note
+            )
+
+        Example:
+            .. code-block:: python
+
+                await message.reply_video_note(video_note)
+
+        Parameters:
+            video_note (``str``):
+                Video note to send.
+                Pass a file_id as string to send a video note that exists on the Telegram servers, or
+                pass a file path as string to upload a new video note that exists on your local machine.
+                Sending video notes by a URL is currently unsupported.
+
+            quote (``bool``, *optional*):
+                If ``True``, the message will be sent as a reply to this message.
+                If *reply_to_message_id* is passed, this parameter will be ignored.
+                Defaults to ``True`` in group chats and ``False`` in private chats.
+
+            duration (``int``, *optional*):
+                Duration of sent video in seconds.
+
+            length (``int``, *optional*):
+                Video width and height.
+
+            thumb (``str``, *optional*):
+                Thumbnail of the video sent.
+                The thumbnail should be in JPEG format and less than 200 KB in size.
+                A thumbnail's width and height should not exceed 320 pixels.
+                Thumbnails can't be reused and can be only uploaded as a new file.
+
+            disable_notification (``bool``, *optional*):
+                Sends the message silently.
+                Users will receive a notification with no sound.
+
+            reply_to_message_id (``int``, *optional*):
+                If the message is a reply, ID of the original message
+
+            reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*):
+                Additional interface options. An object for an inline keyboard, custom reply keyboard,
+                instructions to remove reply keyboard or to force a reply from the user.
+
+            progress (``Callable``, *optional*):
+                Pass a callback function to view the file transmission progress.
+                The function must take *(current, total)* as positional arguments (look at Other Parameters below for a
+                detailed description) and will be called back each time a new file chunk has been successfully
+                transmitted.
+
+            progress_args (``tuple``, *optional*):
+                Extra custom arguments for the progress callback function.
+                You can pass anything you need to be available in the progress callback scope; for example, a Message
+                object or a Client instance in order to edit the message with the updated progress status.
+
+        Other Parameters:
+            current (``int``):
+                The amount of bytes transmitted so far.
+
+            total (``int``):
+                The total size of the file.
+
+            *args (``tuple``, *optional*):
+                Extra custom arguments as defined in the ``progress_args`` parameter.
+                You can either keep ``*args`` or add every single extra argument in your function signature.
+
+        Returns:
+            On success, the sent :obj:`~pyrogram.types.Message` is returned.
+            In case the upload is deliberately stopped with :meth:`~pyrogram.Client.stop_transmission`, None is returned
+            instead.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+        if quote is None:
+            quote = self.chat.type != enums.ChatType.PRIVATE
+
+        if reply_to_message_id is None and quote:
+            reply_to_message_id = self.id
+
+        return await self._client.send_video_note(
+            chat_id=self.chat.id,
+            video_note=video_note,
+            duration=duration,
+            length=length,
+            thumb=thumb,
+            disable_notification=disable_notification,
+            reply_to_message_id=reply_to_message_id,
+            reply_markup=reply_markup,
+            progress=progress,
+            progress_args=progress_args
+        )
+
+    async def reply_voice(
+        self,
+        voice: Union[str, BinaryIO],
+        quote: bool = None,
+        caption: str = "",
+        parse_mode: Optional["enums.ParseMode"] = None,
+        caption_entities: List["types.MessageEntity"] = None,
+        duration: int = 0,
+        disable_notification: bool = None,
+        reply_to_message_id: int = None,
+        reply_markup: Union[
+            "types.InlineKeyboardMarkup",
+            "types.ReplyKeyboardMarkup",
+            "types.ReplyKeyboardRemove",
+            "types.ForceReply"
+        ] = None,
+        progress: Callable = None,
+        progress_args: tuple = ()
+    ) -> "Message":
+        """Bound method *reply_voice* of :obj:`~pyrogram.types.Message`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.send_voice(
+                chat_id=message.chat.id,
+                voice=voice
+            )
+
+        Example:
+            .. code-block:: python
+
+                await message.reply_voice(voice)
+
+        Parameters:
+            voice (``str``):
+                Audio file to send.
+                Pass a file_id as string to send an audio that exists on the Telegram servers,
+                pass an HTTP URL as a string for Telegram to get an audio from the Internet, or
+                pass a file path as string to upload a new audio that exists on your local machine.
+
+            quote (``bool``, *optional*):
+                If ``True``, the message will be sent as a reply to this message.
+                If *reply_to_message_id* is passed, this parameter will be ignored.
+                Defaults to ``True`` in group chats and ``False`` in private chats.
+
+            caption (``str``, *optional*):
+                Voice message caption, 0-1024 characters.
+
+            parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+                By default, texts are parsed using both Markdown and HTML styles.
+                You can combine both syntaxes together.
+
+            caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
+                List of special entities that appear in the caption, which can be specified instead of *parse_mode*.
+
+            duration (``int``, *optional*):
+                Duration of the voice message in seconds.
+
+            disable_notification (``bool``, *optional*):
+                Sends the message silently.
+                Users will receive a notification with no sound.
+
+            reply_to_message_id (``int``, *optional*):
+                If the message is a reply, ID of the original message
+
+            reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*):
+                Additional interface options. An object for an inline keyboard, custom reply keyboard,
+                instructions to remove reply keyboard or to force a reply from the user.
+
+            progress (``Callable``, *optional*):
+                Pass a callback function to view the file transmission progress.
+                The function must take *(current, total)* as positional arguments (look at Other Parameters below for a
+                detailed description) and will be called back each time a new file chunk has been successfully
+                transmitted.
+
+            progress_args (``tuple``, *optional*):
+                Extra custom arguments for the progress callback function.
+                You can pass anything you need to be available in the progress callback scope; for example, a Message
+                object or a Client instance in order to edit the message with the updated progress status.
+
+        Other Parameters:
+            current (``int``):
+                The amount of bytes transmitted so far.
+
+            total (``int``):
+                The total size of the file.
+
+            *args (``tuple``, *optional*):
+                Extra custom arguments as defined in the ``progress_args`` parameter.
+                You can either keep ``*args`` or add every single extra argument in your function signature.
+
+        Returns:
+            On success, the sent :obj:`~pyrogram.types.Message` is returned.
+            In case the upload is deliberately stopped with :meth:`~pyrogram.Client.stop_transmission`, None is returned
+            instead.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+        if quote is None:
+            quote = self.chat.type != enums.ChatType.PRIVATE
+
+        if reply_to_message_id is None and quote:
+            reply_to_message_id = self.id
+
+        return await self._client.send_voice(
+            chat_id=self.chat.id,
+            voice=voice,
+            caption=caption,
+            parse_mode=parse_mode,
+            caption_entities=caption_entities,
+            duration=duration,
+            disable_notification=disable_notification,
+            reply_to_message_id=reply_to_message_id,
+            reply_markup=reply_markup,
+            progress=progress,
+            progress_args=progress_args
+        )
+
+    async def edit_text(
+        self,
+        text: str,
+        parse_mode: Optional["enums.ParseMode"] = None,
+        entities: List["types.MessageEntity"] = None,
+        disable_web_page_preview: bool = None,
+        reply_markup: "types.InlineKeyboardMarkup" = None
+    ) -> "Message":
+        """Bound method *edit_text* of :obj:`~pyrogram.types.Message`.
+
+        An alias exists as *edit*.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.edit_message_text(
+                chat_id=message.chat.id,
+                message_id=message.id,
+                text="hello"
+            )
+
+        Example:
+            .. code-block:: python
+
+                await message.edit_text("hello")
+
+        Parameters:
+            text (``str``):
+                New text of the message.
+
+            parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+                By default, texts are parsed using both Markdown and HTML styles.
+                You can combine both syntaxes together.
+
+            entities (List of :obj:`~pyrogram.types.MessageEntity`):
+                List of special entities that appear in message text, which can be specified instead of *parse_mode*.
+
+            disable_web_page_preview (``bool``, *optional*):
+                Disables link previews for links in this message.
+
+            reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*):
+                An InlineKeyboardMarkup object.
+
+        Returns:
+            On success, the edited :obj:`~pyrogram.types.Message` is returned.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+        return await self._client.edit_message_text(
+            chat_id=self.chat.id,
+            message_id=self.id,
+            text=text,
+            parse_mode=parse_mode,
+            entities=entities,
+            disable_web_page_preview=disable_web_page_preview,
+            reply_markup=reply_markup
+        )
+
+    edit = edit_text
+
+    async def edit_caption(
+        self,
+        caption: str,
+        parse_mode: Optional["enums.ParseMode"] = None,
+        caption_entities: List["types.MessageEntity"] = None,
+        reply_markup: "types.InlineKeyboardMarkup" = None
+    ) -> "Message":
+        """Bound method *edit_caption* of :obj:`~pyrogram.types.Message`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.edit_message_caption(
+                chat_id=message.chat.id,
+                message_id=message.id,
+                caption="hello"
+            )
+
+        Example:
+            .. code-block:: python
+
+                await message.edit_caption("hello")
+
+        Parameters:
+            caption (``str``):
+                New caption of the message.
+
+            parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+                By default, texts are parsed using both Markdown and HTML styles.
+                You can combine both syntaxes together.
+
+            caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
+                List of special entities that appear in the caption, which can be specified instead of *parse_mode*.
+
+            reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*):
+                An InlineKeyboardMarkup object.
+
+        Returns:
+            On success, the edited :obj:`~pyrogram.types.Message` is returned.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+        return await self._client.edit_message_caption(
+            chat_id=self.chat.id,
+            message_id=self.id,
+            caption=caption,
+            parse_mode=parse_mode,
+            caption_entities=caption_entities,
+            reply_markup=reply_markup
+        )
+
+    async def edit_media(
+        self,
+        media: "types.InputMedia",
+        reply_markup: "types.InlineKeyboardMarkup" = None
+    ) -> "Message":
+        """Bound method *edit_media* of :obj:`~pyrogram.types.Message`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.edit_message_media(
+                chat_id=message.chat.id,
+                message_id=message.id,
+                media=media
+            )
+
+        Example:
+            .. code-block:: python
+
+                await message.edit_media(media)
+
+        Parameters:
+            media (:obj:`~pyrogram.types.InputMedia`):
+                One of the InputMedia objects describing an animation, audio, document, photo or video.
+
+            reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*):
+                An InlineKeyboardMarkup object.
+
+        Returns:
+            On success, the edited :obj:`~pyrogram.types.Message` is returned.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+        return await self._client.edit_message_media(
+            chat_id=self.chat.id,
+            message_id=self.id,
+            media=media,
+            reply_markup=reply_markup
+        )
+
+    async def edit_reply_markup(self, reply_markup: "types.InlineKeyboardMarkup" = None) -> "Message":
+        """Bound method *edit_reply_markup* of :obj:`~pyrogram.types.Message`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.edit_message_reply_markup(
+                chat_id=message.chat.id,
+                message_id=message.id,
+                reply_markup=inline_reply_markup
+            )
+
+        Example:
+            .. code-block:: python
+
+                await message.edit_reply_markup(inline_reply_markup)
+
+        Parameters:
+            reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`):
+                An InlineKeyboardMarkup object.
+
+        Returns:
+            On success, if edited message is sent by the bot, the edited
+            :obj:`~pyrogram.types.Message` is returned, otherwise True is returned.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+        return await self._client.edit_message_reply_markup(
+            chat_id=self.chat.id,
+            message_id=self.id,
+            reply_markup=reply_markup
+        )
+
+    async def forward(
+        self,
+        chat_id: Union[int, str],
+        disable_notification: bool = None,
+        schedule_date: datetime = None
+    ) -> Union["types.Message", List["types.Message"]]:
+        """Bound method *forward* of :obj:`~pyrogram.types.Message`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.forward_messages(
+                chat_id=chat_id,
+                from_chat_id=message.chat.id,
+                message_ids=message.id
+            )
+
+        Example:
+            .. code-block:: python
+
+                await message.forward(chat_id)
+
+        Parameters:
+            chat_id (``int`` | ``str``):
+                Unique identifier (int) or username (str) of the target chat.
+                For your personal cloud (Saved Messages) you can simply use "me" or "self".
+                For a contact that exists in your Telegram address book you can use his phone number (str).
+
+            disable_notification (``bool``, *optional*):
+                Sends the message silently.
+                Users will receive a notification with no sound.
+
+            schedule_date (:py:obj:`~datetime.datetime`, *optional*):
+                Date when the message will be automatically sent.
+
+        Returns:
+            On success, the forwarded Message is returned.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+        return await self._client.forward_messages(
+            chat_id=chat_id,
+            from_chat_id=self.chat.id,
+            message_ids=self.id,
+            disable_notification=disable_notification,
+            schedule_date=schedule_date
+        )
+
+    async def copy(
+        self,
+        chat_id: Union[int, str],
+        caption: str = None,
+        parse_mode: Optional["enums.ParseMode"] = None,
+        caption_entities: List["types.MessageEntity"] = None,
+        disable_notification: bool = None,
+        reply_to_message_id: int = None,
+        schedule_date: datetime = None,
+        protect_content: bool = None,
+        reply_markup: Union[
+            "types.InlineKeyboardMarkup",
+            "types.ReplyKeyboardMarkup",
+            "types.ReplyKeyboardRemove",
+            "types.ForceReply"
+        ] = object
+    ) -> Union["types.Message", List["types.Message"]]:
+        """Bound method *copy* of :obj:`~pyrogram.types.Message`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.copy_message(
+                chat_id=chat_id,
+                from_chat_id=message.chat.id,
+                message_id=message.id
+            )
+
+        Example:
+            .. code-block:: python
+
+                await message.copy(chat_id)
+
+        Parameters:
+            chat_id (``int`` | ``str``):
+                Unique identifier (int) or username (str) of the target chat.
+                For your personal cloud (Saved Messages) you can simply use "me" or "self".
+                For a contact that exists in your Telegram address book you can use his phone number (str).
+
+            caption (``string``, *optional*):
+                New caption for media, 0-1024 characters after entities parsing.
+                If not specified, the original caption is kept.
+                Pass "" (empty string) to remove the caption.
+
+            parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+                By default, texts are parsed using both Markdown and HTML styles.
+                You can combine both syntaxes together.
+
+            caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
+                List of special entities that appear in the new caption, which can be specified instead of *parse_mode*.
+
+            disable_notification (``bool``, *optional*):
+                Sends the message silently.
+                Users will receive a notification with no sound.
+
+            reply_to_message_id (``int``, *optional*):
+                If the message is a reply, ID of the original message.
+
+            schedule_date (:py:obj:`~datetime.datetime`, *optional*):
+                Date when the message will be automatically sent.
+
+            protect_content (``bool``, *optional*):
+                Protects the contents of the sent message from forwarding and saving.
+
+            reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*):
+                Additional interface options. An object for an inline keyboard, custom reply keyboard,
+                instructions to remove reply keyboard or to force a reply from the user.
+                If not specified, the original reply markup is kept.
+                Pass None to remove the reply markup.
+
+        Returns:
+            :obj:`~pyrogram.types.Message`: On success, the copied message is returned.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+        if self.service:
+            log.warning("Service messages cannot be copied. chat_id: %s, message_id: %s",
+                        self.chat.id, self.id)
+        elif self.game and not await self._client.storage.is_bot():
+            log.warning("Users cannot send messages with Game media type. chat_id: %s, message_id: %s",
+                        self.chat.id, self.id)
+        elif self.empty:
+            log.warning("Empty messages cannot be copied.")
+        elif self.text:
+            return await self._client.send_message(
+                chat_id,
+                text=self.text,
+                entities=self.entities,
+                parse_mode=enums.ParseMode.DISABLED,
+                disable_web_page_preview=not self.web_page,
+                disable_notification=disable_notification,
+                reply_to_message_id=reply_to_message_id,
+                schedule_date=schedule_date,
+                protect_content=protect_content,
+                reply_markup=self.reply_markup if reply_markup is object else reply_markup
+            )
+        elif self.media:
+            send_media = partial(
+                self._client.send_cached_media,
+                chat_id=chat_id,
+                disable_notification=disable_notification,
+                reply_to_message_id=reply_to_message_id,
+                schedule_date=schedule_date,
+                protect_content=protect_content,
+                reply_markup=self.reply_markup if reply_markup is object else reply_markup
+            )
+
+            if self.photo:
+                file_id = self.photo.file_id
+            elif self.audio:
+                file_id = self.audio.file_id
+            elif self.document:
+                file_id = self.document.file_id
+            elif self.video:
+                file_id = self.video.file_id
+            elif self.animation:
+                file_id = self.animation.file_id
+            elif self.voice:
+                file_id = self.voice.file_id
+            elif self.sticker:
+                file_id = self.sticker.file_id
+            elif self.video_note:
+                file_id = self.video_note.file_id
+            elif self.contact:
+                return await self._client.send_contact(
+                    chat_id,
+                    phone_number=self.contact.phone_number,
+                    first_name=self.contact.first_name,
+                    last_name=self.contact.last_name,
+                    vcard=self.contact.vcard,
+                    disable_notification=disable_notification,
+                    schedule_date=schedule_date
+                )
+            elif self.location:
+                return await self._client.send_location(
+                    chat_id,
+                    latitude=self.location.latitude,
+                    longitude=self.location.longitude,
+                    disable_notification=disable_notification,
+                    schedule_date=schedule_date
+                )
+            elif self.venue:
+                return await self._client.send_venue(
+                    chat_id,
+                    latitude=self.venue.location.latitude,
+                    longitude=self.venue.location.longitude,
+                    title=self.venue.title,
+                    address=self.venue.address,
+                    foursquare_id=self.venue.foursquare_id,
+                    foursquare_type=self.venue.foursquare_type,
+                    disable_notification=disable_notification,
+                    schedule_date=schedule_date
+                )
+            elif self.poll:
+                return await self._client.send_poll(
+                    chat_id,
+                    question=self.poll.question,
+                    options=[opt.text for opt in self.poll.options],
+                    disable_notification=disable_notification,
+                    schedule_date=schedule_date
+                )
+            elif self.game:
+                return await self._client.send_game(
+                    chat_id,
+                    game_short_name=self.game.short_name,
+                    disable_notification=disable_notification
+                )
+            else:
+                raise ValueError("Unknown media type")
+
+            if self.sticker or self.video_note:  # Sticker and VideoNote should have no caption
+                return await send_media(file_id=file_id)
+            else:
+                if caption is None:
+                    caption = self.caption or ""
+                    caption_entities = self.caption_entities
+
+                return await send_media(
+                    file_id=file_id,
+                    caption=caption,
+                    parse_mode=parse_mode,
+                    caption_entities=caption_entities
+                )
+        else:
+            raise ValueError("Can't copy this message")
+
+    async def delete(self, revoke: bool = True):
+        """Bound method *delete* of :obj:`~pyrogram.types.Message`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.delete_messages(
+                chat_id=chat_id,
+                message_ids=message.id
+            )
+
+        Example:
+            .. code-block:: python
+
+                await message.delete()
+
+        Parameters:
+            revoke (``bool``, *optional*):
+                Deletes messages on both parts.
+                This is only for private cloud chats and normal groups, messages on
+                channels and supergroups are always revoked (i.e.: deleted for everyone).
+                Defaults to True.
+
+        Returns:
+            True on success, False otherwise.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+        return await self._client.delete_messages(
+            chat_id=self.chat.id,
+            message_ids=self.id,
+            revoke=revoke
+        )
+
+    async def click(self, x: Union[int, str] = 0, y: int = None, quote: bool = None, timeout: int = 10):
+        """Bound method *click* of :obj:`~pyrogram.types.Message`.
+
+        Use as a shortcut for clicking a button attached to the message instead of:
+
+        - Clicking inline buttons:
+
+        .. code-block:: python
+
+            await client.request_callback_answer(
+                chat_id=message.chat.id,
+                message_id=message.id,
+                callback_data=message.reply_markup[i][j].callback_data
+            )
+
+        - Clicking normal buttons:
+
+        .. code-block:: python
+
+            await client.send_message(
+                chat_id=message.chat.id,
+                text=message.reply_markup[i][j].text
+            )
+
+        Example:
+            This method can be used in three different ways:
+
+            1.  Pass one integer argument only (e.g.: ``.click(2)``, to click a button at index 2).
+                Buttons are counted left to right, starting from the top.
+
+            2.  Pass two integer arguments (e.g.: ``.click(1, 0)``, to click a button at position (1, 0)).
+                The origin (0, 0) is top-left.
+
+            3.  Pass one string argument only (e.g.: ``.click("Settings")``, to click a button by using its label).
+                Only the first matching button will be pressed.
+
+        Parameters:
+            x (``int`` | ``str``):
+                Used as integer index, integer abscissa (in pair with y) or as string label.
+                Defaults to 0 (first button).
+
+            y (``int``, *optional*):
+                Used as ordinate only (in pair with x).
+
+            quote (``bool``, *optional*):
+                Useful for normal buttons only, where pressing it will result in a new message sent.
+                If ``True``, the message will be sent as a reply to this message.
+                Defaults to ``True`` in group chats and ``False`` in private chats.
+
+            timeout (``int``, *optional*):
+                Timeout in seconds.
+
+        Returns:
+            -   The result of :meth:`~pyrogram.Client.request_callback_answer` in case of inline callback button clicks.
+            -   The result of :meth:`~Message.reply()` in case of normal button clicks.
+            -   A string in case the inline button is a URL, a *switch_inline_query* or a
+                *switch_inline_query_current_chat* button.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+            ValueError: In case the provided index or position is out of range or the button label was not found.
+            TimeoutError: In case, after clicking an inline button, the bot fails to answer within the timeout.
+        """
+
+        if isinstance(self.reply_markup, types.ReplyKeyboardMarkup):
+            keyboard = self.reply_markup.keyboard
+            is_inline = False
+        elif isinstance(self.reply_markup, types.InlineKeyboardMarkup):
+            keyboard = self.reply_markup.inline_keyboard
+            is_inline = True
+        else:
+            raise ValueError("The message doesn't contain any keyboard")
+
+        if isinstance(x, int) and y is None:
+            try:
+                button = [
+                    button
+                    for row in keyboard
+                    for button in row
+                ][x]
+            except IndexError:
+                raise ValueError(f"The button at index {x} doesn't exist")
+        elif isinstance(x, int) and isinstance(y, int):
+            try:
+                button = keyboard[y][x]
+            except IndexError:
+                raise ValueError(f"The button at position ({x}, {y}) doesn't exist")
+        elif isinstance(x, str) and y is None:
+            label = x.encode("utf-16", "surrogatepass").decode("utf-16")
+
+            try:
+                button = [
+                    button
+                    for row in keyboard
+                    for button in row
+                    if label == button.text
+                ][0]
+            except IndexError:
+                raise ValueError(f"The button with label '{x}' doesn't exists")
+        else:
+            raise ValueError("Invalid arguments")
+
+        if is_inline:
+            if button.callback_data:
+                return await self._client.request_callback_answer(
+                    chat_id=self.chat.id,
+                    message_id=self.id,
+                    callback_data=button.callback_data,
+                    timeout=timeout
+                )
+            elif button.url:
+                return button.url
+            elif button.switch_inline_query:
+                return button.switch_inline_query
+            elif button.switch_inline_query_current_chat:
+                return button.switch_inline_query_current_chat
+            else:
+                raise ValueError("This button is not supported yet")
+        else:
+            await self.reply(button, quote=quote)
+
+    async def react(self, emoji: str = "", big: bool = False) -> bool:
+        """Bound method *react* of :obj:`~pyrogram.types.Message`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.send_reaction(
+                chat_id=chat_id,
+                message_id=message.id,
+                emoji="🔥"
+            )
+
+        Example:
+            .. code-block:: python
+
+                await message.react(emoji="🔥")
+
+        Parameters:
+            emoji (``str``, *optional*):
+                Reaction emoji.
+                Pass "" as emoji (default) to retract the reaction.
+             
+            big (``bool``, *optional*):
+                Pass True to show a bigger and longer reaction.
+                Defaults to False.
+
+        Returns:
+            ``bool``: On success, True is returned.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+
+        return await self._client.send_reaction(
+            chat_id=self.chat.id,
+            message_id=self.id,
+            emoji=emoji,
+            big=big
+        )
+
+    async def retract_vote(
+        self,
+    ) -> "types.Poll":
+        """Bound method *retract_vote* of :obj:`~pyrogram.types.Message`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            client.retract_vote(
+                chat_id=message.chat.id,
+                message_id=message_id,
+            )
+
+        Example:
+            .. code-block:: python
+
+                message.retract_vote()
+
+        Returns:
+            :obj:`~pyrogram.types.Poll`: On success, the poll with the retracted vote is returned.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+
+        return await self._client.retract_vote(
+            chat_id=self.chat.id,
+            message_id=self.id
+        )
+
+    async def download(
+        self,
+        file_name: str = "",
+        in_memory: bool = False,
+        block: bool = True,
+        progress: Callable = None,
+        progress_args: tuple = ()
+    ) -> str:
+        """Bound method *download* of :obj:`~pyrogram.types.Message`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.download_media(message)
+
+        Example:
+            .. code-block:: python
+
+                await message.download()
+
+        Parameters:
+            file_name (``str``, *optional*):
+                A custom *file_name* to be used instead of the one provided by Telegram.
+                By default, all files are downloaded in the *downloads* folder in your working directory.
+                You can also specify a path for downloading files in a custom location: paths that end with "/"
+                are considered directories. All non-existent folders will be created automatically.
+
+            in_memory (``bool``, *optional*):
+                Pass True to download the media in-memory.
+                A binary file-like object with its attribute ".name" set will be returned.
+                Defaults to False.
+
+            block (``bool``, *optional*):
+                Blocks the code execution until the file has been downloaded.
+                Defaults to True.
+
+            progress (``Callable``, *optional*):
+                Pass a callback function to view the file transmission progress.
+                The function must take *(current, total)* as positional arguments (look at Other Parameters below for a
+                detailed description) and will be called back each time a new file chunk has been successfully
+                transmitted.
+
+            progress_args (``tuple``, *optional*):
+                Extra custom arguments for the progress callback function.
+                You can pass anything you need to be available in the progress callback scope; for example, a Message
+                object or a Client instance in order to edit the message with the updated progress status.
+
+        Other Parameters:
+            current (``int``):
+                The amount of bytes transmitted so far.
+
+            total (``int``):
+                The total size of the file.
+
+            *args (``tuple``, *optional*):
+                Extra custom arguments as defined in the ``progress_args`` parameter.
+                You can either keep ``*args`` or add every single extra argument in your function signature.
+
+        Returns:
+            On success, the absolute path of the downloaded file as string is returned, None otherwise.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+            ``ValueError``: If the message doesn't contain any downloadable media
+        """
+        return await self._client.download_media(
+            message=self,
+            file_name=file_name,
+            in_memory=in_memory,
+            block=block,
+            progress=progress,
+            progress_args=progress_args,
+        )
+
+    async def vote(
+        self,
+        option: int,
+    ) -> "types.Poll":
+        """Bound method *vote* of :obj:`~pyrogram.types.Message`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            client.vote_poll(
+                chat_id=message.chat.id,
+                message_id=message.id,
+                option=1
+            )
+
+        Example:
+            .. code-block:: python
+
+                message.vote(6)
+
+        Parameters:
+            option (``int``):
+                Index of the poll option you want to vote for (0 to 9).
+
+        Returns:
+            :obj:`~pyrogram.types.Poll`: On success, the poll with the chosen option is returned.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+
+        return await self._client.vote_poll(
+            chat_id=self.chat.id,
+            message_id=self.id,
+            options=option
+        )
+
+    async def pin(self, disable_notification: bool = False, both_sides: bool = False) -> "types.Message":
+        """Bound method *pin* of :obj:`~pyrogram.types.Message`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.pin_chat_message(
+                chat_id=message.chat.id,
+                message_id=message_id
+            )
+
+        Example:
+            .. code-block:: python
+
+                await message.pin()
+
+        Parameters:
+            disable_notification (``bool``):
+                Pass True, if it is not necessary to send a notification to all chat members about the new pinned
+                message. Notifications are always disabled in channels.
+
+            both_sides (``bool``, *optional*):
+                Pass True to pin the message for both sides (you and recipient).
+                Applicable to private chats only. Defaults to False.
+
+        Returns:
+            :obj:`~pyrogram.types.Message`: On success, the service message is returned.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+        return await self._client.pin_chat_message(
+            chat_id=self.chat.id,
+            message_id=self.id,
+            disable_notification=disable_notification,
+            both_sides=both_sides
+        )
+
+    async def unpin(self) -> bool:
+        """Bound method *unpin* of :obj:`~pyrogram.types.Message`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.unpin_chat_message(
+                chat_id=message.chat.id,
+                message_id=message_id
+            )
+
+        Example:
+            .. code-block:: python
+
+                await message.unpin()
+
+        Returns:
+            True on success.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+        return await self._client.unpin_chat_message(
+            chat_id=self.chat.id,
+            message_id=self.id
+        )
diff --git a/pyrogram/types/messages_and_media/message_entity.py b/pyrogram/types/messages_and_media/message_entity.py
new file mode 100644
index 00000000000..d2bf654dcdd
--- /dev/null
+++ b/pyrogram/types/messages_and_media/message_entity.py
@@ -0,0 +1,124 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import Optional
+
+import pyrogram
+from pyrogram import raw, enums
+from pyrogram import types
+from ..object import Object
+
+
+class MessageEntity(Object):
+    """One special entity in a text message.
+    
+    For example, hashtags, usernames, URLs, etc.
+
+    Parameters:
+        type (:obj:`~pyrogram.enums.MessageEntityType`):
+            Type of the entity.
+
+        offset (``int``):
+            Offset in UTF-16 code units to the start of the entity.
+
+        length (``int``):
+            Length of the entity in UTF-16 code units.
+
+        url (``str``, *optional*):
+            For :obj:`~pyrogram.enums.MessageEntityType.TEXT_LINK` only, url that will be opened after user taps on the text.
+
+        user (:obj:`~pyrogram.types.User`, *optional*):
+            For :obj:`~pyrogram.enums.MessageEntityType.TEXT_MENTION` only, the mentioned user.
+
+        language (``str``, *optional*):
+            For "pre" only, the programming language of the entity text.
+
+        custom_emoji_id (``int``, *optional*):
+            For :obj:`~pyrogram.enums.MessageEntityType.CUSTOM_EMOJI` only, unique identifier of the custom emoji.
+            Use :meth:`~pyrogram.Client.get_custom_emoji_stickers` to get full information about the sticker.
+    """
+
+    def __init__(
+        self,
+        *,
+        client: "pyrogram.Client" = None,
+        type: "enums.MessageEntityType",
+        offset: int,
+        length: int,
+        url: str = None,
+        user: "types.User" = None,
+        language: str = None,
+        custom_emoji_id: int = None
+    ):
+        super().__init__(client)
+
+        self.type = type
+        self.offset = offset
+        self.length = length
+        self.url = url
+        self.user = user
+        self.language = language
+        self.custom_emoji_id = custom_emoji_id
+
+    @staticmethod
+    def _parse(client, entity: "raw.base.MessageEntity", users: dict) -> Optional["MessageEntity"]:
+        # Special case for InputMessageEntityMentionName -> MessageEntityType.TEXT_MENTION
+        # This happens in case of UpdateShortSentMessage inside send_message() where entities are parsed from the input
+        if isinstance(entity, raw.types.InputMessageEntityMentionName):
+            entity_type = enums.MessageEntityType.TEXT_MENTION
+            user_id = entity.user_id.user_id
+        else:
+            entity_type = enums.MessageEntityType(entity.__class__)
+            user_id = getattr(entity, "user_id", None)
+
+        return MessageEntity(
+            type=entity_type,
+            offset=entity.offset,
+            length=entity.length,
+            url=getattr(entity, "url", None),
+            user=types.User._parse(client, users.get(user_id, None)),
+            language=getattr(entity, "language", None),
+            custom_emoji_id=getattr(entity, "document_id", None),
+            client=client
+        )
+
+    async def write(self):
+        args = self.__dict__.copy()
+
+        for arg in ("_client", "type", "user"):
+            args.pop(arg)
+
+        if self.user:
+            args["user_id"] = await self._client.resolve_peer(self.user.id)
+
+        if not self.url:
+            args.pop("url")
+
+        if self.language is None:
+            args.pop("language")
+
+        args.pop("custom_emoji_id")
+        if self.custom_emoji_id is not None:
+            args["document_id"] = self.custom_emoji_id
+
+        entity = self.type.value
+
+        if entity is raw.types.MessageEntityMentionName:
+            entity = raw.types.InputMessageEntityMentionName
+
+        return entity(**args)
diff --git a/pyrogram/types/messages_and_media/message_reactions.py b/pyrogram/types/messages_and_media/message_reactions.py
new file mode 100644
index 00000000000..8f057cf559b
--- /dev/null
+++ b/pyrogram/types/messages_and_media/message_reactions.py
@@ -0,0 +1,56 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import Optional, List
+
+import pyrogram
+from pyrogram import raw, types
+from ..object import Object
+
+
+class MessageReactions(Object):
+    """Contains information about a message reactions.
+
+    Parameters:
+        reactions (List of :obj:`~pyrogram.types.Reaction`):
+            Reactions list.
+    """
+
+    def __init__(
+        self,
+        *,
+        client: "pyrogram.Client" = None,
+        reactions: Optional[List["types.Reaction"]] = None,
+    ):
+        super().__init__(client)
+
+        self.reactions = reactions
+
+    @staticmethod
+    def _parse(
+        client: "pyrogram.Client",
+        message_reactions: Optional["raw.base.MessageReactions"] = None
+    ) -> Optional["MessageReactions"]:
+        if not message_reactions:
+            return None
+
+        return MessageReactions(
+            client=client,
+            reactions=[types.Reaction._parse_count(client, reaction)
+                       for reaction in message_reactions.results]
+        )
diff --git a/pyrogram/types/messages_and_media/photo.py b/pyrogram/types/messages_and_media/photo.py
new file mode 100644
index 00000000000..d6ad92263df
--- /dev/null
+++ b/pyrogram/types/messages_and_media/photo.py
@@ -0,0 +1,130 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 datetime import datetime
+from typing import List
+
+import pyrogram
+from pyrogram import raw, utils
+from pyrogram import types
+from pyrogram.file_id import FileId, FileType, FileUniqueId, FileUniqueType, ThumbnailSource
+from ..object import Object
+
+
+class Photo(Object):
+    """A Photo.
+
+    Parameters:
+        file_id (``str``):
+            Identifier for this file, which can be used to download or reuse the file.
+
+        file_unique_id (``str``):
+            Unique identifier for this file, which is supposed to be the same over time and for different accounts.
+            Can't be used to download or reuse the file.
+
+        width (``int``):
+            Photo width.
+
+        height (``int``):
+            Photo height.
+
+        file_size (``int``):
+            File size.
+
+        date (:py:obj:`~datetime.datetime`):
+            Date the photo was sent.
+
+        ttl_seconds (``int``, *optional*):
+            Time-to-live seconds, for secret photos.
+
+        thumbs (List of :obj:`~pyrogram.types.Thumbnail`, *optional*):
+            Available thumbnails of this photo.
+    """
+
+    def __init__(
+        self,
+        *,
+        client: "pyrogram.Client" = None,
+        file_id: str,
+        file_unique_id: str,
+        width: int,
+        height: int,
+        file_size: int,
+        date: datetime,
+        ttl_seconds: int = None,
+        thumbs: List["types.Thumbnail"] = None
+    ):
+        super().__init__(client)
+
+        self.file_id = file_id
+        self.file_unique_id = file_unique_id
+        self.width = width
+        self.height = height
+        self.file_size = file_size
+        self.date = date
+        self.ttl_seconds = ttl_seconds
+        self.thumbs = thumbs
+
+    @staticmethod
+    def _parse(client, photo: "raw.types.Photo", ttl_seconds: int = None) -> "Photo":
+        if isinstance(photo, raw.types.Photo):
+            photos: List[raw.types.PhotoSize] = []
+
+            for p in photo.sizes:
+                if isinstance(p, raw.types.PhotoSize):
+                    photos.append(p)
+
+                if isinstance(p, raw.types.PhotoSizeProgressive):
+                    photos.append(
+                        raw.types.PhotoSize(
+                            type=p.type,
+                            w=p.w,
+                            h=p.h,
+                            size=max(p.sizes)
+                        )
+                    )
+
+            photos.sort(key=lambda p: p.size)
+
+            main = photos[-1]
+
+            return Photo(
+                file_id=FileId(
+                    file_type=FileType.PHOTO,
+                    dc_id=photo.dc_id,
+                    media_id=photo.id,
+                    access_hash=photo.access_hash,
+                    file_reference=photo.file_reference,
+                    thumbnail_source=ThumbnailSource.THUMBNAIL,
+                    thumbnail_file_type=FileType.PHOTO,
+                    thumbnail_size=main.type,
+                    volume_id=0,
+                    local_id=0
+                ).encode(),
+                file_unique_id=FileUniqueId(
+                    file_unique_type=FileUniqueType.DOCUMENT,
+                    media_id=photo.id
+                ).encode(),
+                width=main.w,
+                height=main.h,
+                file_size=main.size,
+                date=utils.timestamp_to_datetime(photo.date),
+                ttl_seconds=ttl_seconds,
+                thumbs=types.Thumbnail._parse(client, photo),
+                client=client
+            )
diff --git a/pyrogram/types/messages_and_media/poll.py b/pyrogram/types/messages_and_media/poll.py
new file mode 100644
index 00000000000..cdb97ea1000
--- /dev/null
+++ b/pyrogram/types/messages_and_media/poll.py
@@ -0,0 +1,203 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 datetime import datetime
+from typing import List, Union, Optional
+
+import pyrogram
+from pyrogram import raw, enums, utils
+from pyrogram import types
+from ..object import Object
+from ..update import Update
+
+
+class Poll(Object, Update):
+    """A Poll.
+
+    Parameters:
+        id (``str``):
+            Unique poll identifier.
+
+        question (``str``):
+            Poll question, 1-255 characters.
+
+        options (List of :obj:`~pyrogram.types.PollOption`):
+            List of poll options.
+
+        total_voter_count (``int``):
+            Total number of users that voted in the poll.
+
+        is_closed (``bool``):
+            True, if the poll is closed.
+
+        is_anonymous (``bool``, *optional*):
+            True, if the poll is anonymous
+
+        type (:obj:`~pyrogram.enums.PollType`, *optional*):
+            Poll type.
+
+        allows_multiple_answers (``bool``, *optional*):
+            True, if the poll allows multiple answers.
+
+        chosen_option_id (``int``, *optional*):
+            0-based index of the chosen option), None in case of no vote yet.
+
+        correct_option_id (``int``, *optional*):
+            0-based identifier of the correct answer option.
+            Available only for polls in the quiz mode, which are closed, or was sent (not forwarded) by the bot or to
+            the private chat with the bot.
+
+        explanation (``str``, *optional*):
+            Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style poll,
+            0-200 characters.
+
+        explanation_entities (List of :obj:`~pyrogram.types.MessageEntity`, *optional*):
+            Special entities like usernames, URLs, bot commands, etc. that appear in the explanation.
+
+        open_period (``int``, *optional*):
+            Amount of time in seconds the poll will be active after creation.
+
+        close_date (:py:obj:`~datetime.datetime`, *optional*):
+            Point in time when the poll will be automatically closed.
+    """
+
+    def __init__(
+        self,
+        *,
+        client: "pyrogram.Client" = None,
+        id: str,
+        question: str,
+        options: List["types.PollOption"],
+        total_voter_count: int,
+        is_closed: bool,
+        is_anonymous: bool = None,
+        type: "enums.PollType" = None,
+        allows_multiple_answers: bool = None,
+        chosen_option_id: Optional[int] = None,
+        correct_option_id: Optional[int] = None,
+        explanation: Optional[str] = None,
+        explanation_entities: Optional[List["types.MessageEntity"]] = None,
+        open_period: Optional[int] = None,
+        close_date: Optional[datetime] = None
+    ):
+        super().__init__(client)
+
+        self.id = id
+        self.question = question
+        self.options = options
+        self.total_voter_count = total_voter_count
+        self.is_closed = is_closed
+        self.is_anonymous = is_anonymous
+        self.type = type
+        self.allows_multiple_answers = allows_multiple_answers
+        self.chosen_option_id = chosen_option_id
+        self.correct_option_id = correct_option_id
+        self.explanation = explanation
+        self.explanation_entities = explanation_entities
+        self.open_period = open_period
+        self.close_date = close_date
+
+    @staticmethod
+    def _parse(client, media_poll: Union["raw.types.MessageMediaPoll", "raw.types.UpdateMessagePoll"]) -> "Poll":
+        poll: raw.types.Poll = media_poll.poll
+        poll_results: raw.types.PollResults = media_poll.results
+        results: List[raw.types.PollAnswerVoters] = poll_results.results
+
+        chosen_option_id = None
+        correct_option_id = None
+        options = []
+
+        for i, answer in enumerate(poll.answers):
+            voter_count = 0
+
+            if results:
+                result = results[i]
+                voter_count = result.voters
+
+                if result.chosen:
+                    chosen_option_id = i
+
+                if result.correct:
+                    correct_option_id = i
+
+            options.append(
+                types.PollOption(
+                    text=answer.text,
+                    voter_count=voter_count,
+                    data=answer.option,
+                    client=client
+                )
+            )
+
+        return Poll(
+            id=str(poll.id),
+            question=poll.question,
+            options=options,
+            total_voter_count=media_poll.results.total_voters,
+            is_closed=poll.closed,
+            is_anonymous=not poll.public_voters,
+            type=enums.PollType.QUIZ if poll.quiz else enums.PollType.REGULAR,
+            allows_multiple_answers=poll.multiple_choice,
+            chosen_option_id=chosen_option_id,
+            correct_option_id=correct_option_id,
+            explanation=poll_results.solution,
+            explanation_entities=[
+                types.MessageEntity._parse(client, i, {})
+                for i in poll_results.solution_entities
+            ] if poll_results.solution_entities else None,
+            open_period=poll.close_period,
+            close_date=utils.timestamp_to_datetime(poll.close_date),
+            client=client
+        )
+
+    @staticmethod
+    def _parse_update(client, update: "raw.types.UpdateMessagePoll"):
+        if update.poll is not None:
+            return Poll._parse(client, update)
+
+        results = update.results.results
+        chosen_option_id = None
+        correct_option_id = None
+        options = []
+
+        for i, result in enumerate(results):
+            if result.chosen:
+                chosen_option_id = i
+
+            if result.correct:
+                correct_option_id = i
+
+            options.append(
+                types.PollOption(
+                    text="",
+                    voter_count=result.voters,
+                    data=result.option,
+                    client=client
+                )
+            )
+
+        return Poll(
+            id=str(update.poll_id),
+            question="",
+            options=options,
+            total_voter_count=update.results.total_voters,
+            is_closed=False,
+            chosen_option_id=chosen_option_id,
+            correct_option_id=correct_option_id,
+            client=client
+        )
diff --git a/pyrogram/types/messages_and_media/poll_option.py b/pyrogram/types/messages_and_media/poll_option.py
new file mode 100644
index 00000000000..a40926b0778
--- /dev/null
+++ b/pyrogram/types/messages_and_media/poll_option.py
@@ -0,0 +1,50 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import pyrogram
+from ..object import Object
+
+
+class PollOption(Object):
+    """Contains information about one answer option in a poll.
+
+    Parameters:
+        text (``str``):
+            Option text, 1-100 characters.
+
+        voter_count (``int``):
+            Number of users that voted for this option.
+            Equals to 0 until you vote.
+
+        data (``bytes``):
+            The data this poll option is holding.
+    """
+
+    def __init__(
+        self,
+        *,
+        client: "pyrogram.Client" = None,
+        text: str,
+        voter_count: int,
+        data: bytes
+    ):
+        super().__init__(client)
+
+        self.text = text
+        self.voter_count = voter_count
+        self.data = data
diff --git a/pyrogram/types/messages_and_media/reaction.py b/pyrogram/types/messages_and_media/reaction.py
new file mode 100644
index 00000000000..17e08ff578b
--- /dev/null
+++ b/pyrogram/types/messages_and_media/reaction.py
@@ -0,0 +1,86 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import Optional
+
+import pyrogram
+from pyrogram import raw
+from ..object import Object
+
+
+class Reaction(Object):
+    """Contains information about a reaction.
+
+    Parameters:
+        emoji (``str``, *optional*):
+            Reaction emoji.
+
+        custom_emoji_id (``int``, *optional*):
+            Custom emoji id.
+
+        count (``int``, *optional*):
+            Reaction count.
+
+        chosen_order (``int``, *optional*):
+            Chosen reaction order.
+            Available for chosen reactions.
+    """
+
+    def __init__(
+        self,
+        *,
+        client: "pyrogram.Client" = None,
+        emoji: Optional[str] = None,
+        custom_emoji_id: Optional[int] = None,
+        count: Optional[int] = None,
+        chosen_order: Optional[int] = None
+    ):
+        super().__init__(client)
+
+        self.emoji = emoji
+        self.custom_emoji_id = custom_emoji_id
+        self.count = count
+        self.chosen_order = chosen_order
+
+    @staticmethod
+    def _parse(
+        client: "pyrogram.Client",
+        reaction: "raw.base.Reaction"
+    ) -> "Reaction":
+        if isinstance(reaction, raw.types.ReactionEmoji):
+            return Reaction(
+                client=client,
+                emoji=reaction.emoticon
+            )
+
+        if isinstance(reaction, raw.types.ReactionCustomEmoji):
+            return Reaction(
+                client=client,
+                custom_emoji_id=reaction.document_id
+            )
+
+    @staticmethod
+    def _parse_count(
+        client: "pyrogram.Client",
+        reaction_count: "raw.base.ReactionCount"
+    ) -> "Reaction":
+        reaction = Reaction._parse(client, reaction_count.reaction)
+        reaction.count = reaction_count.count
+        reaction.chosen_order = reaction_count.chosen_order
+
+        return reaction
diff --git a/pyrogram/types/messages_and_media/sticker.py b/pyrogram/types/messages_and_media/sticker.py
new file mode 100644
index 00000000000..de266b2fd2b
--- /dev/null
+++ b/pyrogram/types/messages_and_media/sticker.py
@@ -0,0 +1,206 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 datetime import datetime
+from typing import List, Dict, Type
+
+import pyrogram
+from pyrogram import raw, utils
+from pyrogram import types
+from pyrogram.errors import StickersetInvalid
+from pyrogram.file_id import FileId, FileType, FileUniqueId, FileUniqueType
+from ..object import Object
+
+
+class Sticker(Object):
+    """A sticker.
+
+    Parameters:
+        file_id (``str``):
+            Identifier for this file, which can be used to download or reuse the file.
+
+        file_unique_id (``str``):
+            Unique identifier for this file, which is supposed to be the same over time and for different accounts.
+            Can't be used to download or reuse the file.
+
+        width (``int``):
+            Sticker width.
+
+        height (``int``):
+            Sticker height.
+
+        is_animated (``bool``):
+            True, if the sticker is animated
+
+        is_video (``bool``):
+            True, if the sticker is a video sticker
+
+        file_name (``str``, *optional*):
+            Sticker file name.
+
+        mime_type (``str``, *optional*):
+            MIME type of the file as defined by sender.
+
+        file_size (``int``, *optional*):
+            File size.
+
+        date (:py:obj:`~datetime.datetime`, *optional*):
+            Date the sticker was sent.
+
+        emoji (``str``, *optional*):
+            Emoji associated with the sticker.
+
+        set_name (``str``, *optional*):
+            Name of the sticker set to which the sticker belongs.
+
+        thumbs (List of :obj:`~pyrogram.types.Thumbnail`, *optional*):
+            Sticker thumbnails in the .webp or .jpg format.
+    """
+
+    # TODO: Add mask position
+
+    def __init__(
+        self,
+        *,
+        client: "pyrogram.Client" = None,
+        file_id: str,
+        file_unique_id: str,
+        width: int,
+        height: int,
+        is_animated: bool,
+        is_video: bool,
+        file_name: str = None,
+        mime_type: str = None,
+        file_size: int = None,
+        date: datetime = None,
+        emoji: str = None,
+        set_name: str = None,
+        thumbs: List["types.Thumbnail"] = None
+    ):
+        super().__init__(client)
+
+        self.file_id = file_id
+        self.file_unique_id = file_unique_id
+        self.file_name = file_name
+        self.mime_type = mime_type
+        self.file_size = file_size
+        self.date = date
+        self.width = width
+        self.height = height
+        self.is_animated = is_animated
+        self.is_video = is_video
+        self.emoji = emoji
+        self.set_name = set_name
+        self.thumbs = thumbs
+        # self.mask_position = mask_position
+
+    cache = {}
+
+    @staticmethod
+    async def _get_sticker_set_name(invoke, input_sticker_set_id):
+        try:
+            set_id = input_sticker_set_id[0]
+            set_access_hash = input_sticker_set_id[1]
+
+            name = Sticker.cache.get((set_id, set_access_hash), None)
+
+            if name is not None:
+                return name
+
+            name = (await invoke(
+                raw.functions.messages.GetStickerSet(
+                    stickerset=raw.types.InputStickerSetID(
+                        id=set_id,
+                        access_hash=set_access_hash
+                    ),
+                    hash=0
+                )
+            )).set.short_name
+
+            Sticker.cache[(set_id, set_access_hash)] = name
+
+            if len(Sticker.cache) > 250:
+                for i in range(50):
+                    Sticker.cache.pop(next(iter(Sticker.cache)))
+
+            return name
+        except StickersetInvalid:
+            return None
+
+    @staticmethod
+    async def _parse(
+        client,
+        sticker: "raw.types.Document",
+        document_attributes: Dict[Type["raw.base.DocumentAttribute"], "raw.base.DocumentAttribute"],
+    ) -> "Sticker":
+        sticker_attributes = (
+            document_attributes[raw.types.DocumentAttributeSticker]
+            if raw.types.DocumentAttributeSticker in document_attributes
+            else document_attributes[raw.types.DocumentAttributeCustomEmoji]
+        )
+
+        image_size_attributes = document_attributes.get(raw.types.DocumentAttributeImageSize, None)
+        file_name = getattr(document_attributes.get(raw.types.DocumentAttributeFilename, None), "file_name", None)
+        video_attributes = document_attributes.get(raw.types.DocumentAttributeVideo, None)
+
+        sticker_set = sticker_attributes.stickerset
+
+        if isinstance(sticker_set, raw.types.InputStickerSetID):
+            input_sticker_set_id = (sticker_set.id, sticker_set.access_hash)
+            set_name = await Sticker._get_sticker_set_name(client.invoke, input_sticker_set_id)
+        else:
+            set_name = None
+
+        return Sticker(
+            file_id=FileId(
+                file_type=FileType.STICKER,
+                dc_id=sticker.dc_id,
+                media_id=sticker.id,
+                access_hash=sticker.access_hash,
+                file_reference=sticker.file_reference
+            ).encode(),
+            file_unique_id=FileUniqueId(
+                file_unique_type=FileUniqueType.DOCUMENT,
+                media_id=sticker.id
+            ).encode(),
+            width=(
+                image_size_attributes.w
+                if image_size_attributes
+                else video_attributes.w
+                if video_attributes
+                else 512
+            ),
+            height=(
+                image_size_attributes.h
+                if image_size_attributes
+                else video_attributes.h
+                if video_attributes
+                else 512
+            ),
+            is_animated=sticker.mime_type == "application/x-tgsticker",
+            is_video=sticker.mime_type == "video/webm",
+            # TODO: mask_position
+            set_name=set_name,
+            emoji=sticker_attributes.alt or None,
+            file_size=sticker.size,
+            mime_type=sticker.mime_type,
+            file_name=file_name,
+            date=utils.timestamp_to_datetime(sticker.date),
+            thumbs=types.Thumbnail._parse(client, sticker),
+            client=client
+        )
diff --git a/pyrogram/types/messages_and_media/stripped_thumbnail.py b/pyrogram/types/messages_and_media/stripped_thumbnail.py
new file mode 100644
index 00000000000..e9756607b96
--- /dev/null
+++ b/pyrogram/types/messages_and_media/stripped_thumbnail.py
@@ -0,0 +1,47 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import pyrogram
+from pyrogram import raw
+from ..object import Object
+
+
+class StrippedThumbnail(Object):
+    """A stripped thumbnail
+
+    Parameters:
+        data (``bytes``):
+            Thumbnail data
+    """
+
+    def __init__(
+        self,
+        *,
+        client: "pyrogram.Client" = None,
+        data: bytes
+    ):
+        super().__init__(client)
+
+        self.data = data
+
+    @staticmethod
+    def _parse(client, stripped_thumbnail: "raw.types.PhotoStrippedSize") -> "StrippedThumbnail":
+        return StrippedThumbnail(
+            data=stripped_thumbnail.bytes,
+            client=client
+        )
diff --git a/pyrogram/types/messages_and_media/thumbnail.py b/pyrogram/types/messages_and_media/thumbnail.py
new file mode 100644
index 00000000000..b9cb93e1274
--- /dev/null
+++ b/pyrogram/types/messages_and_media/thumbnail.py
@@ -0,0 +1,111 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import List, Optional, Union
+
+import pyrogram
+from pyrogram import raw
+from pyrogram.file_id import FileId, FileType, FileUniqueId, FileUniqueType, ThumbnailSource
+from ..object import Object
+
+
+class Thumbnail(Object):
+    """One size of a photo or a file/sticker thumbnail.
+
+    Parameters:
+        file_id (``str``):
+            Identifier for this file, which can be used to download or reuse the file.
+
+        file_unique_id (``str``):
+            Unique identifier for this file, which is supposed to be the same over time and for different accounts.
+            Can't be used to download or reuse the file.
+
+        width (``int``):
+            Photo width.
+
+        height (``int``):
+            Photo height.
+
+        file_size (``int``):
+            File size.
+    """
+
+    def __init__(
+        self,
+        *,
+        client: "pyrogram.Client" = None,
+        file_id: str,
+        file_unique_id: str,
+        width: int,
+        height: int,
+        file_size: int
+    ):
+        super().__init__(client)
+
+        self.file_id = file_id
+        self.file_unique_id = file_unique_id
+        self.width = width
+        self.height = height
+        self.file_size = file_size
+
+    @staticmethod
+    def _parse(client, media: Union["raw.types.Photo", "raw.types.Document"]) -> Optional[List["Thumbnail"]]:
+        if isinstance(media, raw.types.Photo):
+            raw_thumbs = [i for i in media.sizes if isinstance(i, raw.types.PhotoSize)]
+            raw_thumbs.sort(key=lambda p: p.size)
+            raw_thumbs = raw_thumbs[:-1]
+
+            file_type = FileType.PHOTO
+        elif isinstance(media, raw.types.Document):
+            raw_thumbs = media.thumbs
+            file_type = FileType.THUMBNAIL
+        else:
+            return
+
+        parsed_thumbs = []
+
+        for thumb in raw_thumbs:
+            if not isinstance(thumb, raw.types.PhotoSize):
+                continue
+
+            parsed_thumbs.append(
+                Thumbnail(
+                    file_id=FileId(
+                        file_type=file_type,
+                        dc_id=media.dc_id,
+                        media_id=media.id,
+                        access_hash=media.access_hash,
+                        file_reference=media.file_reference,
+                        thumbnail_file_type=file_type,
+                        thumbnail_source=ThumbnailSource.THUMBNAIL,
+                        thumbnail_size=thumb.type,
+                        volume_id=0,
+                        local_id=0
+                    ).encode(),
+                    file_unique_id=FileUniqueId(
+                        file_unique_type=FileUniqueType.DOCUMENT,
+                        media_id=media.id
+                    ).encode(),
+                    width=thumb.w,
+                    height=thumb.h,
+                    file_size=thumb.size,
+                    client=client
+                )
+            )
+
+        return parsed_thumbs or None
diff --git a/pyrogram/types/messages_and_media/venue.py b/pyrogram/types/messages_and_media/venue.py
new file mode 100644
index 00000000000..8a26f600610
--- /dev/null
+++ b/pyrogram/types/messages_and_media/venue.py
@@ -0,0 +1,74 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import pyrogram
+from pyrogram import raw
+from pyrogram import types
+from ..object import Object
+
+
+class Venue(Object):
+    """A venue.
+
+    Parameters:
+        location (:obj:`~pyrogram.types.Location`):
+            Venue location.
+
+        title (``str``):
+            Name of the venue.
+
+        address (``str``):
+            Address of the venue.
+
+        foursquare_id (``str``, *optional*):
+            Foursquare identifier of the venue.
+
+        foursquare_type (``str``, *optional*):
+            Foursquare type of the venue.
+            (For example, "arts_entertainment/default", "arts_entertainment/aquarium" or "food/icecream".)
+
+    """
+
+    def __init__(
+        self,
+        *,
+        client: "pyrogram.Client" = None,
+        location: "types.Location",
+        title: str,
+        address: str,
+        foursquare_id: str = None,
+        foursquare_type: str = None
+    ):
+        super().__init__(client)
+
+        self.location = location
+        self.title = title
+        self.address = address
+        self.foursquare_id = foursquare_id
+        self.foursquare_type = foursquare_type
+
+    @staticmethod
+    def _parse(client, venue: "raw.types.MessageMediaVenue"):
+        return Venue(
+            location=types.Location._parse(client, venue.geo),
+            title=venue.title,
+            address=venue.address,
+            foursquare_id=venue.venue_id or None,
+            foursquare_type=venue.venue_type,
+            client=client
+        )
diff --git a/pyrogram/types/messages_and_media/video.py b/pyrogram/types/messages_and_media/video.py
new file mode 100644
index 00000000000..b50f9fca782
--- /dev/null
+++ b/pyrogram/types/messages_and_media/video.py
@@ -0,0 +1,134 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 datetime import datetime
+from typing import List
+
+import pyrogram
+from pyrogram import raw, utils
+from pyrogram import types
+from pyrogram.file_id import FileId, FileType, FileUniqueId, FileUniqueType
+from ..object import Object
+
+
+class Video(Object):
+    """A video file.
+
+    Parameters:
+        file_id (``str``):
+            Identifier for this file, which can be used to download or reuse the file.
+
+        file_unique_id (``str``):
+            Unique identifier for this file, which is supposed to be the same over time and for different accounts.
+            Can't be used to download or reuse the file.
+
+        width (``int``):
+            Video width as defined by sender.
+
+        height (``int``):
+            Video height as defined by sender.
+
+        duration (``int``):
+            Duration of the video in seconds as defined by sender.
+
+        file_name (``str``, *optional*):
+            Video file name.
+
+        mime_type (``str``, *optional*):
+            Mime type of a file as defined by sender.
+
+        file_size (``int``, *optional*):
+            File size.
+
+        supports_streaming (``bool``, *optional*):
+            True, if the video was uploaded with streaming support.
+
+        ttl_seconds (``int``. *optional*):
+            Time-to-live seconds, for secret photos.
+
+        date (:py:obj:`~datetime.datetime`, *optional*):
+            Date the video was sent.
+
+        thumbs (List of :obj:`~pyrogram.types.Thumbnail`, *optional*):
+            Video thumbnails.
+    """
+
+    def __init__(
+        self,
+        *,
+        client: "pyrogram.Client" = None,
+        file_id: str,
+        file_unique_id: str,
+        width: int,
+        height: int,
+        duration: int,
+        file_name: str = None,
+        mime_type: str = None,
+        file_size: int = None,
+        supports_streaming: bool = None,
+        ttl_seconds: int = None,
+        date: datetime = None,
+        thumbs: List["types.Thumbnail"] = None
+    ):
+        super().__init__(client)
+
+        self.file_id = file_id
+        self.file_unique_id = file_unique_id
+        self.width = width
+        self.height = height
+        self.duration = duration
+        self.file_name = file_name
+        self.mime_type = mime_type
+        self.file_size = file_size
+        self.supports_streaming = supports_streaming
+        self.ttl_seconds = ttl_seconds
+        self.date = date
+        self.thumbs = thumbs
+
+    @staticmethod
+    def _parse(
+        client,
+        video: "raw.types.Document",
+        video_attributes: "raw.types.DocumentAttributeVideo",
+        file_name: str,
+        ttl_seconds: int = None
+    ) -> "Video":
+        return Video(
+            file_id=FileId(
+                file_type=FileType.VIDEO,
+                dc_id=video.dc_id,
+                media_id=video.id,
+                access_hash=video.access_hash,
+                file_reference=video.file_reference
+            ).encode(),
+            file_unique_id=FileUniqueId(
+                file_unique_type=FileUniqueType.DOCUMENT,
+                media_id=video.id
+            ).encode(),
+            width=video_attributes.w,
+            height=video_attributes.h,
+            duration=video_attributes.duration,
+            file_name=file_name,
+            mime_type=video.mime_type,
+            supports_streaming=video_attributes.supports_streaming,
+            file_size=video.size,
+            date=utils.timestamp_to_datetime(video.date),
+            ttl_seconds=ttl_seconds,
+            thumbs=types.Thumbnail._parse(client, video),
+            client=client
+        )
diff --git a/pyrogram/types/messages_and_media/video_note.py b/pyrogram/types/messages_and_media/video_note.py
new file mode 100644
index 00000000000..3e6b40d0c91
--- /dev/null
+++ b/pyrogram/types/messages_and_media/video_note.py
@@ -0,0 +1,108 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 datetime import datetime
+from typing import List
+
+import pyrogram
+from pyrogram import raw, utils
+from pyrogram import types
+from pyrogram.file_id import FileId, FileType, FileUniqueId, FileUniqueType
+from ..object import Object
+
+
+class VideoNote(Object):
+    """A video note.
+
+    Parameters:
+        file_id (``str``):
+            Identifier for this file, which can be used to download or reuse the file.
+
+        file_unique_id (``str``):
+            Unique identifier for this file, which is supposed to be the same over time and for different accounts.
+            Can't be used to download or reuse the file.
+
+        length (``int``):
+            Video width and height as defined by sender.
+
+        duration (``int``):
+            Duration of the video in seconds as defined by sender.
+
+        mime_type (``str``, *optional*):
+            MIME type of the file as defined by sender.
+
+        file_size (``int``, *optional*):
+            File size.
+
+        date (:py:obj:`~datetime.datetime`, *optional*):
+            Date the video note was sent.
+
+        thumbs (List of :obj:`~pyrogram.types.Thumbnail`, *optional*):
+            Video thumbnails.
+    """
+
+    def __init__(
+        self,
+        *,
+        client: "pyrogram.Client" = None,
+        file_id: str,
+        file_unique_id: str,
+        length: int,
+        duration: int,
+        thumbs: List["types.Thumbnail"] = None,
+        mime_type: str = None,
+        file_size: int = None,
+        date: datetime = None
+    ):
+        super().__init__(client)
+
+        self.file_id = file_id
+        self.file_unique_id = file_unique_id
+        self.mime_type = mime_type
+        self.file_size = file_size
+        self.date = date
+        self.length = length
+        self.duration = duration
+        self.thumbs = thumbs
+
+    @staticmethod
+    def _parse(
+        client,
+        video_note: "raw.types.Document",
+        video_attributes: "raw.types.DocumentAttributeVideo"
+    ) -> "VideoNote":
+        return VideoNote(
+            file_id=FileId(
+                file_type=FileType.VIDEO_NOTE,
+                dc_id=video_note.dc_id,
+                media_id=video_note.id,
+                access_hash=video_note.access_hash,
+                file_reference=video_note.file_reference
+            ).encode(),
+            file_unique_id=FileUniqueId(
+                file_unique_type=FileUniqueType.DOCUMENT,
+                media_id=video_note.id
+            ).encode(),
+            length=video_attributes.w,
+            duration=video_attributes.duration,
+            file_size=video_note.size,
+            mime_type=video_note.mime_type,
+            date=utils.timestamp_to_datetime(video_note.date),
+            thumbs=types.Thumbnail._parse(client, video_note),
+            client=client
+        )
diff --git a/pyrogram/types/messages_and_media/voice.py b/pyrogram/types/messages_and_media/voice.py
new file mode 100644
index 00000000000..8d1c15f657f
--- /dev/null
+++ b/pyrogram/types/messages_and_media/voice.py
@@ -0,0 +1,96 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 datetime import datetime
+
+import pyrogram
+from pyrogram import raw, utils
+from pyrogram.file_id import FileId, FileType, FileUniqueId, FileUniqueType
+from ..object import Object
+
+
+class Voice(Object):
+    """A voice note.
+
+    Parameters:
+        file_id (``str``):
+            Identifier for this file, which can be used to download or reuse the file.
+
+        file_unique_id (``str``):
+            Unique identifier for this file, which is supposed to be the same over time and for different accounts.
+            Can't be used to download or reuse the file.
+
+        duration (``int``):
+            Duration of the audio in seconds as defined by sender.
+
+        waveform (``bytes``, *optional*):
+            Voice waveform.
+
+        mime_type (``str``, *optional*):
+            MIME type of the file as defined by sender.
+
+        file_size (``int``, *optional*):
+            File size.
+
+        date (:py:obj:`~datetime.datetime`, *optional*):
+            Date the voice was sent.
+    """
+
+    def __init__(
+        self,
+        *,
+        client: "pyrogram.Client" = None,
+        file_id: str,
+        file_unique_id: str,
+        duration: int,
+        waveform: bytes = None,
+        mime_type: str = None,
+        file_size: int = None,
+        date: datetime = None
+    ):
+        super().__init__(client)
+
+        self.file_id = file_id
+        self.file_unique_id = file_unique_id
+        self.duration = duration
+        self.waveform = waveform
+        self.mime_type = mime_type
+        self.file_size = file_size
+        self.date = date
+
+    @staticmethod
+    def _parse(client, voice: "raw.types.Document", attributes: "raw.types.DocumentAttributeAudio") -> "Voice":
+        return Voice(
+            file_id=FileId(
+                file_type=FileType.VOICE,
+                dc_id=voice.dc_id,
+                media_id=voice.id,
+                access_hash=voice.access_hash,
+                file_reference=voice.file_reference
+            ).encode(),
+            file_unique_id=FileUniqueId(
+                file_unique_type=FileUniqueType.DOCUMENT,
+                media_id=voice.id
+            ).encode(),
+            duration=attributes.duration,
+            mime_type=voice.mime_type,
+            file_size=voice.size,
+            waveform=attributes.waveform,
+            date=utils.timestamp_to_datetime(voice.date),
+            client=client
+        )
diff --git a/pyrogram/types/messages_and_media/web_app_data.py b/pyrogram/types/messages_and_media/web_app_data.py
new file mode 100644
index 00000000000..b9a471fd89c
--- /dev/null
+++ b/pyrogram/types/messages_and_media/web_app_data.py
@@ -0,0 +1,51 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 pyrogram import raw
+from ..object import Object
+
+
+class WebAppData(Object):
+    """Contains data sent from a `Web App `_ to the bot.
+
+    Parameters:
+        data (``str``):
+            The data.
+
+        button_text (``str``):
+            Text of the *web_app* keyboard button, from which the Web App was opened.
+
+    """
+
+    def __init__(
+        self,
+        *,
+        data: str,
+        button_text: str,
+    ):
+        super().__init__()
+
+        self.data = data
+        self.button_text = button_text
+
+    @staticmethod
+    def _parse(action: "raw.types.MessageActionWebViewDataSentMe"):
+        return WebAppData(
+            data=action.data,
+            button_text=action.text
+        )
diff --git a/pyrogram/types/messages_and_media/web_page.py b/pyrogram/types/messages_and_media/web_page.py
new file mode 100644
index 00000000000..34e51d88cc7
--- /dev/null
+++ b/pyrogram/types/messages_and_media/web_page.py
@@ -0,0 +1,187 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import pyrogram
+from pyrogram import raw
+from pyrogram import types
+from ..object import Object
+
+
+class WebPage(Object):
+    # TODO: hash, cached_page
+    """A webpage preview
+
+    Parameters:
+        id (``str``):
+            Unique identifier for this webpage.
+
+        url (``str``):
+            Full URL for this webpage.
+
+        display_url (``str``):
+            Display URL for this webpage.
+
+        type (``str``, *optional*):
+            Type of webpage preview, known types (at the time of writing) are:
+            *"article"*, *"photo"*, *"gif"*, *"video"* and *"document"*,
+            *"telegram_user"*, *"telegram_bot"*, *"telegram_channel"*, *"telegram_megagroup"*.
+
+        site_name (``str``, *optional*):
+            Webpage site name.
+
+        title (``str``, *optional*):
+            Title of this webpage.
+
+        description (``str``, *optional*):
+            Description of this webpage.
+
+        audio (:obj:`~pyrogram.types.Audio`, *optional*):
+            Webpage preview is an audio file, information about the file.
+
+        document (:obj:`~pyrogram.types.Document`, *optional*):
+            Webpage preview is a general file, information about the file.
+
+        photo (:obj:`~pyrogram.types.Photo`, *optional*):
+            Webpage preview is a photo, information about the photo.
+
+        animation (:obj:`~pyrogram.types.Animation`, *optional*):
+            Webpage preview is an animation, information about the animation.
+
+        video (:obj:`~pyrogram.types.Video`, *optional*):
+            Webpage preview is a video, information about the video.
+
+        embed_url (``str``, *optional*):
+            Embedded content URL.
+
+        embed_type (``str``, *optional*):
+            Embedded content type, like `iframe`
+
+        embed_width (``int``, *optional*):
+            Embedded content width.
+
+        embed_height (``int``, *optional*):
+            Embedded content height.
+
+        duration (``int``, *optional*):
+            Unknown at the time of writing.
+
+        author (``str``, *optional*):
+            Author of the webpage, eg the Twitter user for a tweet, or the author in an article.
+    """
+
+    def __init__(
+        self,
+        *,
+        client: "pyrogram.Client" = None,
+        id: str,
+        url: str,
+        display_url: str,
+        type: str = None,
+        site_name: str = None,
+        title: str = None,
+        description: str = None,
+        audio: "types.Audio" = None,
+        document: "types.Document" = None,
+        photo: "types.Photo" = None,
+        animation: "types.Animation" = None,
+        video: "types.Video" = None,
+        embed_url: str = None,
+        embed_type: str = None,
+        embed_width: int = None,
+        embed_height: int = None,
+        duration: int = None,
+        author: str = None
+    ):
+        super().__init__(client)
+
+        self.id = id
+        self.url = url
+        self.display_url = display_url
+        self.type = type
+        self.site_name = site_name
+        self.title = title
+        self.description = description
+        self.audio = audio
+        self.document = document
+        self.photo = photo
+        self.animation = animation
+        self.video = video
+        self.embed_url = embed_url
+        self.embed_type = embed_type
+        self.embed_width = embed_width
+        self.embed_height = embed_height
+        self.duration = duration
+        self.author = author
+
+    @staticmethod
+    def _parse(client, webpage: "raw.types.WebPage") -> "WebPage":
+        audio = None
+        document = None
+        photo = None
+        animation = None
+        video = None
+
+        if isinstance(webpage.photo, raw.types.Photo):
+            photo = types.Photo._parse(client, webpage.photo)
+
+        doc = webpage.document
+
+        if isinstance(doc, raw.types.Document):
+            attributes = {type(i): i for i in doc.attributes}
+
+            file_name = getattr(
+                attributes.get(
+                    raw.types.DocumentAttributeFilename, None
+                ), "file_name", None
+            )
+
+            if raw.types.DocumentAttributeAudio in attributes:
+                audio_attributes = attributes[raw.types.DocumentAttributeAudio]
+                audio = types.Audio._parse(client, doc, audio_attributes, file_name)
+
+            elif raw.types.DocumentAttributeAnimated in attributes:
+                video_attributes = attributes.get(raw.types.DocumentAttributeVideo, None)
+                animation = types.Animation._parse(client, doc, video_attributes, file_name)
+
+            elif raw.types.DocumentAttributeVideo in attributes:
+                video_attributes = attributes[raw.types.DocumentAttributeVideo]
+                video = types.Video._parse(client, doc, video_attributes, file_name)
+
+            else:
+                document = types.Document._parse(client, doc, file_name)
+
+        return WebPage(
+            id=str(webpage.id),
+            url=webpage.url,
+            display_url=webpage.display_url,
+            type=webpage.type,
+            site_name=webpage.site_name,
+            title=webpage.title,
+            description=webpage.description,
+            audio=audio,
+            document=document,
+            photo=photo,
+            animation=animation,
+            video=video,
+            embed_url=webpage.embed_url,
+            embed_type=webpage.embed_type,
+            embed_width=webpage.embed_width,
+            embed_height=webpage.embed_height,
+            duration=webpage.duration,
+            author=webpage.author
+        )
diff --git a/pyrogram/types/object.py b/pyrogram/types/object.py
new file mode 100644
index 00000000000..3253c8ecff9
--- /dev/null
+++ b/pyrogram/types/object.py
@@ -0,0 +1,121 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import typing
+from datetime import datetime
+from enum import Enum
+from json import dumps
+
+import pyrogram
+
+
+class Object:
+    def __init__(self, client: "pyrogram.Client" = None):
+        self._client = client
+
+    def bind(self, client: "pyrogram.Client"):
+        """Bind a Client instance to this and to all nested Pyrogram objects.
+
+        Parameters:
+            client (:obj:`~pyrogram.types.Client`):
+                The Client instance to bind this object with. Useful to re-enable bound methods after serializing and
+                deserializing Pyrogram objects with ``repr`` and ``eval``.
+        """
+        self._client = client
+
+        for i in self.__dict__:
+            o = getattr(self, i)
+
+            if isinstance(o, Object):
+                o.bind(client)
+
+    @staticmethod
+    def default(obj: "Object"):
+        if isinstance(obj, bytes):
+            return repr(obj)
+
+        # https://t.me/pyrogramchat/167281
+        # Instead of re.Match, which breaks for python <=3.6
+        if isinstance(obj, typing.Match):
+            return repr(obj)
+
+        if isinstance(obj, Enum):
+            return str(obj)
+
+        if isinstance(obj, datetime):
+            return str(obj)
+
+        return {
+            "_": obj.__class__.__name__,
+            **{
+                attr: (
+                    "*" * 9 if attr == "phone_number" else
+                    getattr(obj, attr)
+                )
+                for attr in filter(lambda x: not x.startswith("_"), obj.__dict__)
+                if getattr(obj, attr) is not None
+            }
+        }
+
+    def __str__(self) -> str:
+        return dumps(self, indent=4, default=Object.default, ensure_ascii=False)
+
+    def __repr__(self) -> str:
+        return "pyrogram.types.{}({})".format(
+            self.__class__.__name__,
+            ", ".join(
+                f"{attr}={repr(getattr(self, attr))}"
+                for attr in filter(lambda x: not x.startswith("_"), self.__dict__)
+                if getattr(self, attr) is not None
+            )
+        )
+
+    def __eq__(self, other: "Object") -> bool:
+        for attr in self.__dict__:
+            try:
+                if attr.startswith("_"):
+                    continue
+
+                if getattr(self, attr) != getattr(other, attr):
+                    return False
+            except AttributeError:
+                return False
+
+        return True
+
+    def __setstate__(self, state):
+        for attr in state:
+            obj = state[attr]
+
+            # Maybe a better alternative would be https://docs.python.org/3/library/inspect.html#inspect.signature
+            if isinstance(obj, tuple) and len(obj) == 2 and obj[0] == "dt":
+                state[attr] = datetime.fromtimestamp(obj[1])
+
+        self.__dict__ = state
+
+    def __getstate__(self):
+        state = self.__dict__.copy()
+        state.pop("_client", None)
+
+        for attr in state:
+            obj = state[attr]
+
+            if isinstance(obj, datetime):
+                state[attr] = ("dt", obj.timestamp())
+
+        return state
diff --git a/pyrogram/types/update.py b/pyrogram/types/update.py
new file mode 100644
index 00000000000..d3e45b4abdb
--- /dev/null
+++ b/pyrogram/types/update.py
@@ -0,0 +1,27 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import pyrogram
+
+
+class Update:
+    def stop_propagation(self):
+        raise pyrogram.StopPropagation
+
+    def continue_propagation(self):
+        raise pyrogram.ContinuePropagation
diff --git a/pyrogram/types/user_and_chats/__init__.py b/pyrogram/types/user_and_chats/__init__.py
new file mode 100644
index 00000000000..348ecd9f3ca
--- /dev/null
+++ b/pyrogram/types/user_and_chats/__init__.py
@@ -0,0 +1,67 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .chat import Chat
+from .chat_admin_with_invite_links import ChatAdminWithInviteLinks
+from .chat_event import ChatEvent
+from .chat_event_filter import ChatEventFilter
+from .chat_invite_link import ChatInviteLink
+from .chat_join_request import ChatJoinRequest
+from .chat_joiner import ChatJoiner
+from .chat_member import ChatMember
+from .chat_member_updated import ChatMemberUpdated
+from .chat_permissions import ChatPermissions
+from .chat_photo import ChatPhoto
+from .chat_preview import ChatPreview
+from .chat_privileges import ChatPrivileges
+from .chat_reactions import ChatReactions
+from .dialog import Dialog
+from .emoji_status import EmojiStatus
+from .invite_link_importer import InviteLinkImporter
+from .restriction import Restriction
+from .user import User
+from .video_chat_ended import VideoChatEnded
+from .video_chat_members_invited import VideoChatMembersInvited
+from .video_chat_scheduled import VideoChatScheduled
+from .video_chat_started import VideoChatStarted
+
+__all__ = [
+    "Chat",
+    "ChatMember",
+    "ChatPermissions",
+    "ChatPhoto",
+    "ChatPreview",
+    "Dialog",
+    "User",
+    "Restriction",
+    "ChatEvent",
+    "ChatEventFilter",
+    "ChatInviteLink",
+    "InviteLinkImporter",
+    "ChatAdminWithInviteLinks",
+    "VideoChatStarted",
+    "VideoChatEnded",
+    "VideoChatMembersInvited",
+    "ChatMemberUpdated",
+    "VideoChatScheduled",
+    "ChatJoinRequest",
+    "ChatPrivileges",
+    "ChatJoiner",
+    "EmojiStatus",
+    "ChatReactions"
+]
diff --git a/pyrogram/types/user_and_chats/chat.py b/pyrogram/types/user_and_chats/chat.py
new file mode 100644
index 00000000000..f37542fb0cd
--- /dev/null
+++ b/pyrogram/types/user_and_chats/chat.py
@@ -0,0 +1,963 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 datetime import datetime
+from typing import Union, List, Optional, AsyncGenerator, BinaryIO
+
+import pyrogram
+from pyrogram import raw, enums
+from pyrogram import types
+from pyrogram import utils
+from ..object import Object
+
+
+class Chat(Object):
+    """A chat.
+
+    Parameters:
+        id (``int``):
+            Unique identifier for this chat.
+
+        type (:obj:`~pyrogram.enums.ChatType`):
+            Type of chat.
+
+        is_verified (``bool``, *optional*):
+            True, if this chat has been verified by Telegram. Supergroups, channels and bots only.
+
+        is_restricted (``bool``, *optional*):
+            True, if this chat has been restricted. Supergroups, channels and bots only.
+            See *restriction_reason* for details.
+
+        is_creator (``bool``, *optional*):
+            True, if this chat owner is the current user. Supergroups, channels and groups only.
+
+        is_scam (``bool``, *optional*):
+            True, if this chat has been flagged for scam.
+
+        is_fake (``bool``, *optional*):
+            True, if this chat has been flagged for impersonation.
+
+        is_support (``bool``):
+            True, if this chat is part of the Telegram support team. Users and bots only.
+
+        title (``str``, *optional*):
+            Title, for supergroups, channels and basic group chats.
+
+        username (``str``, *optional*):
+            Username, for private chats, bots, supergroups and channels if available.
+
+        first_name (``str``, *optional*):
+            First name of the other party in a private chat, for private chats and bots.
+
+        last_name (``str``, *optional*):
+            Last name of the other party in a private chat, for private chats.
+
+        photo (:obj:`~pyrogram.types.ChatPhoto`, *optional*):
+            Chat photo. Suitable for downloads only.
+
+        bio (``str``, *optional*):
+            Bio of the other party in a private chat.
+            Returned only in :meth:`~pyrogram.Client.get_chat`.
+
+        description (``str``, *optional*):
+            Description, for groups, supergroups and channel chats.
+            Returned only in :meth:`~pyrogram.Client.get_chat`.
+
+        dc_id (``int``, *optional*):
+            The chat assigned DC (data center). Available only in case the chat has a photo.
+            Note that this information is approximate; it is based on where Telegram stores the current chat photo.
+            It is accurate only in case the owner has set the chat photo, otherwise the dc_id will be the one assigned
+            to the administrator who set the current chat photo.
+
+        has_protected_content (``bool``, *optional*):
+            True, if messages from the chat can't be forwarded to other chats.
+
+        invite_link (``str``, *optional*):
+            Chat invite link, for groups, supergroups and channels.
+            Returned only in :meth:`~pyrogram.Client.get_chat`.
+
+        pinned_message (:obj:`~pyrogram.types.Message`, *optional*):
+            Pinned message, for groups, supergroups channels and own chat.
+            Returned only in :meth:`~pyrogram.Client.get_chat`.
+
+        sticker_set_name (``str``, *optional*):
+            For supergroups, name of group sticker set.
+            Returned only in :meth:`~pyrogram.Client.get_chat`.
+
+        can_set_sticker_set (``bool``, *optional*):
+            True, if the group sticker set can be changed by you.
+            Returned only in :meth:`~pyrogram.Client.get_chat`.
+
+        members_count (``int``, *optional*):
+            Chat members count, for groups, supergroups and channels only.
+            Returned only in :meth:`~pyrogram.Client.get_chat`.
+
+        restrictions (List of :obj:`~pyrogram.types.Restriction`, *optional*):
+            The list of reasons why this chat might be unavailable to some users.
+            This field is available only in case *is_restricted* is True.
+
+        permissions (:obj:`~pyrogram.types.ChatPermissions` *optional*):
+            Default chat member permissions, for groups and supergroups.
+
+        distance (``int``, *optional*):
+            Distance in meters of this group chat from your location.
+            Returned only in :meth:`~pyrogram.Client.get_nearby_chats`.
+
+        linked_chat (:obj:`~pyrogram.types.Chat`, *optional*):
+            The linked discussion group (in case of channels) or the linked channel (in case of supergroups).
+            Returned only in :meth:`~pyrogram.Client.get_chat`.
+
+        send_as_chat (:obj:`~pyrogram.types.Chat`, *optional*):
+            The default "send_as" chat.
+            Returned only in :meth:`~pyrogram.Client.get_chat`.
+
+        available_reactions (:obj:`~pyrogram.types.ChatReactions`, *optional*):
+            Available reactions in the chat.
+            Returned only in :meth:`~pyrogram.Client.get_chat`.
+    """
+
+    def __init__(
+        self,
+        *,
+        client: "pyrogram.Client" = None,
+        id: int,
+        type: "enums.ChatType",
+        is_verified: bool = None,
+        is_restricted: bool = None,
+        is_creator: bool = None,
+        is_scam: bool = None,
+        is_fake: bool = None,
+        is_support: bool = None,
+        title: str = None,
+        username: str = None,
+        first_name: str = None,
+        last_name: str = None,
+        photo: "types.ChatPhoto" = None,
+        bio: str = None,
+        description: str = None,
+        dc_id: int = None,
+        has_protected_content: bool = None,
+        invite_link: str = None,
+        pinned_message=None,
+        sticker_set_name: str = None,
+        can_set_sticker_set: bool = None,
+        members_count: int = None,
+        restrictions: List["types.Restriction"] = None,
+        permissions: "types.ChatPermissions" = None,
+        distance: int = None,
+        linked_chat: "types.Chat" = None,
+        send_as_chat: "types.Chat" = None,
+        available_reactions: Optional["types.ChatReactions"] = None
+    ):
+        super().__init__(client)
+
+        self.id = id
+        self.type = type
+        self.is_verified = is_verified
+        self.is_restricted = is_restricted
+        self.is_creator = is_creator
+        self.is_scam = is_scam
+        self.is_fake = is_fake
+        self.is_support = is_support
+        self.title = title
+        self.username = username
+        self.first_name = first_name
+        self.last_name = last_name
+        self.photo = photo
+        self.bio = bio
+        self.description = description
+        self.dc_id = dc_id
+        self.has_protected_content = has_protected_content
+        self.invite_link = invite_link
+        self.pinned_message = pinned_message
+        self.sticker_set_name = sticker_set_name
+        self.can_set_sticker_set = can_set_sticker_set
+        self.members_count = members_count
+        self.restrictions = restrictions
+        self.permissions = permissions
+        self.distance = distance
+        self.linked_chat = linked_chat
+        self.send_as_chat = send_as_chat
+        self.available_reactions = available_reactions
+
+    @staticmethod
+    def _parse_user_chat(client, user: raw.types.User) -> "Chat":
+        peer_id = user.id
+
+        return Chat(
+            id=peer_id,
+            type=enums.ChatType.BOT if user.bot else enums.ChatType.PRIVATE,
+            is_verified=getattr(user, "verified", None),
+            is_restricted=getattr(user, "restricted", None),
+            is_scam=getattr(user, "scam", None),
+            is_fake=getattr(user, "fake", None),
+            is_support=getattr(user, "support", None),
+            username=user.username,
+            first_name=user.first_name,
+            last_name=user.last_name,
+            photo=types.ChatPhoto._parse(client, user.photo, peer_id, user.access_hash),
+            restrictions=types.List([types.Restriction._parse(r) for r in user.restriction_reason]) or None,
+            dc_id=getattr(getattr(user, "photo", None), "dc_id", None),
+            client=client
+        )
+
+    @staticmethod
+    def _parse_chat_chat(client, chat: raw.types.Chat) -> "Chat":
+        peer_id = -chat.id
+
+        return Chat(
+            id=peer_id,
+            type=enums.ChatType.GROUP,
+            title=chat.title,
+            is_creator=getattr(chat, "creator", None),
+            photo=types.ChatPhoto._parse(client, getattr(chat, "photo", None), peer_id, 0),
+            permissions=types.ChatPermissions._parse(getattr(chat, "default_banned_rights", None)),
+            members_count=getattr(chat, "participants_count", None),
+            dc_id=getattr(getattr(chat, "photo", None), "dc_id", None),
+            has_protected_content=getattr(chat, "noforwards", None),
+            client=client
+        )
+
+    @staticmethod
+    def _parse_channel_chat(client, channel: raw.types.Channel) -> "Chat":
+        peer_id = utils.get_channel_id(channel.id)
+        restriction_reason = getattr(channel, "restriction_reason", [])
+
+        return Chat(
+            id=peer_id,
+            type=enums.ChatType.SUPERGROUP if getattr(channel, "megagroup", None) else enums.ChatType.CHANNEL,
+            is_verified=getattr(channel, "verified", None),
+            is_restricted=getattr(channel, "restricted", None),
+            is_creator=getattr(channel, "creator", None),
+            is_scam=getattr(channel, "scam", None),
+            is_fake=getattr(channel, "fake", None),
+            title=channel.title,
+            username=getattr(channel, "username", None),
+            photo=types.ChatPhoto._parse(client, getattr(channel, "photo", None), peer_id,
+                                         getattr(channel, "access_hash", 0)),
+            restrictions=types.List([types.Restriction._parse(r) for r in restriction_reason]) or None,
+            permissions=types.ChatPermissions._parse(getattr(channel, "default_banned_rights", None)),
+            members_count=getattr(channel, "participants_count", None),
+            dc_id=getattr(getattr(channel, "photo", None), "dc_id", None),
+            has_protected_content=getattr(channel, "noforwards", None),
+            client=client
+        )
+
+    @staticmethod
+    def _parse(
+        client,
+        message: Union[raw.types.Message, raw.types.MessageService],
+        users: dict,
+        chats: dict,
+        is_chat: bool
+    ) -> "Chat":
+        from_id = utils.get_raw_peer_id(message.from_id)
+        peer_id = utils.get_raw_peer_id(message.peer_id)
+        chat_id = (peer_id or from_id) if is_chat else (from_id or peer_id)
+
+        if isinstance(message.peer_id, raw.types.PeerUser):
+            return Chat._parse_user_chat(client, users[chat_id])
+
+        if isinstance(message.peer_id, raw.types.PeerChat):
+            return Chat._parse_chat_chat(client, chats[chat_id])
+
+        return Chat._parse_channel_chat(client, chats[chat_id])
+
+    @staticmethod
+    def _parse_dialog(client, peer, users: dict, chats: dict):
+        if isinstance(peer, raw.types.PeerUser):
+            return Chat._parse_user_chat(client, users[peer.user_id])
+        elif isinstance(peer, raw.types.PeerChat):
+            return Chat._parse_chat_chat(client, chats[peer.chat_id])
+        else:
+            return Chat._parse_channel_chat(client, chats[peer.channel_id])
+
+    @staticmethod
+    async def _parse_full(client, chat_full: Union[raw.types.messages.ChatFull, raw.types.users.UserFull]) -> "Chat":
+        users = {u.id: u for u in chat_full.users}
+        chats = {c.id: c for c in chat_full.chats}
+
+        if isinstance(chat_full, raw.types.users.UserFull):
+            full_user = chat_full.full_user
+
+            parsed_chat = Chat._parse_user_chat(client, users[full_user.id])
+            parsed_chat.bio = full_user.about
+
+            if full_user.pinned_msg_id:
+                parsed_chat.pinned_message = await client.get_messages(
+                    parsed_chat.id,
+                    message_ids=full_user.pinned_msg_id
+                )
+        else:
+            full_chat = chat_full.full_chat
+            chat_raw = chats[full_chat.id]
+
+            if isinstance(full_chat, raw.types.ChatFull):
+                parsed_chat = Chat._parse_chat_chat(client, chat_raw)
+                parsed_chat.description = full_chat.about or None
+
+                if isinstance(full_chat.participants, raw.types.ChatParticipants):
+                    parsed_chat.members_count = len(full_chat.participants.participants)
+            else:
+                parsed_chat = Chat._parse_channel_chat(client, chat_raw)
+                parsed_chat.members_count = full_chat.participants_count
+                parsed_chat.description = full_chat.about or None
+                # TODO: Add StickerSet type
+                parsed_chat.can_set_sticker_set = full_chat.can_set_stickers
+                parsed_chat.sticker_set_name = getattr(full_chat.stickerset, "short_name", None)
+
+                linked_chat_raw = chats.get(full_chat.linked_chat_id, None)
+
+                if linked_chat_raw:
+                    parsed_chat.linked_chat = Chat._parse_channel_chat(client, linked_chat_raw)
+
+                default_send_as = full_chat.default_send_as
+
+                if default_send_as:
+                    if isinstance(default_send_as, raw.types.PeerUser):
+                        send_as_raw = users[default_send_as.user_id]
+                    else:
+                        send_as_raw = chats[default_send_as.channel_id]
+
+                    parsed_chat.send_as_chat = Chat._parse_chat(client, send_as_raw)
+
+            if full_chat.pinned_msg_id:
+                parsed_chat.pinned_message = await client.get_messages(
+                    parsed_chat.id,
+                    message_ids=full_chat.pinned_msg_id
+                )
+
+            if isinstance(full_chat.exported_invite, raw.types.ChatInviteExported):
+                parsed_chat.invite_link = full_chat.exported_invite.link
+
+            parsed_chat.available_reactions = types.ChatReactions._parse(client, full_chat.available_reactions)
+
+        return parsed_chat
+
+    @staticmethod
+    def _parse_chat(client, chat: Union[raw.types.Chat, raw.types.User, raw.types.Channel]) -> "Chat":
+        if isinstance(chat, raw.types.Chat):
+            return Chat._parse_chat_chat(client, chat)
+        elif isinstance(chat, raw.types.User):
+            return Chat._parse_user_chat(client, chat)
+        else:
+            return Chat._parse_channel_chat(client, chat)
+
+    async def archive(self):
+        """Bound method *archive* of :obj:`~pyrogram.types.Chat`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.archive_chats(-100123456789)
+
+        Example:
+            .. code-block:: python
+
+                await chat.archive()
+
+        Returns:
+            True on success.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+
+        return await self._client.archive_chats(self.id)
+
+    async def unarchive(self):
+        """Bound method *unarchive* of :obj:`~pyrogram.types.Chat`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.unarchive_chats(-100123456789)
+
+        Example:
+            .. code-block:: python
+
+                await chat.unarchive()
+
+        Returns:
+            True on success.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+
+        return await self._client.unarchive_chats(self.id)
+
+    # TODO: Remove notes about "All Members Are Admins" for basic groups, the attribute doesn't exist anymore
+    async def set_title(self, title: str) -> bool:
+        """Bound method *set_title* of :obj:`~pyrogram.types.Chat`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.set_chat_title(
+                chat_id=chat_id,
+                title=title
+            )
+
+        Example:
+            .. code-block:: python
+
+                await chat.set_title("Lounge")
+
+        Note:
+            In regular groups (non-supergroups), this method will only work if the "All Members Are Admins"
+            setting is off.
+
+        Parameters:
+            title (``str``):
+                New chat title, 1-255 characters.
+
+        Returns:
+            ``bool``: True on success.
+
+        Raises:
+            RPCError: In case of Telegram RPC error.
+            ValueError: In case a chat_id belongs to user.
+        """
+
+        return await self._client.set_chat_title(
+            chat_id=self.id,
+            title=title
+        )
+
+    async def set_description(self, description: str) -> bool:
+        """Bound method *set_description* of :obj:`~pyrogram.types.Chat`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.set_chat_description(
+                chat_id=chat_id,
+                description=description
+            )
+
+        Example:
+            .. code-block:: python
+
+                await chat.set_chat_description("Don't spam!")
+
+        Parameters:
+            description (``str``):
+                New chat description, 0-255 characters.
+
+        Returns:
+            ``bool``: True on success.
+
+        Raises:
+            RPCError: In case of Telegram RPC error.
+            ValueError: If a chat_id doesn't belong to a supergroup or a channel.
+        """
+
+        return await self._client.set_chat_description(
+            chat_id=self.id,
+            description=description
+        )
+
+    async def set_photo(
+        self,
+        *,
+        photo: Union[str, BinaryIO] = None,
+        video: Union[str, BinaryIO] = None,
+        video_start_ts: float = None,
+    ) -> bool:
+        """Bound method *set_photo* of :obj:`~pyrogram.types.Chat`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.set_chat_photo(
+                chat_id=chat_id,
+                photo=photo
+            )
+
+        Example:
+            .. code-block:: python
+
+                # Set chat photo using a local file
+                await chat.set_photo(photo="photo.jpg")
+
+                # Set chat photo using an existing Photo file_id
+                await chat.set_photo(photo=photo.file_id)
+
+
+                # Set chat video using a local file
+                await chat.set_photo(video="video.mp4")
+
+                # Set chat photo using an existing Video file_id
+                await chat.set_photo(video=video.file_id)
+
+        Parameters:
+            photo (``str`` | ``BinaryIO``, *optional*):
+                New chat photo. You can pass a :obj:`~pyrogram.types.Photo` file_id, a file path to upload a new photo
+                from your local machine or a binary file-like object with its attribute
+                ".name" set for in-memory uploads.
+
+            video (``str`` | ``BinaryIO``, *optional*):
+                New chat video. You can pass a :obj:`~pyrogram.types.Video` file_id, a file path to upload a new video
+                from your local machine or a binary file-like object with its attribute
+                ".name" set for in-memory uploads.
+
+            video_start_ts (``float``, *optional*):
+                The timestamp in seconds of the video frame to use as photo profile preview.
+
+        Returns:
+            ``bool``: True on success.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+            ValueError: if a chat_id belongs to user.
+        """
+
+        return await self._client.set_chat_photo(
+            chat_id=self.id,
+            photo=photo,
+            video=video,
+            video_start_ts=video_start_ts
+        )
+
+    async def ban_member(
+        self,
+        user_id: Union[int, str],
+        until_date: datetime = utils.zero_datetime()
+    ) -> Union["types.Message", bool]:
+        """Bound method *ban_member* of :obj:`~pyrogram.types.Chat`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.ban_chat_member(
+                chat_id=chat_id,
+                user_id=user_id
+            )
+
+        Example:
+            .. code-block:: python
+
+                await chat.ban_member(123456789)
+
+        Note:
+            In regular groups (non-supergroups), this method will only work if the "All Members Are Admins" setting is
+            off in the target group. Otherwise members may only be removed by the group's creator or by the member
+            that added them.
+
+        Parameters:
+            user_id (``int`` | ``str``):
+                Unique identifier (int) or username (str) of the target user.
+                For a contact that exists in your Telegram address book you can use his phone number (str).
+
+            until_date (:py:obj:`~datetime.datetime`, *optional*):
+                Date when the user will be unbanned.
+                If user is banned for more than 366 days or less than 30 seconds from the current time they are
+                considered to be banned forever. Defaults to epoch (ban forever).
+
+        Returns:
+            :obj:`~pyrogram.types.Message` | ``bool``: On success, a service message will be returned (when applicable), otherwise, in
+            case a message object couldn't be returned, True is returned.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+
+        return await self._client.ban_chat_member(
+            chat_id=self.id,
+            user_id=user_id,
+            until_date=until_date
+        )
+
+    async def unban_member(
+        self,
+        user_id: Union[int, str]
+    ) -> bool:
+        """Bound method *unban_member* of :obj:`~pyrogram.types.Chat`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.unban_chat_member(
+                chat_id=chat_id,
+                user_id=user_id
+            )
+
+        Example:
+            .. code-block:: python
+
+                await chat.unban_member(123456789)
+
+        Parameters:
+            user_id (``int`` | ``str``):
+                Unique identifier (int) or username (str) of the target user.
+                For a contact that exists in your Telegram address book you can use his phone number (str).
+
+        Returns:
+            ``bool``: True on success.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+
+        return await self._client.unban_chat_member(
+            chat_id=self.id,
+            user_id=user_id,
+        )
+
+    async def restrict_member(
+        self,
+        user_id: Union[int, str],
+        permissions: "types.ChatPermissions",
+        until_date: datetime = utils.zero_datetime(),
+    ) -> "types.Chat":
+        """Bound method *unban_member* of :obj:`~pyrogram.types.Chat`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.restrict_chat_member(
+                chat_id=chat_id,
+                user_id=user_id,
+                permissions=ChatPermissions()
+            )
+
+        Example:
+            .. code-block:: python
+
+                await chat.restrict_member(user_id, ChatPermissions())
+
+        Parameters:
+            user_id (``int`` | ``str``):
+                Unique identifier (int) or username (str) of the target user.
+                For a contact that exists in your Telegram address book you can use his phone number (str).
+
+            permissions (:obj:`~pyrogram.types.ChatPermissions`):
+                New user permissions.
+
+            until_date (:py:obj:`~datetime.datetime`, *optional*):
+                Date when the user will be unbanned.
+                If user is banned for more than 366 days or less than 30 seconds from the current time they are
+                considered to be banned forever. Defaults to epoch (ban forever).
+
+        Returns:
+            :obj:`~pyrogram.types.Chat`: On success, a chat object is returned.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+
+        return await self._client.restrict_chat_member(
+            chat_id=self.id,
+            user_id=user_id,
+            permissions=permissions,
+            until_date=until_date,
+        )
+
+    # Set None as privileges default due to issues with partially initialized module, because at the time Chat
+    # is being initialized, ChatPrivileges would be required here, but was not initialized yet.
+    async def promote_member(
+        self,
+        user_id: Union[int, str],
+        privileges: "types.ChatPrivileges" = None
+    ) -> bool:
+        """Bound method *promote_member* of :obj:`~pyrogram.types.Chat`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.promote_chat_member(
+                chat_id=chat_id,
+                user_id=user_id
+            )
+
+        Example:
+
+            .. code-block:: python
+
+                await chat.promote_member(123456789)
+
+        Parameters:
+            user_id (``int`` | ``str``):
+                Unique identifier (int) or username (str) of the target user.
+                For a contact that exists in your Telegram address book you can use his phone number (str).
+
+            privileges (:obj:`~pyrogram.types.ChatPrivileges`, *optional*):
+                New user privileges.
+
+        Returns:
+            ``bool``: True on success.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+
+        return await self._client.promote_chat_member(
+            chat_id=self.id,
+            user_id=user_id,
+            privileges=privileges
+        )
+
+    async def join(self):
+        """Bound method *join* of :obj:`~pyrogram.types.Chat`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.join_chat(123456789)
+
+        Example:
+            .. code-block:: python
+
+                await chat.join()
+
+        Note:
+            This only works for public groups, channels that have set a username or linked chats.
+
+        Returns:
+            :obj:`~pyrogram.types.Chat`: On success, a chat object is returned.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+
+        return await self._client.join_chat(self.username or self.id)
+
+    async def leave(self):
+        """Bound method *leave* of :obj:`~pyrogram.types.Chat`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.leave_chat(123456789)
+
+        Example:
+            .. code-block:: python
+
+                await chat.leave()
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+
+        return await self._client.leave_chat(self.id)
+
+    async def export_invite_link(self):
+        """Bound method *export_invite_link* of :obj:`~pyrogram.types.Chat`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            client.export_chat_invite_link(123456789)
+
+        Example:
+            .. code-block:: python
+
+                chat.export_invite_link()
+
+        Returns:
+            ``str``: On success, the exported invite link is returned.
+
+        Raises:
+            ValueError: In case the chat_id belongs to a user.
+        """
+
+        return await self._client.export_chat_invite_link(self.id)
+
+    async def get_member(
+        self,
+        user_id: Union[int, str],
+    ) -> "types.ChatMember":
+        """Bound method *get_member* of :obj:`~pyrogram.types.Chat`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.get_chat_member(
+                chat_id=chat_id,
+                user_id=user_id
+            )
+
+        Example:
+            .. code-block:: python
+
+                await chat.get_member(user_id)
+
+        Returns:
+            :obj:`~pyrogram.types.ChatMember`: On success, a chat member is returned.
+        """
+
+        return await self._client.get_chat_member(
+            self.id,
+            user_id=user_id
+        )
+
+    def get_members(
+        self,
+        query: str = "",
+        limit: int = 0,
+        filter: "enums.ChatMembersFilter" = enums.ChatMembersFilter.SEARCH
+    ) -> Optional[AsyncGenerator["types.ChatMember", None]]:
+        """Bound method *get_members* of :obj:`~pyrogram.types.Chat`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            async for member in client.get_chat_members(chat_id):
+                print(member)
+
+        Example:
+            .. code-block:: python
+
+                async for member in chat.get_members():
+                    print(member)
+
+        Parameters:
+            query (``str``, *optional*):
+                Query string to filter members based on their display names and usernames.
+                Only applicable to supergroups and channels. Defaults to "" (empty string).
+                A query string is applicable only for :obj:`~pyrogram.enums.ChatMembersFilter.SEARCH`,
+                :obj:`~pyrogram.enums.ChatMembersFilter.BANNED` and :obj:`~pyrogram.enums.ChatMembersFilter.RESTRICTED`
+                filters only.
+
+            limit (``int``, *optional*):
+                Limits the number of members to be retrieved.
+
+            filter (:obj:`~pyrogram.enums.ChatMembersFilter`, *optional*):
+                Filter used to select the kind of members you want to retrieve. Only applicable for supergroups
+                and channels.
+
+        Returns:
+            ``Generator``: On success, a generator yielding :obj:`~pyrogram.types.ChatMember` objects is returned.
+        """
+
+        return self._client.get_chat_members(
+            self.id,
+            query=query,
+            limit=limit,
+            filter=filter
+        )
+
+    async def add_members(
+        self,
+        user_ids: Union[Union[int, str], List[Union[int, str]]],
+        forward_limit: int = 100
+    ) -> bool:
+        """Bound method *add_members* of :obj:`~pyrogram.types.Chat`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.add_chat_members(chat_id, user_id)
+
+        Example:
+            .. code-block:: python
+
+                await chat.add_members(user_id)
+
+        Returns:
+            ``bool``: On success, True is returned.
+        """
+
+        return await self._client.add_chat_members(
+            self.id,
+            user_ids=user_ids,
+            forward_limit=forward_limit
+        )
+
+    async def mark_unread(self, ) -> bool:
+        """Bound method *mark_unread* of :obj:`~pyrogram.types.Chat`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.mark_unread(chat_id)
+
+        Example:
+            .. code-block:: python
+
+                await chat.mark_unread()
+
+        Returns:
+            ``bool``: On success, True is returned.
+        """
+
+        return await self._client.mark_chat_unread(self.id)
+
+    async def set_protected_content(self, enabled: bool) -> bool:
+        """Bound method *set_protected_content* of :obj:`~pyrogram.types.Chat`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.set_chat_protected_content(chat_id, enabled)
+
+        Parameters:
+            enabled (``bool``):
+                Pass True to enable the protected content setting, False to disable.
+
+        Example:
+            .. code-block:: python
+
+                await chat.set_protected_content(enabled)
+
+        Returns:
+            ``bool``: On success, True is returned.
+        """
+
+        return await self._client.set_chat_protected_content(
+            self.id,
+            enabled=enabled
+        )
+
+    async def unpin_all_messages(self) -> bool:
+        """Bound method *unpin_all_messages* of :obj:`~pyrogram.types.Chat`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            client.unpin_all_chat_messages(chat_id)
+
+        Example:
+            .. code-block:: python
+
+                chat.unpin_all_messages()
+
+        Returns:
+            ``bool``: On success, True is returned.
+        """
+
+        return await self._client.unpin_all_chat_messages(self.id)
diff --git a/pyrogram/types/user_and_chats/chat_admin_with_invite_links.py b/pyrogram/types/user_and_chats/chat_admin_with_invite_links.py
new file mode 100644
index 00000000000..385a38da43f
--- /dev/null
+++ b/pyrogram/types/user_and_chats/chat_admin_with_invite_links.py
@@ -0,0 +1,63 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import Dict
+
+import pyrogram
+from pyrogram import raw
+from pyrogram import types
+from ..object import Object
+
+
+class ChatAdminWithInviteLinks(Object):
+    """Represents a chat administrator that has created invite links in a chat.
+
+    Parameters:
+        admin (:obj:`~pyrogram.types.User`):
+            The administrator.
+
+        chat_invite_links_count (``int``):
+            The number of valid chat invite links created by this administrator.
+
+        revoked_chat_invite_links_count (``int``):
+            The number of revoked chat invite links created by this administrator.
+    """
+
+    def __init__(
+        self, *,
+        admin: "types.User",
+        chat_invite_links_count: int,
+        revoked_chat_invite_links_count: int = None
+    ):
+        super().__init__()
+
+        self.admin = admin
+        self.chat_invite_links_count = chat_invite_links_count
+        self.revoked_chat_invite_links_count = revoked_chat_invite_links_count
+
+    @staticmethod
+    def _parse(
+        client: "pyrogram.Client",
+        admin: "raw.types.ChatAdminWithInvites",
+        users: Dict[int, "raw.types.User"] = None
+    ) -> "ChatAdminWithInviteLinks":
+        return ChatAdminWithInviteLinks(
+            admin=types.User._parse(client, users[admin.admin_id]),
+            chat_invite_links_count=admin.invites_count,
+            revoked_chat_invite_links_count=admin.revoked_invites_count
+        )
diff --git a/pyrogram/types/user_and_chats/chat_event.py b/pyrogram/types/user_and_chats/chat_event.py
new file mode 100644
index 00000000000..88ff42f3436
--- /dev/null
+++ b/pyrogram/types/user_and_chats/chat_event.py
@@ -0,0 +1,489 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 datetime import datetime
+from typing import List, Optional
+
+import pyrogram
+from pyrogram import raw
+from pyrogram import types, utils, enums
+from ..object import Object
+
+
+class ChatEvent(Object):
+    """A chat event from the recent actions log (also known as admin log).
+
+    See ``action`` to know which kind of event this is and the relative attributes to get the event content.
+
+    Parameters:
+        id (``int``):
+            Chat event identifier.
+
+        date (:py:obj:`~datetime.datetime`):
+            Date of the event.
+
+        action (:obj:`~pyrogram.enums.ChatEventAction`):
+            Event action.
+
+        user (:obj:`~pyrogram.types.User`):
+            User that triggered the event.
+
+        old_description, new_description (``str``, *optional*):
+            Previous and new chat description.
+            For :obj:`~pyrogram.enums.ChatEventAction.DESCRIPTION_CHANGED` action only.
+
+        old_history_ttl, new_history_ttl (``int``, *optional*):
+            Previous and new chat history TTL.
+            For :obj:`~pyrogram.enums.ChatEventAction.HISTORY_TTL_CHANGED` action only.
+
+        old_linked_chat, new_linked_chat (:obj:`~pyrogram.types.Chat`, *optional*):
+            Previous and new linked chat.
+            For :obj:`~pyrogram.enums.ChatEventAction.LINKED_CHAT_CHANGED` action only.
+
+        old_photo, new_photo (:obj:`~pyrogram.types.Photo`, *optional*):
+            Previous and new chat photo.
+            For :obj:`~pyrogram.enums.ChatEventAction.PHOTO_CHANGED` action only.
+
+        old_title, new_title (``str``, *optional*):
+            Previous and new chat title.
+            For :obj:`~pyrogram.enums.ChatEventAction.TITLE_CHANGED` action only.
+
+        old_username, new_username (``str``, *optional*):
+            Previous and new chat username.
+            For :obj:`~pyrogram.enums.ChatEventAction.USERNAME_CHANGED` action only.
+
+        old_chat_permissions, new_chat_permissions (:obj:`~pyrogram.types.ChatPermissions`, *optional*):
+            Previous and new default chat permissions.
+            For :obj:`~pyrogram.enums.ChatEventAction.CHAT_PERMISSIONS_CHANGED` action only.
+
+        deleted_message (:obj:`~pyrogram.types.Message`, *optional*):
+            Deleted message.
+            For :obj:`~pyrogram.enums.ChatEventAction.MESSAGE_DELETED` action only.
+
+        old_message, new_message (:obj:`~pyrogram.types.Message`, *optional*):
+            Previous and new message before it has been edited.
+            For :obj:`~pyrogram.enums.ChatEventAction.MESSAGE_EDITED` action only.
+
+        invited_member (:obj:`~pyrogram.types.ChatMember`, *optional*):
+            New invited chat member.
+            For :obj:`~pyrogram.enums.ChatEventAction.MEMBER_INVITED` action only.
+
+        old_administrator_privileges, new_administrator_privileges (:obj:`~pyrogram.types.ChatMember`, *optional*):
+            Previous and new administrator privileges.
+            For :obj:`~pyrogram.enums.ChatEventAction.ADMINISTRATOR_PRIVILEGES_CHANGED` action only.
+
+        old_member_permissions, new_member_permissions (:obj:`~pyrogram.types.ChatMember`, *optional*):
+            Previous and new member permissions.
+            For :obj:`~pyrogram.enums.ChatEventAction.MEMBER_PERMISSIONS_CHANGED` action only.
+
+        stopped_poll (:obj:`~pyrogram.types.Message`, *optional*):
+            Message containing the stopped poll.
+            For :obj:`~pyrogram.enums.ChatEventAction.POLL_STOPPED` action only.
+
+        invites_enabled (``bool``, *optional*):
+            If chat invites were enabled (True) or disabled (False).
+            For :obj:`~pyrogram.enums.ChatEventAction.INVITES_ENABLED` action only.
+
+        history_hidden (``bool``, *optional*):
+            If chat history has been hidden (True) or unhidden (False).
+            For :obj:`~pyrogram.enums.ChatEventAction.HISTORY_HIDDEN` action only.
+
+        signatures_enabled (``bool``, *optional*):
+            If message signatures were enabled (True) or disabled (False).
+            For :obj:`~pyrogram.enums.ChatEventAction.SIGNATURES_ENABLED` action only.
+
+        old_slow_mode, new_slow_mode (``int``, *optional*):
+            Previous and new slow mode value in seconds.
+            For :obj:`~pyrogram.enums.ChatEventAction.SLOW_MODE_CHANGED` action only.
+
+        pinned_message (:obj:`~pyrogram.types.Message`, *optional*):
+            Pinned message.
+            For :obj:`~pyrogram.enums.ChatEventAction.MESSAGE_PINNED` action only.
+
+        unpinned_message (:obj:`~pyrogram.types.Message`, *optional*):
+            Unpinned message.
+            For :obj:`~pyrogram.enums.ChatEventAction.MESSAGE_UNPINNED` action only.
+
+        old_invite_link, new_invite_link (:obj:`~pyrogram.types.ChatInviteLink`, *optional*):
+            Previous and new edited invite link.
+            For :obj:`~pyrogram.enums.ChatEventAction.INVITE_LINK_EDITED` action only.
+
+        revoked_invite_link (:obj:`~pyrogram.types.ChatInviteLink`, *optional*):
+            Revoked invite link.
+            For :obj:`~pyrogram.enums.ChatEventAction.INVITE_LINK_REVOKED` action only.
+
+        deleted_invite_link (:obj:`~pyrogram.types.ChatInviteLink`, *optional*):
+            Deleted invite link.
+            For :obj:`~pyrogram.enums.ChatEventAction.INVITE_LINK_DELETED` action only.
+    """
+
+    def __init__(
+        self, *,
+        id: int,
+        date: datetime,
+        user: "types.User",
+        action: str,
+
+        old_description: str = None,
+        new_description: str = None,
+
+        old_history_ttl: int = None,
+        new_history_ttl: int = None,
+
+        old_linked_chat: "types.Chat" = None,
+        new_linked_chat: "types.Chat" = None,
+
+        old_photo: "types.Photo" = None,
+        new_photo: "types.Photo" = None,
+
+        old_title: str = None,
+        new_title: str = None,
+
+        old_username: str = None,
+        new_username: str = None,
+
+        old_chat_permissions: "types.ChatPermissions" = None,
+        new_chat_permissions: "types.ChatPermissions" = None,
+
+        deleted_message: "types.Message" = None,
+
+        old_message: "types.Message" = None,
+        new_message: "types.Message" = None,
+
+        invited_member: "types.ChatMember" = None,
+
+        old_administrator_privileges: "types.ChatMember" = None,
+        new_administrator_privileges: "types.ChatMember" = None,
+
+        old_member_permissions: "types.ChatMember" = None,
+        new_member_permissions: "types.ChatMember" = None,
+
+        stopped_poll: "types.Message" = None,
+
+        invites_enabled: "types.ChatMember" = None,
+
+        history_hidden: bool = None,
+
+        signatures_enabled: bool = None,
+
+        old_slow_mode: int = None,
+        new_slow_mode: int = None,
+
+        pinned_message: "types.Message" = None,
+        unpinned_message: "types.Message" = None,
+
+        old_invite_link: "types.ChatInviteLink" = None,
+        new_invite_link: "types.ChatInviteLink" = None,
+        revoked_invite_link: "types.ChatInviteLink" = None,
+        deleted_invite_link: "types.ChatInviteLink" = None
+    ):
+        super().__init__()
+
+        self.id = id
+        self.date = date
+        self.action = action
+        self.user = user
+
+        self.old_description = old_description
+        self.new_description = new_description
+
+        self.old_history_ttl = old_history_ttl
+        self.new_history_ttl = new_history_ttl
+
+        self.old_linked_chat = old_linked_chat
+        self.new_linked_chat = new_linked_chat
+
+        self.old_photo = old_photo
+        self.new_photo = new_photo
+
+        self.old_title = old_title
+        self.new_title = new_title
+
+        self.old_username = old_username
+        self.new_username = new_username
+
+        self.old_chat_permissions = old_chat_permissions
+        self.new_chat_permissions = new_chat_permissions
+
+        self.deleted_message = deleted_message
+
+        self.old_message = old_message
+        self.new_message = new_message
+
+        self.invited_member = invited_member
+
+        self.old_administrator_privileges = old_administrator_privileges
+        self.new_administrator_privileges = new_administrator_privileges
+
+        self.old_member_permissions = old_member_permissions
+        self.new_member_permissions = new_member_permissions
+
+        self.stopped_poll = stopped_poll
+
+        self.invites_enabled = invites_enabled
+
+        self.history_hidden = history_hidden
+
+        self.signatures_enabled = signatures_enabled
+
+        self.old_slow_mode = old_slow_mode
+        self.new_slow_mode = new_slow_mode
+
+        self.pinned_message = pinned_message
+        self.unpinned_message = unpinned_message
+
+        self.old_invite_link = old_invite_link
+        self.new_invite_link = new_invite_link
+        self.revoked_invite_link = revoked_invite_link
+        self.deleted_invite_link = deleted_invite_link
+
+    @staticmethod
+    async def _parse(
+        client: "pyrogram.Client",
+        event: "raw.base.ChannelAdminLogEvent",
+        users: List["raw.base.User"],
+        chats: List["raw.base.Chat"]
+    ):
+        users = {i.id: i for i in users}
+        chats = {i.id: i for i in chats}
+
+        user = types.User._parse(client, users[event.user_id])
+        action = event.action
+
+        old_description: Optional[str] = None
+        new_description: Optional[str] = None
+
+        old_history_ttl: Optional[int] = None
+        new_history_ttl: Optional[int] = None
+
+        old_linked_chat: Optional[types.Chat] = None
+        new_linked_chat: Optional[types.Chat] = None
+
+        old_photo: Optional[types.Photo] = None
+        new_photo: Optional[types.Photo] = None
+
+        old_title: Optional[str] = None
+        new_title: Optional[str] = None
+
+        old_username: Optional[str] = None
+        new_username: Optional[str] = None
+
+        old_chat_permissions: Optional[types.ChatPermissions] = None
+        new_chat_permissions: Optional[types.ChatPermissions] = None
+
+        deleted_message: Optional[types.Message] = None
+
+        old_message: Optional[types.Message] = None
+        new_message: Optional[types.Message] = None
+
+        invited_member: Optional[types.ChatMember] = None
+
+        old_administrator_privileges: Optional[types.ChatMember] = None
+        new_administrator_privileges: Optional[types.ChatMember] = None
+
+        old_member_permissions: Optional[types.ChatMember] = None
+        new_member_permissions: Optional[types.ChatMember] = None
+
+        stopped_poll: Optional[types.Message] = None
+
+        invites_enabled: Optional[bool] = None
+
+        history_hidden: Optional[bool] = None
+
+        signatures_enabled: Optional[bool] = None
+
+        old_slow_mode: Optional[int] = None
+        new_slow_mode: Optional[int] = None
+
+        pinned_message: Optional[types.Message] = None
+        unpinned_message: Optional[types.Message] = None
+
+        old_invite_link: Optional[types.ChatInviteLink] = None
+        new_invite_link: Optional[types.ChatInviteLink] = None
+        revoked_invite_link: Optional[types.ChatInviteLink] = None
+        deleted_invite_link: Optional[types.ChatInviteLink] = None
+
+        if isinstance(action, raw.types.ChannelAdminLogEventActionChangeAbout):
+            old_description = action.prev_value
+            new_description = action.new_value
+            action = enums.ChatEventAction.DESCRIPTION_CHANGED
+
+        elif isinstance(action, raw.types.ChannelAdminLogEventActionChangeHistoryTTL):
+            old_history_ttl = action.prev_value
+            new_history_ttl = action.new_value
+            action = enums.ChatEventAction.HISTORY_TTL_CHANGED
+
+        elif isinstance(action, raw.types.ChannelAdminLogEventActionChangeLinkedChat):
+            old_linked_chat = types.Chat._parse_chat(client, chats[action.prev_value])
+            new_linked_chat = types.Chat._parse_chat(client, chats[action.new_value])
+            action = enums.ChatEventAction.LINKED_CHAT_CHANGED
+
+        elif isinstance(action, raw.types.ChannelAdminLogEventActionChangePhoto):
+            old_photo = types.Photo._parse(client, action.prev_photo)
+            new_photo = types.Photo._parse(client, action.new_photo)
+            action = enums.ChatEventAction.PHOTO_CHANGED
+
+        elif isinstance(action, raw.types.ChannelAdminLogEventActionChangeTitle):
+            old_title = action.prev_value
+            new_title = action.new_value
+            action = enums.ChatEventAction.TITLE_CHANGED
+
+        elif isinstance(action, raw.types.ChannelAdminLogEventActionChangeUsername):
+            old_username = action.prev_value
+            new_username = action.new_value
+            action = enums.ChatEventAction.USERNAME_CHANGED
+
+        elif isinstance(action, raw.types.ChannelAdminLogEventActionDefaultBannedRights):
+            old_chat_permissions = types.ChatPermissions._parse(action.prev_banned_rights)
+            new_chat_permissions = types.ChatPermissions._parse(action.new_banned_rights)
+            action = enums.ChatEventAction.CHAT_PERMISSIONS_CHANGED
+
+        elif isinstance(action, raw.types.ChannelAdminLogEventActionDeleteMessage):
+            deleted_message = await types.Message._parse(client, action.message, users, chats)
+            action = enums.ChatEventAction.MESSAGE_DELETED
+
+        elif isinstance(action, raw.types.ChannelAdminLogEventActionEditMessage):
+            old_message = await types.Message._parse(client, action.prev_message, users, chats)
+            new_message = await types.Message._parse(client, action.new_message, users, chats)
+            action = enums.ChatEventAction.MESSAGE_EDITED
+
+        elif isinstance(action, raw.types.ChannelAdminLogEventActionParticipantInvite):
+            invited_member = types.ChatMember._parse(client, action.participant, users, chats)
+            action = enums.ChatEventAction.MEMBER_INVITED
+
+        elif isinstance(action, raw.types.ChannelAdminLogEventActionParticipantToggleAdmin):
+            old_administrator_privileges = types.ChatMember._parse(client, action.prev_participant, users, chats)
+            new_administrator_privileges = types.ChatMember._parse(client, action.new_participant, users, chats)
+            action = enums.ChatEventAction.ADMINISTRATOR_PRIVILEGES_CHANGED
+
+        elif isinstance(action, raw.types.ChannelAdminLogEventActionParticipantToggleBan):
+            old_member_permissions = types.ChatMember._parse(client, action.prev_participant, users, chats)
+            new_member_permissions = types.ChatMember._parse(client, action.new_participant, users, chats)
+            action = enums.ChatEventAction.MEMBER_PERMISSIONS_CHANGED
+
+        elif isinstance(action, raw.types.ChannelAdminLogEventActionStopPoll):
+            stopped_poll = await types.Message._parse(client, action.message, users, chats)
+            action = enums.ChatEventAction.POLL_STOPPED
+
+        elif isinstance(action, raw.types.ChannelAdminLogEventActionParticipantJoin):
+            action = enums.ChatEventAction.MEMBER_JOINED
+
+        elif isinstance(action, raw.types.ChannelAdminLogEventActionParticipantLeave):
+            action = enums.ChatEventAction.MEMBER_LEFT
+
+        elif isinstance(action, raw.types.ChannelAdminLogEventActionToggleInvites):
+            invites_enabled = action.new_value
+            action = enums.ChatEventAction.INVITES_ENABLED
+
+        elif isinstance(action, raw.types.ChannelAdminLogEventActionTogglePreHistoryHidden):
+            history_hidden = action.new_value
+            action = enums.ChatEventAction.HISTORY_HIDDEN
+
+        elif isinstance(action, raw.types.ChannelAdminLogEventActionToggleSignatures):
+            signatures_enabled = action.new_value
+            action = enums.ChatEventAction.SIGNATURES_ENABLED
+
+        elif isinstance(action, raw.types.ChannelAdminLogEventActionToggleSlowMode):
+            old_slow_mode = action.prev_value
+            new_slow_mode = action.new_value
+            action = enums.ChatEventAction.SLOW_MODE_CHANGED
+
+        elif isinstance(action, raw.types.ChannelAdminLogEventActionUpdatePinned):
+            message = action.message
+
+            if message.pinned:
+                pinned_message = await types.Message._parse(client, message, users, chats)
+                action = enums.ChatEventAction.MESSAGE_PINNED
+            else:
+                unpinned_message = await types.Message._parse(client, message, users, chats)
+                action = enums.ChatEventAction.MESSAGE_UNPINNED
+
+        elif isinstance(action, raw.types.ChannelAdminLogEventActionExportedInviteEdit):
+            old_invite_link = types.ChatInviteLink._parse(client, action.prev_invite, users)
+            new_invite_link = types.ChatInviteLink._parse(client, action.new_invite, users)
+            action = enums.ChatEventAction.INVITE_LINK_EDITED
+
+        elif isinstance(action, raw.types.ChannelAdminLogEventActionExportedInviteRevoke):
+            revoked_invite_link = types.ChatInviteLink._parse(client, action.invite, users)
+            action = enums.ChatEventAction.INVITE_LINK_REVOKED
+
+        elif isinstance(action, raw.types.ChannelAdminLogEventActionExportedInviteDelete):
+            deleted_invite_link = types.ChatInviteLink._parse(client, action.invite, users)
+            action = enums.ChatEventAction.INVITE_LINK_DELETED
+
+        else:
+            action = f"{enums.ChatEventAction.UNKNOWN}-{action.QUALNAME}"
+
+        return ChatEvent(
+            id=event.id,
+            date=utils.timestamp_to_datetime(event.date),
+            user=user,
+            action=action,
+            old_description=old_description,
+            new_description=new_description,
+
+            old_history_ttl=old_history_ttl,
+            new_history_ttl=new_history_ttl,
+
+            old_linked_chat=old_linked_chat,
+            new_linked_chat=new_linked_chat,
+
+            old_photo=old_photo,
+            new_photo=new_photo,
+
+            old_title=old_title,
+            new_title=new_title,
+
+            old_username=old_username,
+            new_username=new_username,
+
+            old_chat_permissions=old_chat_permissions,
+            new_chat_permissions=new_chat_permissions,
+
+            deleted_message=deleted_message,
+
+            old_message=old_message,
+            new_message=new_message,
+
+            invited_member=invited_member,
+
+            old_administrator_privileges=old_administrator_privileges,
+            new_administrator_privileges=new_administrator_privileges,
+
+            old_member_permissions=old_member_permissions,
+            new_member_permissions=new_member_permissions,
+
+            stopped_poll=stopped_poll,
+
+            invites_enabled=invites_enabled,
+
+            history_hidden=history_hidden,
+
+            signatures_enabled=signatures_enabled,
+
+            old_slow_mode=old_slow_mode,
+            new_slow_mode=new_slow_mode,
+
+            pinned_message=pinned_message,
+            unpinned_message=unpinned_message,
+
+            old_invite_link=old_invite_link,
+            new_invite_link=new_invite_link,
+            revoked_invite_link=revoked_invite_link,
+            deleted_invite_link=deleted_invite_link
+        )
diff --git a/pyrogram/types/user_and_chats/chat_event_filter.py b/pyrogram/types/user_and_chats/chat_event_filter.py
new file mode 100644
index 00000000000..92298ea3bea
--- /dev/null
+++ b/pyrogram/types/user_and_chats/chat_event_filter.py
@@ -0,0 +1,175 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 pyrogram import raw
+from ..object import Object
+
+
+class ChatEventFilter(Object):
+    """Set of filters used to obtain a chat event log.
+
+    Parameters:
+        new_restrictions (``bool``, *optional*):
+            True, if member restricted/unrestricted/banned/unbanned events should be returned.
+            Defaults to False.
+
+        new_privileges (``bool``, *optional*):
+            True, if member promotion/demotion events should be returned.
+            Defaults to False.
+
+        new_members (``bool``, *optional*):
+            True, if members joining events should be returned.
+            Defaults to False.
+
+        chat_info (``bool``, *optional*):
+            True, if chat info changes should be returned. That is, when description, linked chat, location, photo,
+            sticker set, title or username have been modified.
+            Defaults to False.
+
+        chat_settings (``bool``, *optional*):
+            True, if chat settings changes should be returned. That is, when invites, hidden history, message
+            signatures, default chat permissions have been modified.
+            Defaults to False.
+
+        invite_links (``bool``, *optional*):
+            True, if invite links events (edit, revoke, delete) should be returned.
+            Defaults to False.
+
+        deleted_messages (``bool``, *optional*):
+            True, if deleted messages events should be returned.
+            Defaults to False.
+
+        edited_messages (``bool``, *optional*):
+            True, if edited messages events, including closed polls, should be returned.
+            Defaults to False.
+
+        pinned_messages (``bool``, *optional*):
+            True, if pinned/unpinned messages events should be returned.
+            Defaults to False.
+
+        leaving_members (``bool``, *optional*):
+            True, if members leaving events should be returned.
+            Defaults to False.
+
+        video_chats (``bool``, *optional*):
+            True, if video chats events should be returned.
+            Defaults to False.
+    """
+
+    def __init__(
+        self, *,
+        new_restrictions: bool = False,
+        new_privileges: bool = False,
+        new_members: bool = False,
+        chat_info: bool = False,
+        chat_settings: bool = False,
+        invite_links: bool = False,
+        deleted_messages: bool = False,
+        edited_messages: bool = False,
+        pinned_messages: bool = False,
+        leaving_members: bool = False,
+        video_chats: bool = False
+    ):
+        super().__init__()
+
+        self.new_restrictions = new_restrictions
+        self.new_privileges = new_privileges
+        self.new_members = new_members
+        self.chat_info = chat_info
+        self.chat_settings = chat_settings
+        self.invite_links = invite_links
+        self.deleted_messages = deleted_messages
+        self.edited_messages = edited_messages
+        self.pinned_messages = pinned_messages
+        self.leaving_members = leaving_members
+        self.video_chats = video_chats
+
+    def write(self) -> "raw.base.ChannelAdminLogEventsFilter":
+        join = False
+        leave = False
+        invite = False
+        ban = False
+        unban = False
+        kick = False
+        unkick = False
+        promote = False
+        demote = False
+        info = False
+        settings = False
+        pinned = False
+        edit = False
+        delete = False
+        group_call = False
+        invites = False
+
+        if self.new_restrictions:
+            ban = True
+            unban = True
+            kick = True
+            unkick = True
+
+        if self.new_privileges:
+            promote = True
+            demote = True
+
+        if self.new_members:
+            join = True
+            invite = True
+
+        if self.chat_info:
+            info = True
+
+        if self.chat_settings:
+            settings = True
+
+        if self.invite_links:
+            invites = True
+
+        if self.deleted_messages:
+            delete = True
+
+        if self.edited_messages:
+            edit = True
+
+        if self.pinned_messages:
+            pinned = True
+
+        if self.leaving_members:
+            leave = True
+
+        if self.video_chats:
+            group_call = True
+
+        return raw.types.ChannelAdminLogEventsFilter(
+            join=join,
+            leave=leave,
+            invite=invite,
+            ban=ban,
+            unban=unban,
+            kick=kick,
+            unkick=unkick,
+            promote=promote,
+            demote=demote,
+            info=info,
+            settings=settings,
+            pinned=pinned,
+            edit=edit,
+            delete=delete,
+            group_call=group_call,
+            invites=invites
+        )
diff --git a/pyrogram/types/user_and_chats/chat_invite_link.py b/pyrogram/types/user_and_chats/chat_invite_link.py
new file mode 100644
index 00000000000..59f6315ba53
--- /dev/null
+++ b/pyrogram/types/user_and_chats/chat_invite_link.py
@@ -0,0 +1,130 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 datetime import datetime
+from typing import Dict
+from typing import Optional
+
+import pyrogram
+from pyrogram import raw, utils
+from pyrogram import types
+from ..object import Object
+
+
+class ChatInviteLink(Object):
+    """An invite link for a chat.
+
+    Parameters:
+        invite_link (``str``):
+            The invite link. If the link was created by another chat administrator, then the second part of the
+            link will be replaced with "...".
+
+        date (:py:obj:`~datetime.datetime`):
+            The date when the link was created.
+
+        is_primary (``bool``):
+            True, if the link is primary.
+
+        is_revoked (``bool``):
+            True, if the link is revoked.
+
+        creator (:obj:`~pyrogram.types.User`, *optional*):
+            Creator of the link.
+
+        name (``str``, *optional*):
+            Invite link name
+
+        creates_join_request (``bool``, *optional*):
+            True, if users joining the chat via the link need to be approved by chat administrators.
+
+        start_date (:py:obj:`~datetime.datetime`, *optional*):
+            Point in time when the link has been edited.
+
+        expire_date (:py:obj:`~datetime.datetime`, *optional*):
+            Point in time when the link will expire or has been expired.
+
+        member_limit (``int``, *optional*):
+            Maximum number of users that can be members of the chat simultaneously after joining the chat via this
+            invite link; 1-99999.
+
+        member_count (``int``, *optional*):
+            Number of users that joined via this link and are currently member of the chat.
+
+        pending_join_request_count (``int``, *optional*):
+            Number of pending join requests created using this link
+    """
+
+    def __init__(
+        self, *,
+        invite_link: str,
+        date: datetime,
+        is_primary: bool = None,
+        is_revoked: bool = None,
+        creator: "types.User" = None,
+        name: str = None,
+        creates_join_request: bool = None,
+        start_date: datetime = None,
+        expire_date: datetime = None,
+        member_limit: int = None,
+        member_count: int = None,
+        pending_join_request_count: int = None
+    ):
+        super().__init__()
+
+        self.invite_link = invite_link
+        self.date = date
+        self.is_primary = is_primary
+        self.is_revoked = is_revoked
+        self.creator = creator
+        self.name = name
+        self.creates_join_request = creates_join_request
+        self.start_date = start_date
+        self.expire_date = expire_date
+        self.member_limit = member_limit
+        self.member_count = member_count
+        self.pending_join_request_count = pending_join_request_count
+
+    @staticmethod
+    def _parse(
+        client: "pyrogram.Client",
+        invite: "raw.base.ExportedChatInvite",
+        users: Dict[int, "raw.types.User"] = None
+    ) -> Optional["ChatInviteLink"]:
+        if not isinstance(invite, raw.types.ChatInviteExported):
+            return None
+
+        creator = (
+            types.User._parse(client, users[invite.admin_id])
+            if users is not None
+            else None
+        )
+
+        return ChatInviteLink(
+            invite_link=invite.link,
+            date=utils.timestamp_to_datetime(invite.date),
+            is_primary=invite.permanent,
+            is_revoked=invite.revoked,
+            creator=creator,
+            name=invite.title,
+            creates_join_request=invite.request_needed,
+            start_date=utils.timestamp_to_datetime(invite.start_date),
+            expire_date=utils.timestamp_to_datetime(invite.expire_date),
+            member_limit=invite.usage_limit,
+            member_count=invite.usage,
+            pending_join_request_count=invite.requested
+        )
diff --git a/pyrogram/types/user_and_chats/chat_join_request.py b/pyrogram/types/user_and_chats/chat_join_request.py
new file mode 100644
index 00000000000..b810640b76c
--- /dev/null
+++ b/pyrogram/types/user_and_chats/chat_join_request.py
@@ -0,0 +1,139 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 datetime import datetime
+from typing import Dict
+
+import pyrogram
+from pyrogram import raw, utils
+from pyrogram import types
+from ..object import Object
+from ..update import Update
+
+
+class ChatJoinRequest(Object, Update):
+    """Represents a join request sent to a chat.
+
+    Parameters:
+        chat (:obj:`~pyrogram.types.Chat`):
+            Chat to which the request was sent.
+
+        from_user (:obj:`~pyrogram.types.User`):
+            User that sent the join request.
+
+        date (:py:obj:`~datetime.datetime`):
+            Date the request was sent.
+
+        bio (``str``, *optional*):
+            Bio of the user.
+
+        invite_link (:obj:`~pyrogram.types.ChatInviteLink`, *optional*):
+            Chat invite link that was used by the user to send the join request.
+    """
+
+    def __init__(
+        self,
+        *,
+        client: "pyrogram.Client" = None,
+        chat: "types.Chat",
+        from_user: "types.User",
+        date: datetime,
+        bio: str = None,
+        invite_link: "types.ChatInviteLink" = None
+    ):
+        super().__init__(client)
+
+        self.chat = chat
+        self.from_user = from_user
+        self.date = date
+        self.bio = bio
+        self.invite_link = invite_link
+
+    @staticmethod
+    def _parse(
+        client: "pyrogram.Client",
+        update: "raw.types.UpdateBotChatInviteRequester",
+        users: Dict[int, "raw.types.User"],
+        chats: Dict[int, "raw.types.Chat"]
+    ) -> "ChatJoinRequest":
+        chat_id = utils.get_raw_peer_id(update.peer)
+
+        return ChatJoinRequest(
+            chat=types.Chat._parse_chat(client, chats[chat_id]),
+            from_user=types.User._parse(client, users[update.user_id]),
+            date=utils.timestamp_to_datetime(update.date),
+            bio=update.about,
+            invite_link=types.ChatInviteLink._parse(client, update.invite, users),
+            client=client
+        )
+
+    async def approve(self) -> bool:
+        """Bound method *approve* of :obj:`~pyrogram.types.ChatJoinRequest`.
+        
+        Use as a shortcut for:
+        
+        .. code-block:: python
+
+            await client.approve_chat_join_request(
+                chat_id=request.chat.id,
+                user_id=request.from_user.id
+            )
+            
+        Example:
+            .. code-block:: python
+
+                await request.approve()
+                
+        Returns:
+            ``bool``: True on success.
+        
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+        return await self._client.approve_chat_join_request(
+            chat_id=self.chat.id,
+            user_id=self.from_user.id
+        )
+
+    async def decline(self) -> bool:
+        """Bound method *decline* of :obj:`~pyrogram.types.ChatJoinRequest`.
+        
+        Use as a shortcut for:
+        
+        .. code-block:: python
+
+            await client.decline_chat_join_request(
+                chat_id=request.chat.id,
+                user_id=request.from_user.id
+            )
+            
+        Example:
+            .. code-block:: python
+
+                await request.decline()
+                
+        Returns:
+            ``bool``: True on success.
+        
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+        return await self._client.decline_chat_join_request(
+            chat_id=self.chat.id,
+            user_id=self.from_user.id
+        )
diff --git a/pyrogram/types/user_and_chats/chat_joiner.py b/pyrogram/types/user_and_chats/chat_joiner.py
new file mode 100644
index 00000000000..024f88ea268
--- /dev/null
+++ b/pyrogram/types/user_and_chats/chat_joiner.py
@@ -0,0 +1,82 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 datetime import datetime
+from typing import Dict
+
+import pyrogram
+from pyrogram import raw, types, utils
+from ..object import Object
+
+
+class ChatJoiner(Object):
+    """Contains information about a joiner member of a chat.
+
+    Parameters:
+        user (:obj:`~pyrogram.types.User`):
+            Information about the user.
+
+        date (:py:obj:`~datetime.datetime`):
+            Date when the user joined.
+
+        bio (``str``, *optional*):
+            Bio of the user.
+
+        pending (``bool``, *optional*):
+            True in case the chat joiner has a pending request.
+
+        approved_by (:obj:`~pyrogram.types.User`, *optional*):
+            Administrator who approved this chat joiner.
+    """
+
+    def __init__(
+        self,
+        *,
+        client: "pyrogram.Client",
+        user: "types.User",
+        date: datetime = None,
+        bio: str = None,
+        pending: bool = None,
+        approved_by: "types.User" = None,
+    ):
+        super().__init__(client)
+
+        self.user = user
+        self.date = date
+        self.bio = bio
+        self.pending = pending
+        self.approved_by = approved_by
+
+    @staticmethod
+    def _parse(
+        client: "pyrogram.Client",
+        joiner: "raw.base.ChatInviteImporter",
+        users: Dict[int, "raw.base.User"],
+    ) -> "ChatJoiner":
+        return ChatJoiner(
+            user=types.User._parse(client, users[joiner.user_id]),
+            date=utils.timestamp_to_datetime(joiner.date),
+            pending=joiner.requested,
+            bio=joiner.about,
+            approved_by=(
+                types.User._parse(client, users[joiner.approved_by])
+                if joiner.approved_by
+                else None
+            ),
+            client=client
+        )
diff --git a/pyrogram/types/user_and_chats/chat_member.py b/pyrogram/types/user_and_chats/chat_member.py
new file mode 100644
index 00000000000..4459a8d1c03
--- /dev/null
+++ b/pyrogram/types/user_and_chats/chat_member.py
@@ -0,0 +1,227 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 datetime import datetime
+from typing import Union, Dict
+
+import pyrogram
+from pyrogram import raw, types, utils, enums
+from ..object import Object
+
+
+class ChatMember(Object):
+    """Contains information about one member of a chat.
+
+    Parameters:
+        status (:obj:`~pyrogram.enums.ChatMemberStatus`):
+            The member's status in the chat.
+
+        user (:obj:`~pyrogram.types.User`, *optional*):
+            Information about the user.
+
+        chat (:obj:`~pyrogram.types.Chat`, *optional*):
+            Information about the chat (useful in case of banned channel senders).
+
+        joined_date (:py:obj:`~datetime.datetime`, *optional*):
+            Date when the user joined.
+            Not available for the owner.
+
+        custom_title (``str``, *optional*):
+            A custom title that will be shown to all members instead of "Owner" or "Admin".
+            Creator (owner) and administrators only. Can be None in case there's no custom title set.
+
+        until_date (:py:obj:`~datetime.datetime`, *optional*):
+            Restricted and banned only.
+            Date when restrictions will be lifted for this user.
+
+        invited_by (:obj:`~pyrogram.types.User`, *optional*):
+            Administrators and self member only. Information about the user who invited this member.
+            In case the user joined by himself this will be the same as "user".
+
+        promoted_by (:obj:`~pyrogram.types.User`, *optional*):
+            Administrators only. Information about the user who promoted this member as administrator.
+
+        restricted_by (:obj:`~pyrogram.types.User`, *optional*):
+            Restricted and banned only. Information about the user who restricted or banned this member.
+
+        is_member (``bool``, *optional*):
+            Restricted only. True, if the user is a member of the chat at the moment of the request.
+
+        can_be_edited (``bool``, *optional*):
+            True, if the you are allowed to edit administrator privileges of the user.
+
+        permissions (:obj:`~pyrogram.types.ChatPermissions`, *optional*):
+            Restricted only. Restricted actions that a non-administrator user is allowed to take.
+
+        privileges (:obj:`~pyrogram.types.ChatPrivileges`, *optional*):
+            Administrators only. Privileged actions that an administrator is able to take.
+    """
+
+    def __init__(
+        self,
+        *,
+        client: "pyrogram.Client" = None,
+        status: "enums.ChatMemberStatus",
+        user: "types.User" = None,
+        chat: "types.Chat" = None,
+        custom_title: str = None,
+        until_date: datetime = None,
+        joined_date: datetime = None,
+        invited_by: "types.User" = None,
+        promoted_by: "types.User" = None,
+        restricted_by: "types.User" = None,
+        is_member: bool = None,
+        can_be_edited: bool = None,
+        permissions: "types.ChatPermissions" = None,
+        privileges: "types.ChatPrivileges" = None
+    ):
+        super().__init__(client)
+
+        self.status = status
+        self.user = user
+        self.chat = chat
+        self.custom_title = custom_title
+        self.until_date = until_date
+        self.joined_date = joined_date
+        self.invited_by = invited_by
+        self.promoted_by = promoted_by
+        self.restricted_by = restricted_by
+        self.is_member = is_member
+        self.can_be_edited = can_be_edited
+        self.permissions = permissions
+        self.privileges = privileges
+
+    @staticmethod
+    def _parse(
+        client: "pyrogram.Client",
+        member: Union["raw.base.ChatParticipant", "raw.base.ChannelParticipant"],
+        users: Dict[int, "raw.base.User"],
+        chats: Dict[int, "raw.base.Chat"]
+    ) -> "ChatMember":
+        # Chat participants
+        if isinstance(member, raw.types.ChatParticipant):
+            return ChatMember(
+                status=enums.ChatMemberStatus.MEMBER,
+                user=types.User._parse(client, users[member.user_id]),
+                joined_date=utils.timestamp_to_datetime(member.date),
+                invited_by=types.User._parse(client, users[member.inviter_id]),
+                client=client
+            )
+        elif isinstance(member, raw.types.ChatParticipantAdmin):
+            return ChatMember(
+                status=enums.ChatMemberStatus.ADMINISTRATOR,
+                user=types.User._parse(client, users[member.user_id]),
+                joined_date=utils.timestamp_to_datetime(member.date),
+                invited_by=types.User._parse(client, users[member.inviter_id]),
+                client=client
+            )
+        elif isinstance(member, raw.types.ChatParticipantCreator):
+            return ChatMember(
+                status=enums.ChatMemberStatus.OWNER,
+                user=types.User._parse(client, users[member.user_id]),
+                client=client
+            )
+
+        # Channel participants
+        if isinstance(member, raw.types.ChannelParticipant):
+            return ChatMember(
+                status=enums.ChatMemberStatus.MEMBER,
+                user=types.User._parse(client, users[member.user_id]),
+                joined_date=utils.timestamp_to_datetime(member.date),
+                client=client
+            )
+        elif isinstance(member, raw.types.ChannelParticipantAdmin):
+            return ChatMember(
+                status=enums.ChatMemberStatus.ADMINISTRATOR,
+                user=types.User._parse(client, users[member.user_id]),
+                joined_date=utils.timestamp_to_datetime(member.date),
+                promoted_by=types.User._parse(client, users[member.promoted_by]),
+                invited_by=(
+                    types.User._parse(client, users[member.inviter_id])
+                    if member.inviter_id else None
+                ),
+                custom_title=member.rank,
+                can_be_edited=member.can_edit,
+                privileges=types.ChatPrivileges._parse(member.admin_rights),
+                client=client
+            )
+        elif isinstance(member, raw.types.ChannelParticipantBanned):
+            peer = member.peer
+            peer_id = utils.get_raw_peer_id(peer)
+
+            user = (
+                types.User._parse(client, users[peer_id])
+                if isinstance(peer, raw.types.PeerUser) else None
+            )
+
+            chat = (
+                types.Chat._parse_chat(client, chats[peer_id])
+                if not isinstance(peer, raw.types.PeerUser) else None
+            )
+
+            return ChatMember(
+                status=(
+                    enums.ChatMemberStatus.BANNED
+                    if member.banned_rights.view_messages
+                    else enums.ChatMemberStatus.RESTRICTED
+                ),
+                user=user,
+                chat=chat,
+                until_date=utils.timestamp_to_datetime(member.banned_rights.until_date),
+                joined_date=utils.timestamp_to_datetime(member.date),
+                is_member=not member.left,
+                restricted_by=types.User._parse(client, users[member.kicked_by]),
+                permissions=types.ChatPermissions._parse(member.banned_rights),
+                client=client
+            )
+        elif isinstance(member, raw.types.ChannelParticipantCreator):
+            return ChatMember(
+                status=enums.ChatMemberStatus.OWNER,
+                user=types.User._parse(client, users[member.user_id]),
+                custom_title=member.rank,
+                privileges=types.ChatPrivileges._parse(member.admin_rights),
+                client=client
+            )
+        elif isinstance(member, raw.types.ChannelParticipantLeft):
+            peer = member.peer
+            peer_id = utils.get_raw_peer_id(peer)
+
+            user = (
+                types.User._parse(client, users[peer_id])
+                if isinstance(peer, raw.types.PeerUser) else None
+            )
+
+            chat = (
+                types.Chat._parse_chat(client, chats[peer_id])
+                if not isinstance(peer, raw.types.PeerUser) else None
+            )
+
+            return ChatMember(
+                status=enums.ChatMemberStatus.LEFT,
+                user=user,
+                chat=chat,
+                client=client
+            )
+        elif isinstance(member, raw.types.ChannelParticipantSelf):
+            return ChatMember(
+                status=enums.ChatMemberStatus.MEMBER,
+                user=types.User._parse(client, users[member.user_id]),
+                joined_date=utils.timestamp_to_datetime(member.date),
+                invited_by=types.User._parse(client, users[member.inviter_id]),
+                client=client
+            )
diff --git a/pyrogram/types/user_and_chats/chat_member_updated.py b/pyrogram/types/user_and_chats/chat_member_updated.py
new file mode 100644
index 00000000000..f8b6638db99
--- /dev/null
+++ b/pyrogram/types/user_and_chats/chat_member_updated.py
@@ -0,0 +1,102 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 datetime import datetime
+from typing import Dict, Union
+
+import pyrogram
+from pyrogram import raw, utils
+from pyrogram import types
+from ..object import Object
+from ..update import Update
+
+
+class ChatMemberUpdated(Object, Update):
+    """Represents changes in the status of a chat member.
+
+    Parameters:
+        chat (:obj:`~pyrogram.types.Chat`):
+            Chat the user belongs to.
+
+        from_user (:obj:`~pyrogram.types.User`):
+            Performer of the action, which resulted in the change.
+
+        date (:py:obj:`~datetime.datetime`):
+            Date the change was done.
+
+        old_chat_member (:obj:`~pyrogram.types.ChatMember`, *optional*):
+            Previous information about the chat member.
+
+        new_chat_member (:obj:`~pyrogram.types.ChatMember`, *optional*):
+            New information about the chat member.
+
+        invite_link (:obj:`~pyrogram.types.ChatInviteLink`, *optional*):
+            Chat invite link, which was used by the user to join the chat; for joining by invite link events only.
+    """
+
+    def __init__(
+        self,
+        *,
+        client: "pyrogram.Client" = None,
+        chat: "types.Chat",
+        from_user: "types.User",
+        date: datetime,
+        old_chat_member: "types.ChatMember",
+        new_chat_member: "types.ChatMember",
+        invite_link: "types.ChatInviteLink" = None,
+    ):
+        super().__init__(client)
+
+        self.chat = chat
+        self.from_user = from_user
+        self.date = date
+        self.old_chat_member = old_chat_member
+        self.new_chat_member = new_chat_member
+        self.invite_link = invite_link
+
+    @staticmethod
+    def _parse(
+        client: "pyrogram.Client",
+        update: Union["raw.types.UpdateChatParticipant", "raw.types.UpdateChannelParticipant"],
+        users: Dict[int, "raw.types.User"],
+        chats: Dict[int, "raw.types.Chat"]
+    ) -> "ChatMemberUpdated":
+        chat_id = getattr(update, "chat_id", None) or getattr(update, "channel_id")
+
+        old_chat_member = None
+        new_chat_member = None
+        invite_link = None
+
+        if update.prev_participant:
+            old_chat_member = types.ChatMember._parse(client, update.prev_participant, users, chats)
+
+        if update.new_participant:
+            new_chat_member = types.ChatMember._parse(client, update.new_participant, users, chats)
+
+        if update.invite:
+            invite_link = types.ChatInviteLink._parse(client, update.invite, users)
+
+        return ChatMemberUpdated(
+            chat=types.Chat._parse_chat(client, chats[chat_id]),
+            from_user=types.User._parse(client, users[update.actor_id]),
+            date=utils.timestamp_to_datetime(update.date),
+            old_chat_member=old_chat_member,
+            new_chat_member=new_chat_member,
+            invite_link=invite_link,
+            client=client
+        )
diff --git a/pyrogram/types/user_and_chats/chat_permissions.py b/pyrogram/types/user_and_chats/chat_permissions.py
new file mode 100644
index 00000000000..d920394c67f
--- /dev/null
+++ b/pyrogram/types/user_and_chats/chat_permissions.py
@@ -0,0 +1,98 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 pyrogram import raw
+from ..object import Object
+
+
+class ChatPermissions(Object):
+    """Describes actions that a non-administrator user is allowed to take in a chat.
+
+    Parameters:
+        can_send_messages (``bool``, *optional*):
+            True, if the user is allowed to send text messages, contacts, locations and venues.
+
+        can_send_media_messages (``bool``, *optional*):
+            True, if the user is allowed to send audios, documents, photos, videos, video notes and voice notes.
+            Implies *can_send_messages*.
+
+        can_send_other_messages (``bool``, *optional*):
+            True, if the user is allowed to send animations, games, stickers and use inline bots.
+            Implies *can_send_media_messages*.
+
+        can_send_polls (``bool``, *optional*):
+            True, if the user is allowed to send polls.
+            Implies can_send_messages
+
+        can_add_web_page_previews (``bool``, *optional*):
+            True, if the user is allowed to add web page previews to their messages.
+            Implies *can_send_media_messages*.
+
+        can_change_info (``bool``, *optional*):
+            True, if the user is allowed to change the chat title, photo and other settings.
+            Ignored in public supergroups
+
+        can_invite_users (``bool``, *optional*):
+            True, if the user is allowed to invite new users to the chat.
+
+        can_pin_messages (``bool``, *optional*):
+            True, if the user is allowed to pin messages.
+            Ignored in public supergroups.
+    """
+
+    def __init__(
+        self,
+        *,
+        can_send_messages: bool = None,  # Text, contacts, locations and venues
+        can_send_media_messages: bool = None,  # Audio files, documents, photos, videos, video notes and voice notes
+        can_send_other_messages: bool = None,  # Stickers, animations, games, inline bots
+        can_send_polls: bool = None,
+        can_add_web_page_previews: bool = None,
+        can_change_info: bool = None,
+        can_invite_users: bool = None,
+        can_pin_messages: bool = None
+    ):
+        super().__init__(None)
+
+        self.can_send_messages = can_send_messages
+        self.can_send_media_messages = can_send_media_messages
+        self.can_send_other_messages = can_send_other_messages
+        self.can_send_polls = can_send_polls
+        self.can_add_web_page_previews = can_add_web_page_previews
+        self.can_change_info = can_change_info
+        self.can_invite_users = can_invite_users
+        self.can_pin_messages = can_pin_messages
+
+    @staticmethod
+    def _parse(denied_permissions: "raw.base.ChatBannedRights") -> "ChatPermissions":
+        if isinstance(denied_permissions, raw.types.ChatBannedRights):
+            return ChatPermissions(
+                can_send_messages=not denied_permissions.send_messages,
+                can_send_media_messages=not denied_permissions.send_media,
+                can_send_other_messages=any([
+                    not denied_permissions.send_stickers,
+                    not denied_permissions.send_gifs,
+                    not denied_permissions.send_games,
+                    not denied_permissions.send_inline
+                ]),
+                can_add_web_page_previews=not denied_permissions.embed_links,
+                can_send_polls=not denied_permissions.send_polls,
+                can_change_info=not denied_permissions.change_info,
+                can_invite_users=not denied_permissions.invite_users,
+                can_pin_messages=not denied_permissions.pin_messages
+            )
diff --git a/pyrogram/types/user_and_chats/chat_photo.py b/pyrogram/types/user_and_chats/chat_photo.py
new file mode 100644
index 00000000000..b3aba61dd07
--- /dev/null
+++ b/pyrogram/types/user_and_chats/chat_photo.py
@@ -0,0 +1,107 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import Union
+
+import pyrogram
+from pyrogram import raw
+from pyrogram.file_id import FileId, FileType, FileUniqueId, FileUniqueType, ThumbnailSource
+from ..object import Object
+
+
+class ChatPhoto(Object):
+    """A chat photo.
+
+    Parameters:
+        small_file_id (``str``):
+            File identifier of small (160x160) chat photo.
+            This file_id can be used only for photo download and only for as long as the photo is not changed.
+
+        small_photo_unique_id (``str``):
+            Unique file identifier of small (160x160) chat photo, which is supposed to be the same over time and for
+            different accounts. Can't be used to download or reuse the file.
+
+        big_file_id (``str``):
+            File identifier of big (640x640) chat photo.
+            This file_id can be used only for photo download and only for as long as the photo is not changed.
+
+        big_photo_unique_id (``str``):
+            Unique file identifier of big (640x640) chat photo, which is supposed to be the same over time and for
+            different accounts. Can't be used to download or reuse the file.
+    """
+
+    def __init__(
+        self,
+        *,
+        client: "pyrogram.Client" = None,
+        small_file_id: str,
+        small_photo_unique_id: str,
+        big_file_id: str,
+        big_photo_unique_id: str
+
+    ):
+        super().__init__(client)
+
+        self.small_file_id = small_file_id
+        self.small_photo_unique_id = small_photo_unique_id
+        self.big_file_id = big_file_id
+        self.big_photo_unique_id = big_photo_unique_id
+
+    @staticmethod
+    def _parse(
+        client,
+        chat_photo: Union["raw.types.UserProfilePhoto", "raw.types.ChatPhoto"],
+        peer_id: int,
+        peer_access_hash: int
+    ):
+        if not isinstance(chat_photo, (raw.types.UserProfilePhoto, raw.types.ChatPhoto)):
+            return None
+
+        return ChatPhoto(
+            small_file_id=FileId(
+                file_type=FileType.CHAT_PHOTO,
+                dc_id=chat_photo.dc_id,
+                media_id=chat_photo.photo_id,
+                access_hash=0,
+                volume_id=0,
+                thumbnail_source=ThumbnailSource.CHAT_PHOTO_SMALL,
+                local_id=0,
+                chat_id=peer_id,
+                chat_access_hash=peer_access_hash
+            ).encode(),
+            small_photo_unique_id=FileUniqueId(
+                file_unique_type=FileUniqueType.DOCUMENT,
+                media_id=chat_photo.photo_id
+            ).encode(),
+            big_file_id=FileId(
+                file_type=FileType.CHAT_PHOTO,
+                dc_id=chat_photo.dc_id,
+                media_id=chat_photo.photo_id,
+                access_hash=0,
+                volume_id=0,
+                thumbnail_source=ThumbnailSource.CHAT_PHOTO_BIG,
+                local_id=0,
+                chat_id=peer_id,
+                chat_access_hash=peer_access_hash
+            ).encode(),
+            big_photo_unique_id=FileUniqueId(
+                file_unique_type=FileUniqueType.DOCUMENT,
+                media_id=chat_photo.photo_id
+            ).encode(),
+            client=client
+        )
diff --git a/pyrogram/types/user_and_chats/chat_preview.py b/pyrogram/types/user_and_chats/chat_preview.py
new file mode 100644
index 00000000000..e251c865816
--- /dev/null
+++ b/pyrogram/types/user_and_chats/chat_preview.py
@@ -0,0 +1,79 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import List
+
+import pyrogram
+from pyrogram import raw
+from pyrogram import types
+from ..object import Object
+
+
+class ChatPreview(Object):
+    """A chat preview.
+
+    Parameters:
+        title (``str``):
+            Title of the chat.
+
+        type (``str``):
+            Type of chat, can be either, "group", "supergroup" or "channel".
+
+        members_count (``int``):
+            Chat members count.
+
+        photo (:obj:`~pyrogram.types.Photo`, *optional*):
+            Chat photo.
+
+        members (List of :obj:`~pyrogram.types.User`, *optional*):
+            Preview of some of the chat members.
+    """
+
+    def __init__(
+        self,
+        *,
+        client: "pyrogram.Client" = None,
+        title: str,
+        type: str,
+        members_count: int,
+        photo: "types.Photo" = None,
+        members: List["types.User"] = None
+    ):
+        super().__init__(client)
+
+        self.title = title
+        self.type = type
+        self.members_count = members_count
+        self.photo = photo
+        self.members = members
+
+    @staticmethod
+    def _parse(client, chat_invite: "raw.types.ChatInvite") -> "ChatPreview":
+        return ChatPreview(
+            title=chat_invite.title,
+            type=("group" if not chat_invite.channel else
+                  "channel" if chat_invite.broadcast else
+                  "supergroup"),
+            members_count=chat_invite.participants_count,
+            photo=types.Photo._parse(client, chat_invite.photo),
+            members=[types.User._parse(client, user) for user in chat_invite.participants] or None,
+            client=client
+        )
+
+    # TODO: Maybe just merge this object into Chat itself by adding the "members" field.
+    #  get_chat can be used as well instead of get_chat_preview
diff --git a/pyrogram/types/user_and_chats/chat_privileges.py b/pyrogram/types/user_and_chats/chat_privileges.py
new file mode 100644
index 00000000000..09bb341dfc3
--- /dev/null
+++ b/pyrogram/types/user_and_chats/chat_privileges.py
@@ -0,0 +1,112 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 pyrogram import raw
+from ..object import Object
+
+
+class ChatPrivileges(Object):
+    """Describes privileged actions an administrator is able to take in a chat.
+
+    Parameters:
+        can_manage_chat (``bool``, *optional*):
+            True, if the administrator can access the chat event log, chat statistics, message statistics in channels,
+            see channel members, see anonymous administrators in supergroups and ignore slow mode.
+            Implied by any other administrator privilege.
+
+        can_delete_messages (``bool``, *optional*):
+            True, if the administrator can delete messages of other users.
+
+        can_manage_video_chats (``bool``, *optional*):
+            Groups and supergroups only.
+            True, if the administrator can manage video chats (also called group calls).
+
+        can_restrict_members (``bool``, *optional*):
+            True, if the administrator can restrict, ban or unban chat members.
+
+        can_promote_members (``bool``, *optional*):
+            True, if the administrator can add new administrators with a subset of his own privileges or demote
+            administrators that he has promoted, directly or indirectly (promoted by administrators that were appointed
+            by the user).
+
+        can_change_info (``bool``, *optional*):
+            True, if the user is allowed to change the chat title, photo and other settings.
+
+        can_post_messages (``bool``, *optional*):
+            Channels only.
+            True, if the administrator can post messages in the channel.
+
+        can_edit_messages (``bool``, *optional*):
+            Channels only.
+            True, if the administrator can edit messages of other users and can pin messages.
+
+        can_invite_users (``bool``, *optional*):
+            True, if the user is allowed to invite new users to the chat.
+
+        can_pin_messages (``bool``, *optional*):
+            Groups and supergroups only.
+            True, if the user is allowed to pin messages.
+
+        is_anonymous (``bool``, *optional*):
+            True, if the user's presence in the chat is hidden.
+    """
+
+    def __init__(
+        self,
+        *,
+        can_manage_chat: bool = True,
+        can_delete_messages: bool = False,
+        can_manage_video_chats: bool = False,  # Groups and supergroups only
+        can_restrict_members: bool = False,
+        can_promote_members: bool = False,
+        can_change_info: bool = False,
+        can_post_messages: bool = False,  # Channels only
+        can_edit_messages: bool = False,  # Channels only
+        can_invite_users: bool = False,
+        can_pin_messages: bool = False,  # Groups and supergroups only
+        is_anonymous: bool = False
+    ):
+        super().__init__(None)
+
+        self.can_manage_chat: bool = can_manage_chat
+        self.can_delete_messages: bool = can_delete_messages
+        self.can_manage_video_chats: bool = can_manage_video_chats
+        self.can_restrict_members: bool = can_restrict_members
+        self.can_promote_members: bool = can_promote_members
+        self.can_change_info: bool = can_change_info
+        self.can_post_messages: bool = can_post_messages
+        self.can_edit_messages: bool = can_edit_messages
+        self.can_invite_users: bool = can_invite_users
+        self.can_pin_messages: bool = can_pin_messages
+        self.is_anonymous: bool = is_anonymous
+
+    @staticmethod
+    def _parse(admin_rights: "raw.base.ChatAdminRights") -> "ChatPrivileges":
+        return ChatPrivileges(
+            can_manage_chat=admin_rights.other,
+            can_delete_messages=admin_rights.delete_messages,
+            can_manage_video_chats=admin_rights.manage_call,
+            can_restrict_members=admin_rights.ban_users,
+            can_promote_members=admin_rights.add_admins,
+            can_change_info=admin_rights.change_info,
+            can_post_messages=admin_rights.post_messages,
+            can_edit_messages=admin_rights.edit_messages,
+            can_invite_users=admin_rights.invite_users,
+            can_pin_messages=admin_rights.pin_messages,
+            is_anonymous=admin_rights.anonymous
+        )
diff --git a/pyrogram/types/user_and_chats/chat_reactions.py b/pyrogram/types/user_and_chats/chat_reactions.py
new file mode 100644
index 00000000000..057fb96654d
--- /dev/null
+++ b/pyrogram/types/user_and_chats/chat_reactions.py
@@ -0,0 +1,69 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import Optional, List
+
+import pyrogram
+from pyrogram import raw, types
+from ..object import Object
+
+
+class ChatReactions(Object):
+    """A chat reactions
+
+    Parameters:
+        all_are_enabled (``bool``, *optional*)
+
+        allow_custom_emoji (``bool``, *optional*):
+            Whether custom emoji are allowed or not.
+
+        reactions (List of :obj:`~pyrogram.types.Reaction`, *optional*):
+            Reactions available.
+    """
+
+    def __init__(
+        self,
+        *,
+        client: "pyrogram.Client" = None,
+        all_are_enabled: Optional[bool] = None,
+        allow_custom_emoji: Optional[bool] = None,
+        reactions: Optional[List["types.Reaction"]] = None,
+    ):
+        super().__init__(client)
+
+        self.all_are_enabled = all_are_enabled
+        self.allow_custom_emoji = allow_custom_emoji
+        self.reactions = reactions
+
+    @staticmethod
+    def _parse(client, chat_reactions: "raw.base.ChatReactions") -> Optional["ChatReactions"]:
+        if isinstance(chat_reactions, raw.types.ChatReactionsAll):
+            return ChatReactions(
+                client=client,
+                all_are_enabled=True,
+                allow_custom_emoji=chat_reactions.allow_custom
+            )
+
+        if isinstance(chat_reactions, raw.types.ChatReactionsSome):
+            return ChatReactions(
+                client=client,
+                reactions=[types.Reaction._parse(client, reaction)
+                           for reaction in chat_reactions.reactions]
+            )
+
+        return None
diff --git a/pyrogram/types/user_and_chats/dialog.py b/pyrogram/types/user_and_chats/dialog.py
new file mode 100644
index 00000000000..7259a89a574
--- /dev/null
+++ b/pyrogram/types/user_and_chats/dialog.py
@@ -0,0 +1,79 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import pyrogram
+from pyrogram import raw
+from pyrogram import types
+from ..object import Object
+from ... import utils
+
+
+class Dialog(Object):
+    """A user's dialog.
+
+    Parameters:
+        chat (:obj:`~pyrogram.types.Chat`):
+            Conversation the dialog belongs to.
+
+        top_message (:obj:`~pyrogram.types.Message`):
+            The last message sent in the dialog at this time.
+
+        unread_messages_count (``int``):
+            Amount of unread messages in this dialog.
+
+        unread_mentions_count (``int``):
+            Amount of unread messages containing a mention in this dialog.
+
+        unread_mark (``bool``):
+            True, if the dialog has the unread mark set.
+
+        is_pinned (``bool``):
+            True, if the dialog is pinned.
+    """
+
+    def __init__(
+        self,
+        *,
+        client: "pyrogram.Client" = None,
+        chat: "types.Chat",
+        top_message: "types.Message",
+        unread_messages_count: int,
+        unread_mentions_count: int,
+        unread_mark: bool,
+        is_pinned: bool
+    ):
+        super().__init__(client)
+
+        self.chat = chat
+        self.top_message = top_message
+        self.unread_messages_count = unread_messages_count
+        self.unread_mentions_count = unread_mentions_count
+        self.unread_mark = unread_mark
+        self.is_pinned = is_pinned
+
+    @staticmethod
+    def _parse(client, dialog: "raw.types.Dialog", messages, users, chats) -> "Dialog":
+        return Dialog(
+            chat=types.Chat._parse_dialog(client, dialog.peer, users, chats),
+            top_message=messages.get(utils.get_peer_id(dialog.peer)),
+            unread_messages_count=dialog.unread_count,
+            unread_mentions_count=dialog.unread_mentions_count,
+            unread_mark=dialog.unread_mark,
+            is_pinned=dialog.pinned,
+            client=client
+        )
diff --git a/pyrogram/types/user_and_chats/emoji_status.py b/pyrogram/types/user_and_chats/emoji_status.py
new file mode 100644
index 00000000000..50b46bfa464
--- /dev/null
+++ b/pyrogram/types/user_and_chats/emoji_status.py
@@ -0,0 +1,77 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 datetime import datetime
+from typing import Optional
+
+import pyrogram
+from pyrogram import raw
+from pyrogram import utils
+from ..object import Object
+
+
+class EmojiStatus(Object):
+    """A user emoji status.
+
+    Parameters:
+        custom_emoji_id (``int``):
+            Custom emoji id.
+
+        until_date (:py:obj:`~datetime.datetime`, *optional*):
+            Valid until date.
+    """
+
+    def __init__(
+        self,
+        *,
+        client: "pyrogram.Client" = None,
+        custom_emoji_id: int,
+        until_date: Optional[datetime] = None
+    ):
+        super().__init__(client)
+
+        self.custom_emoji_id = custom_emoji_id
+        self.until_date = until_date
+
+    @staticmethod
+    def _parse(client, emoji_status: "raw.base.EmojiStatus") -> Optional["EmojiStatus"]:
+        if isinstance(emoji_status, raw.types.EmojiStatus):
+            return EmojiStatus(
+                client=client,
+                custom_emoji_id=emoji_status.document_id
+            )
+
+        if isinstance(emoji_status, raw.types.EmojiStatusUntil):
+            return EmojiStatus(
+                client=client,
+                custom_emoji_id=emoji_status.document_id,
+                until_date=utils.timestamp_to_datetime(emoji_status.until)
+            )
+
+        return None
+
+    def write(self):
+        if self.until_date:
+            return raw.types.EmojiStatusUntil(
+                document_id=self.custom_emoji_id,
+                until=utils.datetime_to_timestamp(self.until_date)
+            )
+
+        return raw.types.EmojiStatus(
+            document_id=self.custom_emoji_id
+        )
diff --git a/pyrogram/types/user_and_chats/invite_link_importer.py b/pyrogram/types/user_and_chats/invite_link_importer.py
new file mode 100644
index 00000000000..34e5f397f4e
--- /dev/null
+++ b/pyrogram/types/user_and_chats/invite_link_importer.py
@@ -0,0 +1,61 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 datetime import datetime
+
+from pyrogram import raw, utils
+from pyrogram import types
+from ..object import Object
+
+
+class InviteLinkImporter(Object):
+    """The date and user of when someone has joined with an invite link.
+
+    Parameters:
+        date (:py:obj:`~datetime.datetime`):
+            The time of when this user used the given link
+
+        user (:obj:`~pyrogram.types.User`):
+            The user that has used the given invite link
+    """
+
+    def __init__(
+        self, *,
+        date: datetime,
+        user: "types.User"
+    ):
+        super().__init__(None)
+
+        self.date = date
+        self.user = user
+
+    @staticmethod
+    def _parse(client, invite_importers: "raw.types.messages.ChatInviteImporters"):
+        importers = types.List()
+
+        d = {i.id: i for i in invite_importers.users}
+
+        for j in invite_importers.importers:
+            importers.append(
+                InviteLinkImporter(
+                    date=utils.timestamp_to_datetime(j.date),
+                    user=types.User._parse(client=None, user=d[j.user_id])
+                )
+            )
+
+        return importers
diff --git a/pyrogram/types/user_and_chats/restriction.py b/pyrogram/types/user_and_chats/restriction.py
new file mode 100644
index 00000000000..8f7a1b7269f
--- /dev/null
+++ b/pyrogram/types/user_and_chats/restriction.py
@@ -0,0 +1,50 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 pyrogram import raw
+from ..object import Object
+
+
+class Restriction(Object):
+    """A restriction applied to bots or chats.
+
+    Parameters:
+        platform (``str``):
+            The platform the restriction is applied to, e.g. "ios", "android"
+
+        reason (``str``):
+            The restriction reason, e.g. "porn", "copyright".
+
+        text (``str``):
+            The restriction text.
+    """
+
+    def __init__(self, *, platform: str, reason: str, text: str):
+        super().__init__(None)
+
+        self.platform = platform
+        self.reason = reason
+        self.text = text
+
+    @staticmethod
+    def _parse(restriction: "raw.types.RestrictionReason") -> "Restriction":
+        return Restriction(
+            platform=restriction.platform,
+            reason=restriction.reason,
+            text=restriction.text
+        )
diff --git a/pyrogram/types/user_and_chats/user.py b/pyrogram/types/user_and_chats/user.py
new file mode 100644
index 00000000000..e9813578263
--- /dev/null
+++ b/pyrogram/types/user_and_chats/user.py
@@ -0,0 +1,399 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import html
+from datetime import datetime
+from typing import List, Optional
+
+import pyrogram
+from pyrogram import enums, utils
+from pyrogram import raw
+from pyrogram import types
+from ..object import Object
+from ..update import Update
+
+
+class Link(str):
+    HTML = "{text}"
+    MARKDOWN = "[{text}]({url})"
+
+    def __init__(self, url: str, text: str, style: enums.ParseMode):
+        super().__init__()
+
+        self.url = url
+        self.text = text
+        self.style = style
+
+    @staticmethod
+    def format(url: str, text: str, style: enums.ParseMode):
+        if style == enums.ParseMode.MARKDOWN:
+            fmt = Link.MARKDOWN
+        else:
+            fmt = Link.HTML
+
+        return fmt.format(url=url, text=html.escape(text))
+
+    # noinspection PyArgumentList
+    def __new__(cls, url, text, style):
+        return str.__new__(cls, Link.format(url, text, style))
+
+    def __call__(self, other: str = None, *, style: str = None):
+        return Link.format(self.url, other or self.text, style or self.style)
+
+    def __str__(self):
+        return Link.format(self.url, self.text, self.style)
+
+
+class User(Object, Update):
+    """A Telegram user or bot.
+
+    Parameters:
+        id (``int``):
+            Unique identifier for this user or bot.
+
+        is_self(``bool``, *optional*):
+            True, if this user is you yourself.
+
+        is_contact(``bool``, *optional*):
+            True, if this user is in your contacts.
+
+        is_mutual_contact(``bool``, *optional*):
+            True, if you both have each other's contact.
+
+        is_deleted(``bool``, *optional*):
+            True, if this user is deleted.
+
+        is_bot (``bool``, *optional*):
+            True, if this user is a bot.
+
+        is_verified (``bool``, *optional*):
+            True, if this user has been verified by Telegram.
+
+        is_restricted (``bool``, *optional*):
+            True, if this user has been restricted. Bots only.
+            See *restriction_reason* for details.
+
+        is_scam (``bool``, *optional*):
+            True, if this user has been flagged for scam.
+
+        is_fake (``bool``, *optional*):
+            True, if this user has been flagged for impersonation.
+
+        is_support (``bool``, *optional*):
+            True, if this user is part of the Telegram support team.
+
+        is_premium (``bool``, *optional*):
+            True, if this user is a premium user.
+
+        first_name (``str``, *optional*):
+            User's or bot's first name.
+
+        last_name (``str``, *optional*):
+            User's or bot's last name.
+
+        status (:obj:`~pyrogram.enums.UserStatus`, *optional*):
+            User's last seen & online status. ``None``, for bots.
+
+        last_online_date (:py:obj:`~datetime.datetime`, *optional*):
+            Last online date of a user. Only available in case status is :obj:`~pyrogram.enums.UserStatus.OFFLINE`.
+
+        next_offline_date (:py:obj:`~datetime.datetime`, *optional*):
+            Date when a user will automatically go offline. Only available in case status is :obj:`~pyrogram.enums.UserStatus.ONLINE`.
+
+        username (``str``, *optional*):
+            User's or bot's username.
+
+        language_code (``str``, *optional*):
+            IETF language tag of the user's language.
+
+        emoji_status (:obj:`~pyrogram.types.EmojiStatus`, *optional*):
+            Emoji status.
+
+        dc_id (``int``, *optional*):
+            User's or bot's assigned DC (data center). Available only in case the user has set a public profile photo.
+            Note that this information is approximate; it is based on where Telegram stores a user profile pictures and
+            does not by any means tell you the user location (i.e. a user might travel far away, but will still connect
+            to its assigned DC). More info at `FAQs `_.
+
+        phone_number (``str``, *optional*):
+            User's phone number.
+
+        photo (:obj:`~pyrogram.types.ChatPhoto`, *optional*):
+            User's or bot's current profile photo. Suitable for downloads only.
+
+        restrictions (List of :obj:`~pyrogram.types.Restriction`, *optional*):
+            The list of reasons why this bot might be unavailable to some users.
+            This field is available only in case *is_restricted* is True.
+
+        mention (``str``, *property*):
+            Generate a text mention for this user.
+            You can use ``user.mention()`` to mention the user using their first name (styled using html), or
+            ``user.mention("another name")`` for a custom name. To choose a different style
+            ("html" or "md"/"markdown") use ``user.mention(style="md")``.
+    """
+
+    def __init__(
+        self,
+        *,
+        client: "pyrogram.Client" = None,
+        id: int,
+        is_self: bool = None,
+        is_contact: bool = None,
+        is_mutual_contact: bool = None,
+        is_deleted: bool = None,
+        is_bot: bool = None,
+        is_verified: bool = None,
+        is_restricted: bool = None,
+        is_scam: bool = None,
+        is_fake: bool = None,
+        is_support: bool = None,
+        is_premium: bool = None,
+        first_name: str = None,
+        last_name: str = None,
+        status: "enums.UserStatus" = None,
+        last_online_date: datetime = None,
+        next_offline_date: datetime = None,
+        username: str = None,
+        language_code: str = None,
+        emoji_status: Optional["types.EmojiStatus"] = None,
+        dc_id: int = None,
+        phone_number: str = None,
+        photo: "types.ChatPhoto" = None,
+        restrictions: List["types.Restriction"] = None
+    ):
+        super().__init__(client)
+
+        self.id = id
+        self.is_self = is_self
+        self.is_contact = is_contact
+        self.is_mutual_contact = is_mutual_contact
+        self.is_deleted = is_deleted
+        self.is_bot = is_bot
+        self.is_verified = is_verified
+        self.is_restricted = is_restricted
+        self.is_scam = is_scam
+        self.is_fake = is_fake
+        self.is_support = is_support
+        self.is_premium = is_premium
+        self.first_name = first_name
+        self.last_name = last_name
+        self.status = status
+        self.last_online_date = last_online_date
+        self.next_offline_date = next_offline_date
+        self.username = username
+        self.language_code = language_code
+        self.emoji_status = emoji_status
+        self.dc_id = dc_id
+        self.phone_number = phone_number
+        self.photo = photo
+        self.restrictions = restrictions
+
+    @property
+    def mention(self):
+        return Link(
+            f"tg://user?id={self.id}",
+            self.first_name or "Deleted Account",
+            self._client.parse_mode
+        )
+
+    @staticmethod
+    def _parse(client, user: "raw.base.User") -> Optional["User"]:
+        if user is None or isinstance(user, raw.types.UserEmpty):
+            return None
+
+        return User(
+            id=user.id,
+            is_self=user.is_self,
+            is_contact=user.contact,
+            is_mutual_contact=user.mutual_contact,
+            is_deleted=user.deleted,
+            is_bot=user.bot,
+            is_verified=user.verified,
+            is_restricted=user.restricted,
+            is_scam=user.scam,
+            is_fake=user.fake,
+            is_support=user.support,
+            is_premium=user.premium,
+            first_name=user.first_name,
+            last_name=user.last_name,
+            **User._parse_status(user.status, user.bot),
+            username=user.username,
+            language_code=user.lang_code,
+            emoji_status=types.EmojiStatus._parse(client, user.emoji_status),
+            dc_id=getattr(user.photo, "dc_id", None),
+            phone_number=user.phone,
+            photo=types.ChatPhoto._parse(client, user.photo, user.id, user.access_hash),
+            restrictions=types.List([types.Restriction._parse(r) for r in user.restriction_reason]) or None,
+            client=client
+        )
+
+    @staticmethod
+    def _parse_status(user_status: "raw.base.UserStatus", is_bot: bool = False):
+        if isinstance(user_status, raw.types.UserStatusOnline):
+            status, date = enums.UserStatus.ONLINE, user_status.expires
+        elif isinstance(user_status, raw.types.UserStatusOffline):
+            status, date = enums.UserStatus.OFFLINE, user_status.was_online
+        elif isinstance(user_status, raw.types.UserStatusRecently):
+            status, date = enums.UserStatus.RECENTLY, None
+        elif isinstance(user_status, raw.types.UserStatusLastWeek):
+            status, date = enums.UserStatus.LAST_WEEK, None
+        elif isinstance(user_status, raw.types.UserStatusLastMonth):
+            status, date = enums.UserStatus.LAST_MONTH, None
+        else:
+            status, date = enums.UserStatus.LONG_AGO, None
+
+        last_online_date = None
+        next_offline_date = None
+
+        if is_bot:
+            status = None
+
+        if status == enums.UserStatus.ONLINE:
+            next_offline_date = utils.timestamp_to_datetime(date)
+
+        if status == enums.UserStatus.OFFLINE:
+            last_online_date = utils.timestamp_to_datetime(date)
+
+        return {
+            "status": status,
+            "last_online_date": last_online_date,
+            "next_offline_date": next_offline_date
+        }
+
+    @staticmethod
+    def _parse_user_status(client, user_status: "raw.types.UpdateUserStatus"):
+        return User(
+            id=user_status.user_id,
+            **User._parse_status(user_status.status),
+            client=client
+        )
+
+    async def archive(self):
+        """Bound method *archive* of :obj:`~pyrogram.types.User`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.archive_chats(123456789)
+
+        Example:
+            .. code-block:: python
+
+               await user.archive()
+
+        Returns:
+            True on success.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+
+        return await self._client.archive_chats(self.id)
+
+    async def unarchive(self):
+        """Bound method *unarchive* of :obj:`~pyrogram.types.User`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.unarchive_chats(123456789)
+
+        Example:
+            .. code-block:: python
+
+                await user.unarchive()
+
+        Returns:
+            True on success.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+
+        return await self._client.unarchive_chats(self.id)
+
+    def block(self):
+        """Bound method *block* of :obj:`~pyrogram.types.User`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            await client.block_user(123456789)
+
+        Example:
+            .. code-block:: python
+
+                await user.block()
+
+        Returns:
+            True on success.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+
+        return self._client.block_user(self.id)
+
+    def unblock(self):
+        """Bound method *unblock* of :obj:`~pyrogram.types.User`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            client.unblock_user(123456789)
+
+        Example:
+            .. code-block:: python
+
+                user.unblock()
+
+        Returns:
+            True on success.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+
+        return self._client.unblock_user(self.id)
+
+    def get_common_chats(self):
+        """Bound method *get_common_chats* of :obj:`~pyrogram.types.User`.
+
+        Use as a shortcut for:
+
+        .. code-block:: python
+
+            client.get_common_chats(123456789)
+
+        Example:
+            .. code-block:: python
+
+                user.get_common_chats()
+
+        Returns:
+            True on success.
+
+        Raises:
+            RPCError: In case of a Telegram RPC error.
+        """
+
+        return self._client.get_common_chats(self.id)
diff --git a/pyrogram/types/user_and_chats/video_chat_ended.py b/pyrogram/types/user_and_chats/video_chat_ended.py
new file mode 100644
index 00000000000..8c9fac6a0aa
--- /dev/null
+++ b/pyrogram/types/user_and_chats/video_chat_ended.py
@@ -0,0 +1,41 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 pyrogram import raw
+from ..object import Object
+
+
+class VideoChatEnded(Object):
+    """A service message about a voice chat ended in the chat.
+
+    Parameters:
+        duration (``int``):
+            Voice chat duration; in seconds.
+    """
+
+    def __init__(
+        self, *,
+        duration: int
+    ):
+        super().__init__()
+
+        self.duration = duration
+
+    @staticmethod
+    def _parse(action: "raw.types.MessageActionGroupCall") -> "VideoChatEnded":
+        return VideoChatEnded(duration=action.duration)
diff --git a/pyrogram/types/user_and_chats/video_chat_members_invited.py b/pyrogram/types/user_and_chats/video_chat_members_invited.py
new file mode 100644
index 00000000000..9f2e3d1ef47
--- /dev/null
+++ b/pyrogram/types/user_and_chats/video_chat_members_invited.py
@@ -0,0 +1,50 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 typing import List, Dict
+
+from pyrogram import raw, types
+from ..object import Object
+
+
+class VideoChatMembersInvited(Object):
+    """A service message about new members invited to a voice chat.
+
+
+    Parameters:
+        users (List of :obj:`~pyrogram.types.User`):
+            New members that were invited to the voice chat.
+    """
+
+    def __init__(
+        self, *,
+        users: List["types.User"]
+    ):
+        super().__init__()
+
+        self.users = users
+
+    @staticmethod
+    def _parse(
+        client,
+        action: "raw.types.MessageActionInviteToGroupCall",
+        users: Dict[int, "raw.types.User"]
+    ) -> "VideoChatMembersInvited":
+        users = [types.User._parse(client, users[i]) for i in action.users]
+
+        return VideoChatMembersInvited(users=users)
diff --git a/pyrogram/types/user_and_chats/video_chat_scheduled.py b/pyrogram/types/user_and_chats/video_chat_scheduled.py
new file mode 100644
index 00000000000..5bdd5925151
--- /dev/null
+++ b/pyrogram/types/user_and_chats/video_chat_scheduled.py
@@ -0,0 +1,43 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 datetime import datetime
+
+from pyrogram import raw, utils
+from ..object import Object
+
+
+class VideoChatScheduled(Object):
+    """A service message about a voice chat scheduled in the chat.
+
+    Parameters:
+        start_date (:py:obj:`~datetime.datetime`):
+            Point in time when the voice chat is supposed to be started by a chat administrator.
+    """
+
+    def __init__(
+        self, *,
+        start_date: datetime
+    ):
+        super().__init__()
+
+        self.start_date = start_date
+
+    @staticmethod
+    def _parse(action: "raw.types.MessageActionGroupCallScheduled") -> "VideoChatScheduled":
+        return VideoChatScheduled(start_date=utils.timestamp_to_datetime(action.schedule_date))
diff --git a/pyrogram/types/user_and_chats/video_chat_started.py b/pyrogram/types/user_and_chats/video_chat_started.py
new file mode 100644
index 00000000000..ff48b39cd61
--- /dev/null
+++ b/pyrogram/types/user_and_chats/video_chat_started.py
@@ -0,0 +1,29 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 ..object import Object
+
+
+class VideoChatStarted(Object):
+    """A service message about a voice chat started in the chat.
+
+    Currently holds no information.
+    """
+
+    def __init__(self):
+        super().__init__()
diff --git a/pyrogram/utils.py b/pyrogram/utils.py
new file mode 100644
index 00000000000..f7fe59706dc
--- /dev/null
+++ b/pyrogram/utils.py
@@ -0,0 +1,371 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import asyncio
+import base64
+import functools
+import hashlib
+import os
+import struct
+from concurrent.futures.thread import ThreadPoolExecutor
+from datetime import datetime, timezone
+from getpass import getpass
+from typing import Union, List, Dict, Optional
+
+import pyrogram
+from pyrogram import raw, enums
+from pyrogram import types
+from pyrogram.file_id import FileId, FileType, PHOTO_TYPES, DOCUMENT_TYPES
+
+
+async def ainput(prompt: str = "", *, hide: bool = False):
+    """Just like the built-in input, but async"""
+    with ThreadPoolExecutor(1) as executor:
+        func = functools.partial(getpass if hide else input, prompt)
+        return await asyncio.get_event_loop().run_in_executor(executor, func)
+
+
+def get_input_media_from_file_id(
+    file_id: str,
+    expected_file_type: FileType = None,
+    ttl_seconds: int = None
+) -> Union["raw.types.InputMediaPhoto", "raw.types.InputMediaDocument"]:
+    try:
+        decoded = FileId.decode(file_id)
+    except Exception:
+        raise ValueError(
+            f'Failed to decode "{file_id}". The value does not represent an existing local file, '
+            f"HTTP URL, or valid file id."
+        )
+
+    file_type = decoded.file_type
+
+    if expected_file_type is not None and file_type != expected_file_type:
+        raise ValueError(f"Expected {expected_file_type.name}, got {file_type.name} file id instead")
+
+    if file_type in (FileType.THUMBNAIL, FileType.CHAT_PHOTO):
+        raise ValueError(f"This file id can only be used for download: {file_id}")
+
+    if file_type in PHOTO_TYPES:
+        return raw.types.InputMediaPhoto(
+            id=raw.types.InputPhoto(
+                id=decoded.media_id,
+                access_hash=decoded.access_hash,
+                file_reference=decoded.file_reference
+            ),
+            ttl_seconds=ttl_seconds
+        )
+
+    if file_type in DOCUMENT_TYPES:
+        return raw.types.InputMediaDocument(
+            id=raw.types.InputDocument(
+                id=decoded.media_id,
+                access_hash=decoded.access_hash,
+                file_reference=decoded.file_reference
+            ),
+            ttl_seconds=ttl_seconds
+        )
+
+    raise ValueError(f"Unknown file id: {file_id}")
+
+
+async def parse_messages(
+    client,
+    messages: "raw.types.messages.Messages",
+    replies: int = 1
+) -> List["types.Message"]:
+    users = {i.id: i for i in messages.users}
+    chats = {i.id: i for i in messages.chats}
+
+    if not messages.messages:
+        return types.List()
+
+    parsed_messages = []
+
+    for message in messages.messages:
+        parsed_messages.append(await types.Message._parse(client, message, users, chats, replies=0))
+
+    if replies:
+        messages_with_replies = {
+            i.id: i.reply_to.reply_to_msg_id
+            for i in messages.messages
+            if not isinstance(i, raw.types.MessageEmpty) and i.reply_to
+        }
+
+        if messages_with_replies:
+            # We need a chat id, but some messages might be empty (no chat attribute available)
+            # Scan until we find a message with a chat available (there must be one, because we are fetching replies)
+            for m in parsed_messages:
+                if m.chat:
+                    chat_id = m.chat.id
+                    break
+            else:
+                chat_id = 0
+
+            reply_messages = await client.get_messages(
+                chat_id,
+                reply_to_message_ids=messages_with_replies.keys(),
+                replies=replies - 1
+            )
+
+            for message in parsed_messages:
+                reply_id = messages_with_replies.get(message.id, None)
+
+                for reply in reply_messages:
+                    if reply.id == reply_id:
+                        message.reply_to_message = reply
+
+    return types.List(parsed_messages)
+
+
+def parse_deleted_messages(client, update) -> List["types.Message"]:
+    messages = update.messages
+    channel_id = getattr(update, "channel_id", None)
+
+    parsed_messages = []
+
+    for message in messages:
+        parsed_messages.append(
+            types.Message(
+                id=message,
+                chat=types.Chat(
+                    id=get_channel_id(channel_id),
+                    type=enums.ChatType.CHANNEL,
+                    client=client
+                ) if channel_id is not None else None,
+                client=client
+            )
+        )
+
+    return types.List(parsed_messages)
+
+
+def pack_inline_message_id(msg_id: "raw.base.InputBotInlineMessageID"):
+    if isinstance(msg_id, raw.types.InputBotInlineMessageID):
+        inline_message_id_packed = struct.pack(
+            " Optional[int]:
+    """Get the raw peer id from a Peer object"""
+    if isinstance(peer, raw.types.PeerUser):
+        return peer.user_id
+
+    if isinstance(peer, raw.types.PeerChat):
+        return peer.chat_id
+
+    if isinstance(peer, raw.types.PeerChannel):
+        return peer.channel_id
+
+    return None
+
+
+def get_peer_id(peer: raw.base.Peer) -> int:
+    """Get the non-raw peer id from a Peer object"""
+    if isinstance(peer, raw.types.PeerUser):
+        return peer.user_id
+
+    if isinstance(peer, raw.types.PeerChat):
+        return -peer.chat_id
+
+    if isinstance(peer, raw.types.PeerChannel):
+        return MAX_CHANNEL_ID - peer.channel_id
+
+    raise ValueError(f"Peer type invalid: {peer}")
+
+
+def get_peer_type(peer_id: int) -> str:
+    if peer_id < 0:
+        if MIN_CHAT_ID <= peer_id:
+            return "chat"
+
+        if MIN_CHANNEL_ID <= peer_id < MAX_CHANNEL_ID:
+            return "channel"
+    elif 0 < peer_id <= MAX_USER_ID:
+        return "user"
+
+    raise ValueError(f"Peer id invalid: {peer_id}")
+
+
+def get_channel_id(peer_id: int) -> int:
+    return MAX_CHANNEL_ID - peer_id
+
+
+def btoi(b: bytes) -> int:
+    return int.from_bytes(b, "big")
+
+
+def itob(i: int) -> bytes:
+    return i.to_bytes(256, "big")
+
+
+def sha256(data: bytes) -> bytes:
+    return hashlib.sha256(data).digest()
+
+
+def xor(a: bytes, b: bytes) -> bytes:
+    return bytes(i ^ j for i, j in zip(a, b))
+
+
+def compute_password_hash(
+    algo: raw.types.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow,
+    password: str
+) -> bytes:
+    hash1 = sha256(algo.salt1 + password.encode() + algo.salt1)
+    hash2 = sha256(algo.salt2 + hash1 + algo.salt2)
+    hash3 = hashlib.pbkdf2_hmac("sha512", hash2, algo.salt1, 100000)
+
+    return sha256(algo.salt2 + hash3 + algo.salt2)
+
+
+# noinspection PyPep8Naming
+def compute_password_check(
+    r: raw.types.account.Password,
+    password: str
+) -> raw.types.InputCheckPasswordSRP:
+    algo = r.current_algo
+
+    p_bytes = algo.p
+    p = btoi(algo.p)
+
+    g_bytes = itob(algo.g)
+    g = algo.g
+
+    B_bytes = r.srp_B
+    B = btoi(B_bytes)
+
+    srp_id = r.srp_id
+
+    x_bytes = compute_password_hash(algo, password)
+    x = btoi(x_bytes)
+
+    g_x = pow(g, x, p)
+
+    k_bytes = sha256(p_bytes + g_bytes)
+    k = btoi(k_bytes)
+
+    kg_x = (k * g_x) % p
+
+    while True:
+        a_bytes = os.urandom(256)
+        a = btoi(a_bytes)
+
+        A = pow(g, a, p)
+        A_bytes = itob(A)
+
+        u = btoi(sha256(A_bytes + B_bytes))
+
+        if u > 0:
+            break
+
+    g_b = (B - kg_x) % p
+
+    ux = u * x
+    a_ux = a + ux
+    S = pow(g_b, a_ux, p)
+    S_bytes = itob(S)
+
+    K_bytes = sha256(S_bytes)
+
+    M1_bytes = sha256(
+        xor(sha256(p_bytes), sha256(g_bytes))
+        + sha256(algo.salt1)
+        + sha256(algo.salt2)
+        + A_bytes
+        + B_bytes
+        + K_bytes
+    )
+
+    return raw.types.InputCheckPasswordSRP(srp_id=srp_id, A=A_bytes, M1=M1_bytes)
+
+
+async def parse_text_entities(
+    client: "pyrogram.Client",
+    text: str,
+    parse_mode: enums.ParseMode,
+    entities: List["types.MessageEntity"]
+) -> Dict[str, Union[str, List[raw.base.MessageEntity]]]:
+    if entities:
+        # Inject the client instance because parsing user mentions requires it
+        for entity in entities:
+            entity._client = client
+
+        text, entities = text, [await entity.write() for entity in entities] or None
+    else:
+        text, entities = (await client.parser.parse(text, parse_mode)).values()
+
+    return {
+        "message": text,
+        "entities": entities
+    }
+
+
+def zero_datetime() -> datetime:
+    return datetime.fromtimestamp(0, timezone.utc)
+
+
+def timestamp_to_datetime(ts: Optional[int]) -> Optional[datetime]:
+    return datetime.fromtimestamp(ts) if ts else None
+
+
+def datetime_to_timestamp(dt: Optional[datetime]) -> Optional[int]:
+    return int(dt.timestamp()) if dt else None
diff --git a/pyrogram/vendor/__init__.py b/pyrogram/vendor/__init__.py
deleted file mode 100644
index 208b84f935a..00000000000
--- a/pyrogram/vendor/__init__.py
+++ /dev/null
@@ -1,19 +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 .typing import typing
diff --git a/pyrogram/vendor/typing/__init__.py b/pyrogram/vendor/typing/__init__.py
deleted file mode 100644
index f3769dd4d0d..00000000000
--- a/pyrogram/vendor/typing/__init__.py
+++ /dev/null
@@ -1,17 +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 .
diff --git a/pyrogram/vendor/typing/typing.py b/pyrogram/vendor/typing/typing.py
deleted file mode 100644
index 16888d3f149..00000000000
--- a/pyrogram/vendor/typing/typing.py
+++ /dev/null
@@ -1,2406 +0,0 @@
-import abc
-import collections
-import contextlib
-import functools
-import re as stdlib_re  # Avoid confusion with the re we export.
-import sys
-import types
-from abc import abstractmethod, abstractproperty
-
-try:
-    import collections.abc as collections_abc
-except ImportError:
-    import collections as collections_abc  # Fallback for PY3.2.
-if sys.version_info[:2] >= (3, 6):
-    pass
-try:
-    from types import WrapperDescriptorType, MethodWrapperType, MethodDescriptorType
-except ImportError:
-    WrapperDescriptorType = type(object.__init__)
-    MethodWrapperType = type(object().__str__)
-    MethodDescriptorType = type(str.join)
-
-# Please keep __all__ alphabetized within each category.
-__all__ = [
-    # Super-special typing primitives.
-    'Any',
-    'Callable',
-    'ClassVar',
-    'Generic',
-    'Optional',
-    'Tuple',
-    'Type',
-    'TypeVar',
-    'Union',
-
-    # ABCs (from collections.abc).
-    'AbstractSet',  # collections.abc.Set.
-    'GenericMeta',  # subclass of abc.ABCMeta and a metaclass
-    # for 'Generic' and ABCs below.
-    'ByteString',
-    'Container',
-    'ContextManager',
-    'Hashable',
-    'ItemsView',
-    'Iterable',
-    'Iterator',
-    'KeysView',
-    'Mapping',
-    'MappingView',
-    'MutableMapping',
-    'MutableSequence',
-    'MutableSet',
-    'Sequence',
-    'Sized',
-    'ValuesView',
-    # The following are added depending on presence
-    # of their non-generic counterparts in stdlib:
-    # Awaitable,
-    # AsyncIterator,
-    # AsyncIterable,
-    # Coroutine,
-    # Collection,
-    # AsyncGenerator,
-    # AsyncContextManager
-
-    # Structural checks, a.k.a. protocols.
-    'Reversible',
-    'SupportsAbs',
-    'SupportsBytes',
-    'SupportsComplex',
-    'SupportsFloat',
-    'SupportsInt',
-    'SupportsRound',
-
-    # Concrete collection types.
-    'Counter',
-    'Deque',
-    'Dict',
-    'DefaultDict',
-    'List',
-    'Set',
-    'FrozenSet',
-    'NamedTuple',  # Not really a type.
-    'Generator',
-
-    # One-off things.
-    'AnyStr',
-    'cast',
-    'get_type_hints',
-    'NewType',
-    'no_type_check',
-    'no_type_check_decorator',
-    'NoReturn',
-    'overload',
-    'Text',
-    'TYPE_CHECKING',
-]
-
-
-# The pseudo-submodules 're' and 'io' are part of the public
-# namespace, but excluded from __all__ because they might stomp on
-# legitimate imports of those modules.
-
-
-def _qualname(x):
-    if sys.version_info[:2] >= (3, 3):
-        return x.__qualname__
-    else:
-        # Fall back to just name.
-        return x.__name__
-
-
-def _trim_name(nm):
-    whitelist = ('_TypeAlias', '_ForwardRef', '_TypingBase', '_FinalTypingBase')
-    if nm.startswith('_') and nm not in whitelist:
-        nm = nm[1:]
-    return nm
-
-
-class TypingMeta(type):
-    """Metaclass for most types defined in typing module
-    (not a part of public API).
-
-    This overrides __new__() to require an extra keyword parameter
-    '_root', which serves as a guard against naive subclassing of the
-    typing classes.  Any legitimate class defined using a metaclass
-    derived from TypingMeta must pass _root=True.
-
-    This also defines a dummy constructor (all the work for most typing
-    constructs is done in __new__) and a nicer repr().
-    """
-
-    _is_protocol = False
-
-    def __new__(cls, name, bases, namespace, *, _root=False):
-        if not _root:
-            raise TypeError("Cannot subclass %s" %
-                            (', '.join(map(_type_repr, bases)) or '()'))
-        return super().__new__(cls, name, bases, namespace)
-
-    def __init__(self, *args, **kwds):
-        pass
-
-    def _eval_type(self, globalns, localns):
-        """Override this in subclasses to interpret forward references.
-
-        For example, List['C'] is internally stored as
-        List[_ForwardRef('C')], which should evaluate to List[C],
-        where C is an object found in globalns or localns (searching
-        localns first, of course).
-        """
-        return self
-
-    def _get_type_vars(self, tvars):
-        pass
-
-    def __repr__(self):
-        qname = _trim_name(_qualname(self))
-        return '%s.%s' % (self.__module__, qname)
-
-
-class _TypingBase(metaclass=TypingMeta, _root=True):
-    """Internal indicator of special typing constructs."""
-
-    __slots__ = ('__weakref__',)
-
-    def __init__(self, *args, **kwds):
-        pass
-
-    def __new__(cls, *args, **kwds):
-        """Constructor.
-
-        This only exists to give a better error message in case
-        someone tries to subclass a special typing object (not a good idea).
-        """
-        if (len(args) == 3 and
-            isinstance(args[0], str) and
-            isinstance(args[1], tuple)):
-            # Close enough.
-            raise TypeError("Cannot subclass %r" % cls)
-        return super().__new__(cls)
-
-    # Things that are not classes also need these.
-    def _eval_type(self, globalns, localns):
-        return self
-
-    def _get_type_vars(self, tvars):
-        pass
-
-    def __repr__(self):
-        cls = type(self)
-        qname = _trim_name(_qualname(cls))
-        return '%s.%s' % (cls.__module__, qname)
-
-    def __call__(self, *args, **kwds):
-        raise TypeError("Cannot instantiate %r" % type(self))
-
-
-class _FinalTypingBase(_TypingBase, _root=True):
-    """Internal mix-in class to prevent instantiation.
-
-    Prevents instantiation unless _root=True is given in class call.
-    It is used to create pseudo-singleton instances Any, Union, Optional, etc.
-    """
-
-    __slots__ = ()
-
-    def __new__(cls, *args, _root=False, **kwds):
-        self = super().__new__(cls, *args, **kwds)
-        if _root is True:
-            return self
-        raise TypeError("Cannot instantiate %r" % cls)
-
-    def __reduce__(self):
-        return _trim_name(type(self).__name__)
-
-
-class _ForwardRef(_TypingBase, _root=True):
-    """Internal wrapper to hold a forward reference."""
-
-    __slots__ = ('__forward_arg__', '__forward_code__',
-                 '__forward_evaluated__', '__forward_value__')
-
-    def __init__(self, arg):
-        super().__init__(arg)
-        if not isinstance(arg, str):
-            raise TypeError('Forward reference must be a string -- got %r' % (arg,))
-        try:
-            code = compile(arg, '', 'eval')
-        except SyntaxError:
-            raise SyntaxError('Forward reference must be an expression -- got %r' %
-                              (arg,))
-        self.__forward_arg__ = arg
-        self.__forward_code__ = code
-        self.__forward_evaluated__ = False
-        self.__forward_value__ = None
-
-    def _eval_type(self, globalns, localns):
-        if not self.__forward_evaluated__ or localns is not globalns:
-            if globalns is None and localns is None:
-                globalns = localns = {}
-            elif globalns is None:
-                globalns = localns
-            elif localns is None:
-                localns = globalns
-            self.__forward_value__ = _type_check(
-                eval(self.__forward_code__, globalns, localns),
-                "Forward references must evaluate to types.")
-            self.__forward_evaluated__ = True
-        return self.__forward_value__
-
-    def __eq__(self, other):
-        if not isinstance(other, _ForwardRef):
-            return NotImplemented
-        return (self.__forward_arg__ == other.__forward_arg__ and
-                self.__forward_value__ == other.__forward_value__)
-
-    def __hash__(self):
-        return hash((self.__forward_arg__, self.__forward_value__))
-
-    def __instancecheck__(self, obj):
-        raise TypeError("Forward references cannot be used with isinstance().")
-
-    def __subclasscheck__(self, cls):
-        raise TypeError("Forward references cannot be used with issubclass().")
-
-    def __repr__(self):
-        return '_ForwardRef(%r)' % (self.__forward_arg__,)
-
-
-class _TypeAlias(_TypingBase, _root=True):
-    """Internal helper class for defining generic variants of concrete types.
-
-    Note that this is not a type; let's call it a pseudo-type.  It cannot
-    be used in instance and subclass checks in parameterized form, i.e.
-    ``isinstance(42, Match[str])`` raises ``TypeError`` instead of returning
-    ``False``.
-    """
-
-    __slots__ = ('name', 'type_var', 'impl_type', 'type_checker')
-
-    def __init__(self, name, type_var, impl_type, type_checker):
-        """Initializer.
-
-        Args:
-            name: The name, e.g. 'Pattern'.
-            type_var: The type parameter, e.g. AnyStr, or the
-                specific type, e.g. str.
-            impl_type: The implementation type.
-            type_checker: Function that takes an impl_type instance.
-                and returns a value that should be a type_var instance.
-        """
-        assert isinstance(name, str), repr(name)
-        assert isinstance(impl_type, type), repr(impl_type)
-        assert not isinstance(impl_type, TypingMeta), repr(impl_type)
-        assert isinstance(type_var, (type, _TypingBase)), repr(type_var)
-        self.name = name
-        self.type_var = type_var
-        self.impl_type = impl_type
-        self.type_checker = type_checker
-
-    def __repr__(self):
-        return "%s[%s]" % (self.name, _type_repr(self.type_var))
-
-    def __getitem__(self, parameter):
-        if not isinstance(self.type_var, TypeVar):
-            raise TypeError("%s cannot be further parameterized." % self)
-        if self.type_var.__constraints__ and isinstance(parameter, type):
-            if not issubclass(parameter, self.type_var.__constraints__):
-                raise TypeError("%s is not a valid substitution for %s." %
-                                (parameter, self.type_var))
-        if isinstance(parameter, TypeVar) and parameter is not self.type_var:
-            raise TypeError("%s cannot be re-parameterized." % self)
-        return self.__class__(self.name, parameter,
-                              self.impl_type, self.type_checker)
-
-    def __eq__(self, other):
-        if not isinstance(other, _TypeAlias):
-            return NotImplemented
-        return self.name == other.name and self.type_var == other.type_var
-
-    def __hash__(self):
-        return hash((self.name, self.type_var))
-
-    def __instancecheck__(self, obj):
-        if not isinstance(self.type_var, TypeVar):
-            raise TypeError("Parameterized type aliases cannot be used "
-                            "with isinstance().")
-        return isinstance(obj, self.impl_type)
-
-    def __subclasscheck__(self, cls):
-        if not isinstance(self.type_var, TypeVar):
-            raise TypeError("Parameterized type aliases cannot be used "
-                            "with issubclass().")
-        return issubclass(cls, self.impl_type)
-
-
-def _get_type_vars(types, tvars):
-    for t in types:
-        if isinstance(t, TypingMeta) or isinstance(t, _TypingBase):
-            t._get_type_vars(tvars)
-
-
-def _type_vars(types):
-    tvars = []
-    _get_type_vars(types, tvars)
-    return tuple(tvars)
-
-
-def _eval_type(t, globalns, localns):
-    if isinstance(t, TypingMeta) or isinstance(t, _TypingBase):
-        return t._eval_type(globalns, localns)
-    return t
-
-
-def _type_check(arg, msg):
-    """Check that the argument is a type, and return it (internal helper).
-
-    As a special case, accept None and return type(None) instead.
-    Also, _TypeAlias instances (e.g. Match, Pattern) are acceptable.
-
-    The msg argument is a human-readable error message, e.g.
-
-        "Union[arg, ...]: arg should be a type."
-
-    We append the repr() of the actual value (truncated to 100 chars).
-    """
-    if arg is None:
-        return type(None)
-    if isinstance(arg, str):
-        arg = _ForwardRef(arg)
-    if (
-        isinstance(arg, _TypingBase) and type(arg).__name__ == '_ClassVar' or
-        not isinstance(arg, (type, _TypingBase)) and not callable(arg)
-    ):
-        raise TypeError(msg + " Got %.100r." % (arg,))
-    # Bare Union etc. are not valid as type arguments
-    if (
-        type(arg).__name__ in ('_Union', '_Optional') and
-        not getattr(arg, '__origin__', None) or
-        isinstance(arg, TypingMeta) and arg._gorg in (Generic, _Protocol)
-    ):
-        raise TypeError("Plain %s is not valid as type argument" % arg)
-    return arg
-
-
-def _type_repr(obj):
-    """Return the repr() of an object, special-casing types (internal helper).
-
-    If obj is a type, we return a shorter version than the default
-    type.__repr__, based on the module and qualified name, which is
-    typically enough to uniquely identify a type.  For everything
-    else, we fall back on repr(obj).
-    """
-    if isinstance(obj, type) and not isinstance(obj, TypingMeta):
-        if obj.__module__ == 'builtins':
-            return _qualname(obj)
-        return '%s.%s' % (obj.__module__, _qualname(obj))
-    if obj is ...:
-        return ('...')
-    if isinstance(obj, types.FunctionType):
-        return obj.__name__
-    return repr(obj)
-
-
-class _Any(_FinalTypingBase, _root=True):
-    """Special type indicating an unconstrained type.
-
-    - Any is compatible with every type.
-    - Any assumed to have all methods.
-    - All values assumed to be instances of Any.
-
-    Note that all the above statements are true from the point of view of
-    static type checkers. At runtime, Any should not be used with instance
-    or class checks.
-    """
-
-    __slots__ = ()
-
-    def __instancecheck__(self, obj):
-        raise TypeError("Any cannot be used with isinstance().")
-
-    def __subclasscheck__(self, cls):
-        raise TypeError("Any cannot be used with issubclass().")
-
-
-Any = _Any(_root=True)
-
-
-class _NoReturn(_FinalTypingBase, _root=True):
-    """Special type indicating functions that never return.
-    Example::
-
-      from typing import NoReturn
-
-      def stop() -> NoReturn:
-          raise Exception('no way')
-
-    This type is invalid in other positions, e.g., ``List[NoReturn]``
-    will fail in static type checkers.
-    """
-
-    __slots__ = ()
-
-    def __instancecheck__(self, obj):
-        raise TypeError("NoReturn cannot be used with isinstance().")
-
-    def __subclasscheck__(self, cls):
-        raise TypeError("NoReturn cannot be used with issubclass().")
-
-
-NoReturn = _NoReturn(_root=True)
-
-
-class TypeVar(_TypingBase, _root=True):
-    """Type variable.
-
-    Usage::
-
-      T = TypeVar('T')  # Can be anything
-      A = TypeVar('A', str, bytes)  # Must be str or bytes
-
-    Type variables exist primarily for the benefit of static type
-    checkers.  They serve as the parameters for generic types as well
-    as for generic function definitions.  See class Generic for more
-    information on generic types.  Generic functions work as follows:
-
-      def repeat(x: T, n: int) -> List[T]:
-          '''Return a list containing n references to x.'''
-          return [x]*n
-
-      def longest(x: A, y: A) -> A:
-          '''Return the longest of two strings.'''
-          return x if len(x) >= len(y) else y
-
-    The latter example's signature is essentially the overloading
-    of (str, str) -> str and (bytes, bytes) -> bytes.  Also note
-    that if the arguments are instances of some subclass of str,
-    the return type is still plain str.
-
-    At runtime, isinstance(x, T) and issubclass(C, T) will raise TypeError.
-
-    Type variables defined with covariant=True or contravariant=True
-    can be used do declare covariant or contravariant generic types.
-    See PEP 484 for more details. By default generic types are invariant
-    in all type variables.
-
-    Type variables can be introspected. e.g.:
-
-      T.__name__ == 'T'
-      T.__constraints__ == ()
-      T.__covariant__ == False
-      T.__contravariant__ = False
-      A.__constraints__ == (str, bytes)
-    """
-
-    __slots__ = ('__name__', '__bound__', '__constraints__',
-                 '__covariant__', '__contravariant__')
-
-    def __init__(self, name, *constraints, bound=None,
-                 covariant=False, contravariant=False):
-        super().__init__(name, *constraints, bound=bound,
-                         covariant=covariant, contravariant=contravariant)
-        self.__name__ = name
-        if covariant and contravariant:
-            raise ValueError("Bivariant types are not supported.")
-        self.__covariant__ = bool(covariant)
-        self.__contravariant__ = bool(contravariant)
-        if constraints and bound is not None:
-            raise TypeError("Constraints cannot be combined with bound=...")
-        if constraints and len(constraints) == 1:
-            raise TypeError("A single constraint is not allowed")
-        msg = "TypeVar(name, constraint, ...): constraints must be types."
-        self.__constraints__ = tuple(_type_check(t, msg) for t in constraints)
-        if bound:
-            self.__bound__ = _type_check(bound, "Bound must be a type.")
-        else:
-            self.__bound__ = None
-
-    def _get_type_vars(self, tvars):
-        if self not in tvars:
-            tvars.append(self)
-
-    def __repr__(self):
-        if self.__covariant__:
-            prefix = '+'
-        elif self.__contravariant__:
-            prefix = '-'
-        else:
-            prefix = '~'
-        return prefix + self.__name__
-
-    def __instancecheck__(self, instance):
-        raise TypeError("Type variables cannot be used with isinstance().")
-
-    def __subclasscheck__(self, cls):
-        raise TypeError("Type variables cannot be used with issubclass().")
-
-
-# Some unconstrained type variables.  These are used by the container types.
-# (These are not for export.)
-T = TypeVar('T')  # Any type.
-KT = TypeVar('KT')  # Key type.
-VT = TypeVar('VT')  # Value type.
-T_co = TypeVar('T_co', covariant=True)  # Any type covariant containers.
-V_co = TypeVar('V_co', covariant=True)  # Any type covariant containers.
-VT_co = TypeVar('VT_co', covariant=True)  # Value type covariant containers.
-T_contra = TypeVar('T_contra', contravariant=True)  # Ditto contravariant.
-
-# A useful type variable with constraints.  This represents string types.
-# (This one *is* for export!)
-AnyStr = TypeVar('AnyStr', bytes, str)
-
-
-def _replace_arg(arg, tvars, args):
-    """An internal helper function: replace arg if it is a type variable
-    found in tvars with corresponding substitution from args or
-    with corresponding substitution sub-tree if arg is a generic type.
-    """
-
-    if tvars is None:
-        tvars = []
-    if hasattr(arg, '_subs_tree') and isinstance(arg, (GenericMeta, _TypingBase)):
-        return arg._subs_tree(tvars, args)
-    if isinstance(arg, TypeVar):
-        for i, tvar in enumerate(tvars):
-            if arg == tvar:
-                return args[i]
-    return arg
-
-
-# Special typing constructs Union, Optional, Generic, Callable and Tuple
-# use three special attributes for internal bookkeeping of generic types:
-# * __parameters__ is a tuple of unique free type parameters of a generic
-#   type, for example, Dict[T, T].__parameters__ == (T,);
-# * __origin__ keeps a reference to a type that was subscripted,
-#   e.g., Union[T, int].__origin__ == Union;
-# * __args__ is a tuple of all arguments used in subscripting,
-#   e.g., Dict[T, int].__args__ == (T, int).
-
-
-def _subs_tree(cls, tvars=None, args=None):
-    """An internal helper function: calculate substitution tree
-    for generic cls after replacing its type parameters with
-    substitutions in tvars -> args (if any).
-    Repeat the same following __origin__'s.
-
-    Return a list of arguments with all possible substitutions
-    performed. Arguments that are generic classes themselves are represented
-    as tuples (so that no new classes are created by this function).
-    For example: _subs_tree(List[Tuple[int, T]][str]) == [(Tuple, int, str)]
-    """
-
-    if cls.__origin__ is None:
-        return cls
-    # Make of chain of origins (i.e. cls -> cls.__origin__)
-    current = cls.__origin__
-    orig_chain = []
-    while current.__origin__ is not None:
-        orig_chain.append(current)
-        current = current.__origin__
-    # Replace type variables in __args__ if asked ...
-    tree_args = []
-    for arg in cls.__args__:
-        tree_args.append(_replace_arg(arg, tvars, args))
-    # ... then continue replacing down the origin chain.
-    for ocls in orig_chain:
-        new_tree_args = []
-        for arg in ocls.__args__:
-            new_tree_args.append(_replace_arg(arg, ocls.__parameters__, tree_args))
-        tree_args = new_tree_args
-    return tree_args
-
-
-def _remove_dups_flatten(parameters):
-    """An internal helper for Union creation and substitution: flatten Union's
-    among parameters, then remove duplicates and strict subclasses.
-    """
-
-    # Flatten out Union[Union[...], ...].
-    params = []
-    for p in parameters:
-        if isinstance(p, _Union) and p.__origin__ is Union:
-            params.extend(p.__args__)
-        elif isinstance(p, tuple) and len(p) > 0 and p[0] is Union:
-            params.extend(p[1:])
-        else:
-            params.append(p)
-    # Weed out strict duplicates, preserving the first of each occurrence.
-    all_params = set(params)
-    if len(all_params) < len(params):
-        new_params = []
-        for t in params:
-            if t in all_params:
-                new_params.append(t)
-                all_params.remove(t)
-        params = new_params
-        assert not all_params, all_params
-    # Weed out subclasses.
-    # E.g. Union[int, Employee, Manager] == Union[int, Employee].
-    # If object is present it will be sole survivor among proper classes.
-    # Never discard type variables.
-    # (In particular, Union[str, AnyStr] != AnyStr.)
-    all_params = set(params)
-    for t1 in params:
-        if not isinstance(t1, type):
-            continue
-        if any(isinstance(t2, type) and issubclass(t1, t2)
-               for t2 in all_params - {t1}
-               if not (isinstance(t2, GenericMeta) and
-                       t2.__origin__ is not None)):
-            all_params.remove(t1)
-    return tuple(t for t in params if t in all_params)
-
-
-def _check_generic(cls, parameters):
-    # Check correct count for parameters of a generic cls (internal helper).
-    if not cls.__parameters__:
-        raise TypeError("%s is not a generic class" % repr(cls))
-    alen = len(parameters)
-    elen = len(cls.__parameters__)
-    if alen != elen:
-        raise TypeError("Too %s parameters for %s; actual %s, expected %s" %
-                        ("many" if alen > elen else "few", repr(cls), alen, elen))
-
-
-_cleanups = []
-
-
-def _tp_cache(func):
-    """Internal wrapper caching __getitem__ of generic types with a fallback to
-    original function for non-hashable arguments.
-    """
-
-    cached = functools.lru_cache()(func)
-    _cleanups.append(cached.cache_clear)
-
-    @functools.wraps(func)
-    def inner(*args, **kwds):
-        try:
-            return cached(*args, **kwds)
-        except TypeError:
-            pass  # All real errors (not unhashable args) are raised below.
-        return func(*args, **kwds)
-
-    return inner
-
-
-class _Union(_FinalTypingBase, _root=True):
-    """Union type; Union[X, Y] means either X or Y.
-
-    To define a union, use e.g. Union[int, str].  Details:
-
-    - The arguments must be types and there must be at least one.
-
-    - None as an argument is a special case and is replaced by
-      type(None).
-
-    - Unions of unions are flattened, e.g.::
-
-        Union[Union[int, str], float] == Union[int, str, float]
-
-    - Unions of a single argument vanish, e.g.::
-
-        Union[int] == int  # The constructor actually returns int
-
-    - Redundant arguments are skipped, e.g.::
-
-        Union[int, str, int] == Union[int, str]
-
-    - When comparing unions, the argument order is ignored, e.g.::
-
-        Union[int, str] == Union[str, int]
-
-    - When two arguments have a subclass relationship, the least
-      derived argument is kept, e.g.::
-
-        class Employee: pass
-        class Manager(Employee): pass
-        Union[int, Employee, Manager] == Union[int, Employee]
-        Union[Manager, int, Employee] == Union[int, Employee]
-        Union[Employee, Manager] == Employee
-
-    - Similar for object::
-
-        Union[int, object] == object
-
-    - You cannot subclass or instantiate a union.
-
-    - You can use Optional[X] as a shorthand for Union[X, None].
-    """
-
-    __slots__ = ('__parameters__', '__args__', '__origin__', '__tree_hash__')
-
-    def __new__(cls, parameters=None, origin=None, *args, _root=False):
-        self = super().__new__(cls, parameters, origin, *args, _root=_root)
-        if origin is None:
-            self.__parameters__ = None
-            self.__args__ = None
-            self.__origin__ = None
-            self.__tree_hash__ = hash(frozenset(('Union',)))
-            return self
-        if not isinstance(parameters, tuple):
-            raise TypeError("Expected parameters=")
-        if origin is Union:
-            parameters = _remove_dups_flatten(parameters)
-            # It's not a union if there's only one type left.
-            if len(parameters) == 1:
-                return parameters[0]
-        self.__parameters__ = _type_vars(parameters)
-        self.__args__ = parameters
-        self.__origin__ = origin
-        # Pre-calculate the __hash__ on instantiation.
-        # This improves speed for complex substitutions.
-        subs_tree = self._subs_tree()
-        if isinstance(subs_tree, tuple):
-            self.__tree_hash__ = hash(frozenset(subs_tree))
-        else:
-            self.__tree_hash__ = hash(subs_tree)
-        return self
-
-    def _eval_type(self, globalns, localns):
-        if self.__args__ is None:
-            return self
-        ev_args = tuple(_eval_type(t, globalns, localns) for t in self.__args__)
-        ev_origin = _eval_type(self.__origin__, globalns, localns)
-        if ev_args == self.__args__ and ev_origin == self.__origin__:
-            # Everything is already evaluated.
-            return self
-        return self.__class__(ev_args, ev_origin, _root=True)
-
-    def _get_type_vars(self, tvars):
-        if self.__origin__ and self.__parameters__:
-            _get_type_vars(self.__parameters__, tvars)
-
-    def __repr__(self):
-        if self.__origin__ is None:
-            return super().__repr__()
-        tree = self._subs_tree()
-        if not isinstance(tree, tuple):
-            return repr(tree)
-        return tree[0]._tree_repr(tree)
-
-    def _tree_repr(self, tree):
-        arg_list = []
-        for arg in tree[1:]:
-            if not isinstance(arg, tuple):
-                arg_list.append(_type_repr(arg))
-            else:
-                arg_list.append(arg[0]._tree_repr(arg))
-        return super().__repr__() + '[%s]' % ', '.join(arg_list)
-
-    @_tp_cache
-    def __getitem__(self, parameters):
-        if parameters == ():
-            raise TypeError("Cannot take a Union of no types.")
-        if not isinstance(parameters, tuple):
-            parameters = (parameters,)
-        if self.__origin__ is None:
-            msg = "Union[arg, ...]: each arg must be a type."
-        else:
-            msg = "Parameters to generic types must be types."
-        parameters = tuple(_type_check(p, msg) for p in parameters)
-        if self is not Union:
-            _check_generic(self, parameters)
-        return self.__class__(parameters, origin=self, _root=True)
-
-    def _subs_tree(self, tvars=None, args=None):
-        if self is Union:
-            return Union  # Nothing to substitute
-        tree_args = _subs_tree(self, tvars, args)
-        tree_args = _remove_dups_flatten(tree_args)
-        if len(tree_args) == 1:
-            return tree_args[0]  # Union of a single type is that type
-        return (Union,) + tree_args
-
-    def __eq__(self, other):
-        if isinstance(other, _Union):
-            return self.__tree_hash__ == other.__tree_hash__
-        elif self is not Union:
-            return self._subs_tree() == other
-        else:
-            return self is other
-
-    def __hash__(self):
-        return self.__tree_hash__
-
-    def __instancecheck__(self, obj):
-        raise TypeError("Unions cannot be used with isinstance().")
-
-    def __subclasscheck__(self, cls):
-        raise TypeError("Unions cannot be used with issubclass().")
-
-
-Union = _Union(_root=True)
-
-
-class _Optional(_FinalTypingBase, _root=True):
-    """Optional type.
-
-    Optional[X] is equivalent to Union[X, None].
-    """
-
-    __slots__ = ()
-
-    @_tp_cache
-    def __getitem__(self, arg):
-        arg = _type_check(arg, "Optional[t] requires a single type.")
-        return Union[arg, type(None)]
-
-
-Optional = _Optional(_root=True)
-
-
-def _next_in_mro(cls):
-    """Helper for Generic.__new__.
-
-    Returns the class after the last occurrence of Generic or
-    Generic[...] in cls.__mro__.
-    """
-    next_in_mro = object
-    # Look for the last occurrence of Generic or Generic[...].
-    for i, c in enumerate(cls.__mro__[:-1]):
-        if isinstance(c, GenericMeta) and c._gorg is Generic:
-            next_in_mro = cls.__mro__[i + 1]
-    return next_in_mro
-
-
-def _make_subclasshook(cls):
-    """Construct a __subclasshook__ callable that incorporates
-    the associated __extra__ class in subclass checks performed
-    against cls.
-    """
-    if isinstance(cls.__extra__, abc.ABCMeta):
-        # The logic mirrors that of ABCMeta.__subclasscheck__.
-        # Registered classes need not be checked here because
-        # cls and its extra share the same _abc_registry.
-        def __extrahook__(subclass):
-            res = cls.__extra__.__subclasshook__(subclass)
-            if res is not NotImplemented:
-                return res
-            if cls.__extra__ in subclass.__mro__:
-                return True
-            for scls in cls.__extra__.__subclasses__():
-                if isinstance(scls, GenericMeta):
-                    continue
-                if issubclass(subclass, scls):
-                    return True
-            return NotImplemented
-    else:
-        # For non-ABC extras we'll just call issubclass().
-        def __extrahook__(subclass):
-            if cls.__extra__ and issubclass(subclass, cls.__extra__):
-                return True
-            return NotImplemented
-    return __extrahook__
-
-
-def _no_slots_copy(dct):
-    """Internal helper: copy class __dict__ and clean slots class variables.
-    (They will be re-created if necessary by normal class machinery.)
-    """
-    dict_copy = dict(dct)
-    if '__slots__' in dict_copy:
-        for slot in dict_copy['__slots__']:
-            dict_copy.pop(slot, None)
-    return dict_copy
-
-
-class GenericMeta(TypingMeta, abc.ABCMeta):
-    """Metaclass for generic types.
-
-    This is a metaclass for typing.Generic and generic ABCs defined in
-    typing module. User defined subclasses of GenericMeta can override
-    __new__ and invoke super().__new__. Note that GenericMeta.__new__
-    has strict rules on what is allowed in its bases argument:
-    * plain Generic is disallowed in bases;
-    * Generic[...] should appear in bases at most once;
-    * if Generic[...] is present, then it should list all type variables
-      that appear in other bases.
-    In addition, type of all generic bases is erased, e.g., C[int] is
-    stripped to plain C.
-    """
-
-    def __new__(cls, name, bases, namespace,
-                tvars=None, args=None, origin=None, extra=None, orig_bases=None):
-        """Create a new generic class. GenericMeta.__new__ accepts
-        keyword arguments that are used for internal bookkeeping, therefore
-        an override should pass unused keyword arguments to super().
-        """
-        if tvars is not None:
-            # Called from __getitem__() below.
-            assert origin is not None
-            assert all(isinstance(t, TypeVar) for t in tvars), tvars
-        else:
-            # Called from class statement.
-            assert tvars is None, tvars
-            assert args is None, args
-            assert origin is None, origin
-
-            # Get the full set of tvars from the bases.
-            tvars = _type_vars(bases)
-            # Look for Generic[T1, ..., Tn].
-            # If found, tvars must be a subset of it.
-            # If not found, tvars is it.
-            # Also check for and reject plain Generic,
-            # and reject multiple Generic[...].
-            gvars = None
-            for base in bases:
-                if base is Generic:
-                    raise TypeError("Cannot inherit from plain Generic")
-                if (isinstance(base, GenericMeta) and
-                    base.__origin__ is Generic):
-                    if gvars is not None:
-                        raise TypeError(
-                            "Cannot inherit from Generic[...] multiple types.")
-                    gvars = base.__parameters__
-            if gvars is None:
-                gvars = tvars
-            else:
-                tvarset = set(tvars)
-                gvarset = set(gvars)
-                if not tvarset <= gvarset:
-                    raise TypeError(
-                        "Some type variables (%s) "
-                        "are not listed in Generic[%s]" %
-                        (", ".join(str(t) for t in tvars if t not in gvarset),
-                         ", ".join(str(g) for g in gvars)))
-                tvars = gvars
-
-        initial_bases = bases
-        if extra is not None and type(extra) is abc.ABCMeta and extra not in bases:
-            bases = (extra,) + bases
-        bases = tuple(b._gorg if isinstance(b, GenericMeta) else b for b in bases)
-
-        # remove bare Generic from bases if there are other generic bases
-        if any(isinstance(b, GenericMeta) and b is not Generic for b in bases):
-            bases = tuple(b for b in bases if b is not Generic)
-        namespace.update({'__origin__': origin, '__extra__': extra,
-                          '_gorg': None if not origin else origin._gorg})
-        self = super().__new__(cls, name, bases, namespace, _root=True)
-        super(GenericMeta, self).__setattr__('_gorg',
-                                             self if not origin else origin._gorg)
-        self.__parameters__ = tvars
-        # Be prepared that GenericMeta will be subclassed by TupleMeta
-        # and CallableMeta, those two allow ..., (), or [] in __args___.
-        self.__args__ = tuple(... if a is _TypingEllipsis else
-                              () if a is _TypingEmpty else
-                              a for a in args) if args else None
-        # Speed hack (https://github.com/python/typing/issues/196).
-        self.__next_in_mro__ = _next_in_mro(self)
-        # Preserve base classes on subclassing (__bases__ are type erased now).
-        if orig_bases is None:
-            self.__orig_bases__ = initial_bases
-
-        # This allows unparameterized generic collections to be used
-        # with issubclass() and isinstance() in the same way as their
-        # collections.abc counterparts (e.g., isinstance([], Iterable)).
-        if (
-            '__subclasshook__' not in namespace and extra or
-            # allow overriding
-            getattr(self.__subclasshook__, '__name__', '') == '__extrahook__'
-        ):
-            self.__subclasshook__ = _make_subclasshook(self)
-        if isinstance(extra, abc.ABCMeta):
-            self._abc_registry = extra._abc_registry
-            self._abc_cache = extra._abc_cache
-        elif origin is not None:
-            self._abc_registry = origin._abc_registry
-            self._abc_cache = origin._abc_cache
-
-        if origin and hasattr(origin, '__qualname__'):  # Fix for Python 3.2.
-            self.__qualname__ = origin.__qualname__
-        self.__tree_hash__ = (hash(self._subs_tree()) if origin else
-                              super(GenericMeta, self).__hash__())
-        return self
-
-    # _abc_negative_cache and _abc_negative_cache_version
-    # realised as descriptors, since GenClass[t1, t2, ...] always
-    # share subclass info with GenClass.
-    # This is an important memory optimization.
-    @property
-    def _abc_negative_cache(self):
-        if isinstance(self.__extra__, abc.ABCMeta):
-            return self.__extra__._abc_negative_cache
-        return self._gorg._abc_generic_negative_cache
-
-    @_abc_negative_cache.setter
-    def _abc_negative_cache(self, value):
-        if self.__origin__ is None:
-            if isinstance(self.__extra__, abc.ABCMeta):
-                self.__extra__._abc_negative_cache = value
-            else:
-                self._abc_generic_negative_cache = value
-
-    @property
-    def _abc_negative_cache_version(self):
-        if isinstance(self.__extra__, abc.ABCMeta):
-            return self.__extra__._abc_negative_cache_version
-        return self._gorg._abc_generic_negative_cache_version
-
-    @_abc_negative_cache_version.setter
-    def _abc_negative_cache_version(self, value):
-        if self.__origin__ is None:
-            if isinstance(self.__extra__, abc.ABCMeta):
-                self.__extra__._abc_negative_cache_version = value
-            else:
-                self._abc_generic_negative_cache_version = value
-
-    def _get_type_vars(self, tvars):
-        if self.__origin__ and self.__parameters__:
-            _get_type_vars(self.__parameters__, tvars)
-
-    def _eval_type(self, globalns, localns):
-        ev_origin = (self.__origin__._eval_type(globalns, localns)
-                     if self.__origin__ else None)
-        ev_args = tuple(_eval_type(a, globalns, localns) for a
-                        in self.__args__) if self.__args__ else None
-        if ev_origin == self.__origin__ and ev_args == self.__args__:
-            return self
-        return self.__class__(self.__name__,
-                              self.__bases__,
-                              _no_slots_copy(self.__dict__),
-                              tvars=_type_vars(ev_args) if ev_args else None,
-                              args=ev_args,
-                              origin=ev_origin,
-                              extra=self.__extra__,
-                              orig_bases=self.__orig_bases__)
-
-    def __repr__(self):
-        if self.__origin__ is None:
-            return super().__repr__()
-        return self._tree_repr(self._subs_tree())
-
-    def _tree_repr(self, tree):
-        arg_list = []
-        for arg in tree[1:]:
-            if arg == ():
-                arg_list.append('()')
-            elif not isinstance(arg, tuple):
-                arg_list.append(_type_repr(arg))
-            else:
-                arg_list.append(arg[0]._tree_repr(arg))
-        return super().__repr__() + '[%s]' % ', '.join(arg_list)
-
-    def _subs_tree(self, tvars=None, args=None):
-        if self.__origin__ is None:
-            return self
-        tree_args = _subs_tree(self, tvars, args)
-        return (self._gorg,) + tuple(tree_args)
-
-    def __eq__(self, other):
-        if not isinstance(other, GenericMeta):
-            return NotImplemented
-        if self.__origin__ is None or other.__origin__ is None:
-            return self is other
-        return self.__tree_hash__ == other.__tree_hash__
-
-    def __hash__(self):
-        return self.__tree_hash__
-
-    @_tp_cache
-    def __getitem__(self, params):
-        if not isinstance(params, tuple):
-            params = (params,)
-        if not params and self._gorg is not Tuple:
-            raise TypeError(
-                "Parameter list to %s[...] cannot be empty" % _qualname(self))
-        msg = "Parameters to generic types must be types."
-        params = tuple(_type_check(p, msg) for p in params)
-        if self is Generic:
-            # Generic can only be subscripted with unique type variables.
-            if not all(isinstance(p, TypeVar) for p in params):
-                raise TypeError(
-                    "Parameters to Generic[...] must all be type variables")
-            if len(set(params)) != len(params):
-                raise TypeError(
-                    "Parameters to Generic[...] must all be unique")
-            tvars = params
-            args = params
-        elif self in (Tuple, Callable):
-            tvars = _type_vars(params)
-            args = params
-        elif self is _Protocol:
-            # _Protocol is internal, don't check anything.
-            tvars = params
-            args = params
-        elif self.__origin__ in (Generic, _Protocol):
-            # Can't subscript Generic[...] or _Protocol[...].
-            raise TypeError("Cannot subscript already-subscripted %s" %
-                            repr(self))
-        else:
-            # Subscripting a regular Generic subclass.
-            _check_generic(self, params)
-            tvars = _type_vars(params)
-            args = params
-
-        prepend = (self,) if self.__origin__ is None else ()
-        return self.__class__(self.__name__,
-                              prepend + self.__bases__,
-                              _no_slots_copy(self.__dict__),
-                              tvars=tvars,
-                              args=args,
-                              origin=self,
-                              extra=self.__extra__,
-                              orig_bases=self.__orig_bases__)
-
-    def __subclasscheck__(self, cls):
-        if self.__origin__ is not None:
-            if sys._getframe(1).f_globals['__name__'] not in ['abc', 'functools']:
-                raise TypeError("Parameterized generics cannot be used with class "
-                                "or instance checks")
-            return False
-        if self is Generic:
-            raise TypeError("Class %r cannot be used with class "
-                            "or instance checks" % self)
-        return super().__subclasscheck__(cls)
-
-    def __instancecheck__(self, instance):
-        # Since we extend ABC.__subclasscheck__ and
-        # ABC.__instancecheck__ inlines the cache checking done by the
-        # latter, we must extend __instancecheck__ too. For simplicity
-        # we just skip the cache check -- instance checks for generic
-        # classes are supposed to be rare anyways.
-        return issubclass(instance.__class__, self)
-
-    def __setattr__(self, attr, value):
-        # We consider all the subscripted generics as proxies for original class
-        if (
-            attr.startswith('__') and attr.endswith('__') or
-            attr.startswith('_abc_') or
-            self._gorg is None  # The class is not fully created, see #typing/506
-        ):
-            super(GenericMeta, self).__setattr__(attr, value)
-        else:
-            super(GenericMeta, self._gorg).__setattr__(attr, value)
-
-
-# Prevent checks for Generic to crash when defining Generic.
-Generic = None
-
-
-def _generic_new(base_cls, cls, *args, **kwds):
-    # Assure type is erased on instantiation,
-    # but attempt to store it in __orig_class__
-    if cls.__origin__ is None:
-        if (base_cls.__new__ is object.__new__ and
-            cls.__init__ is not object.__init__):
-            return base_cls.__new__(cls)
-        else:
-            return base_cls.__new__(cls, *args, **kwds)
-    else:
-        origin = cls._gorg
-        if (base_cls.__new__ is object.__new__ and
-            cls.__init__ is not object.__init__):
-            obj = base_cls.__new__(origin)
-        else:
-            obj = base_cls.__new__(origin, *args, **kwds)
-        try:
-            obj.__orig_class__ = cls
-        except AttributeError:
-            pass
-        obj.__init__(*args, **kwds)
-        return obj
-
-
-class Generic(metaclass=GenericMeta):
-    """Abstract base class for generic types.
-
-    A generic type is typically declared by inheriting from
-    this class parameterized with one or more type variables.
-    For example, a generic mapping type might be defined as::
-
-      class Mapping(Generic[KT, VT]):
-          def __getitem__(self, key: KT) -> VT:
-              ...
-          # Etc.
-
-    This class can then be used as follows::
-
-      def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT:
-          try:
-              return mapping[key]
-          except KeyError:
-              return default
-    """
-
-    __slots__ = ()
-
-    def __new__(cls, *args, **kwds):
-        if cls._gorg is Generic:
-            raise TypeError("Type Generic cannot be instantiated; "
-                            "it can be used only as a base class")
-        return _generic_new(cls.__next_in_mro__, cls, *args, **kwds)
-
-
-class _TypingEmpty:
-    """Internal placeholder for () or []. Used by TupleMeta and CallableMeta
-    to allow empty list/tuple in specific places, without allowing them
-    to sneak in where prohibited.
-    """
-
-
-class _TypingEllipsis:
-    """Internal placeholder for ... (ellipsis)."""
-
-
-class TupleMeta(GenericMeta):
-    """Metaclass for Tuple (internal)."""
-
-    @_tp_cache
-    def __getitem__(self, parameters):
-        if self.__origin__ is not None or self._gorg is not Tuple:
-            # Normal generic rules apply if this is not the first subscription
-            # or a subscription of a subclass.
-            return super().__getitem__(parameters)
-        if parameters == ():
-            return super().__getitem__((_TypingEmpty,))
-        if not isinstance(parameters, tuple):
-            parameters = (parameters,)
-        if len(parameters) == 2 and parameters[1] is ...:
-            msg = "Tuple[t, ...]: t must be a type."
-            p = _type_check(parameters[0], msg)
-            return super().__getitem__((p, _TypingEllipsis))
-        msg = "Tuple[t0, t1, ...]: each t must be a type."
-        parameters = tuple(_type_check(p, msg) for p in parameters)
-        return super().__getitem__(parameters)
-
-    def __instancecheck__(self, obj):
-        if self.__args__ is None:
-            return isinstance(obj, tuple)
-        raise TypeError("Parameterized Tuple cannot be used "
-                        "with isinstance().")
-
-    def __subclasscheck__(self, cls):
-        if self.__args__ is None:
-            return issubclass(cls, tuple)
-        raise TypeError("Parameterized Tuple cannot be used "
-                        "with issubclass().")
-
-
-class Tuple(tuple, extra=tuple, metaclass=TupleMeta):
-    """Tuple type; Tuple[X, Y] is the cross-product type of X and Y.
-
-    Example: Tuple[T1, T2] is a tuple of two elements corresponding
-    to type variables T1 and T2.  Tuple[int, float, str] is a tuple
-    of an int, a float and a string.
-
-    To specify a variable-length tuple of homogeneous type, use Tuple[T, ...].
-    """
-
-    __slots__ = ()
-
-    def __new__(cls, *args, **kwds):
-        if cls._gorg is Tuple:
-            raise TypeError("Type Tuple cannot be instantiated; "
-                            "use tuple() instead")
-        return _generic_new(tuple, cls, *args, **kwds)
-
-
-class CallableMeta(GenericMeta):
-    """Metaclass for Callable (internal)."""
-
-    def __repr__(self):
-        if self.__origin__ is None:
-            return super().__repr__()
-        return self._tree_repr(self._subs_tree())
-
-    def _tree_repr(self, tree):
-        if self._gorg is not Callable:
-            return super()._tree_repr(tree)
-        # For actual Callable (not its subclass) we override
-        # super()._tree_repr() for nice formatting.
-        arg_list = []
-        for arg in tree[1:]:
-            if not isinstance(arg, tuple):
-                arg_list.append(_type_repr(arg))
-            else:
-                arg_list.append(arg[0]._tree_repr(arg))
-        if arg_list[0] == '...':
-            return repr(tree[0]) + '[..., %s]' % arg_list[1]
-        return (repr(tree[0]) +
-                '[[%s], %s]' % (', '.join(arg_list[:-1]), arg_list[-1]))
-
-    def __getitem__(self, parameters):
-        """A thin wrapper around __getitem_inner__ to provide the latter
-        with hashable arguments to improve speed.
-        """
-
-        if self.__origin__ is not None or self._gorg is not Callable:
-            return super().__getitem__(parameters)
-        if not isinstance(parameters, tuple) or len(parameters) != 2:
-            raise TypeError("Callable must be used as "
-                            "Callable[[arg, ...], result].")
-        args, result = parameters
-        if args is Ellipsis:
-            parameters = (Ellipsis, result)
-        else:
-            if not isinstance(args, list):
-                raise TypeError("Callable[args, result]: args must be a list."
-                                " Got %.100r." % (args,))
-            parameters = (tuple(args), result)
-        return self.__getitem_inner__(parameters)
-
-    @_tp_cache
-    def __getitem_inner__(self, parameters):
-        args, result = parameters
-        msg = "Callable[args, result]: result must be a type."
-        result = _type_check(result, msg)
-        if args is Ellipsis:
-            return super().__getitem__((_TypingEllipsis, result))
-        msg = "Callable[[arg, ...], result]: each arg must be a type."
-        args = tuple(_type_check(arg, msg) for arg in args)
-        parameters = args + (result,)
-        return super().__getitem__(parameters)
-
-
-class Callable(extra=collections_abc.Callable, metaclass=CallableMeta):
-    """Callable type; Callable[[int], str] is a function of (int) -> str.
-
-    The subscription syntax must always be used with exactly two
-    values: the argument list and the return type.  The argument list
-    must be a list of types or ellipsis; the return type must be a single type.
-
-    There is no syntax to indicate optional or keyword arguments,
-    such function types are rarely used as callback types.
-    """
-
-    __slots__ = ()
-
-    def __new__(cls, *args, **kwds):
-        if cls._gorg is Callable:
-            raise TypeError("Type Callable cannot be instantiated; "
-                            "use a non-abstract subclass instead")
-        return _generic_new(cls.__next_in_mro__, cls, *args, **kwds)
-
-
-class _ClassVar(_FinalTypingBase, _root=True):
-    """Special type construct to mark class variables.
-
-    An annotation wrapped in ClassVar indicates that a given
-    attribute is intended to be used as a class variable and
-    should not be set on instances of that class. Usage::
-
-      class Starship:
-          stats: ClassVar[Dict[str, int]] = {} # class variable
-          damage: int = 10                     # instance variable
-
-    ClassVar accepts only types and cannot be further subscribed.
-
-    Note that ClassVar is not a class itself, and should not
-    be used with isinstance() or issubclass().
-    """
-
-    __slots__ = ('__type__',)
-
-    def __init__(self, tp=None, **kwds):
-        self.__type__ = tp
-
-    def __getitem__(self, item):
-        cls = type(self)
-        if self.__type__ is None:
-            return cls(_type_check(item,
-                                   '{} accepts only single type.'.format(cls.__name__[1:])),
-                       _root=True)
-        raise TypeError('{} cannot be further subscripted'
-                        .format(cls.__name__[1:]))
-
-    def _eval_type(self, globalns, localns):
-        new_tp = _eval_type(self.__type__, globalns, localns)
-        if new_tp == self.__type__:
-            return self
-        return type(self)(new_tp, _root=True)
-
-    def __repr__(self):
-        r = super().__repr__()
-        if self.__type__ is not None:
-            r += '[{}]'.format(_type_repr(self.__type__))
-        return r
-
-    def __hash__(self):
-        return hash((type(self).__name__, self.__type__))
-
-    def __eq__(self, other):
-        if not isinstance(other, _ClassVar):
-            return NotImplemented
-        if self.__type__ is not None:
-            return self.__type__ == other.__type__
-        return self is other
-
-
-ClassVar = _ClassVar(_root=True)
-
-
-def cast(typ, val):
-    """Cast a value to a type.
-
-    This returns the value unchanged.  To the type checker this
-    signals that the return value has the designated type, but at
-    runtime we intentionally don't check anything (we want this
-    to be as fast as possible).
-    """
-    return val
-
-
-def _get_defaults(func):
-    """Internal helper to extract the default arguments, by name."""
-    try:
-        code = func.__code__
-    except AttributeError:
-        # Some built-in functions don't have __code__, __defaults__, etc.
-        return {}
-    pos_count = code.co_argcount
-    arg_names = code.co_varnames
-    arg_names = arg_names[:pos_count]
-    defaults = func.__defaults__ or ()
-    kwdefaults = func.__kwdefaults__
-    res = dict(kwdefaults) if kwdefaults else {}
-    pos_offset = pos_count - len(defaults)
-    for name, value in zip(arg_names[pos_offset:], defaults):
-        assert name not in res
-        res[name] = value
-    return res
-
-
-_allowed_types = (types.FunctionType, types.BuiltinFunctionType,
-                  types.MethodType, types.ModuleType,
-                  WrapperDescriptorType, MethodWrapperType, MethodDescriptorType)
-
-
-def get_type_hints(obj, globalns=None, localns=None):
-    """Return type hints for an object.
-
-    This is often the same as obj.__annotations__, but it handles
-    forward references encoded as string literals, and if necessary
-    adds Optional[t] if a default value equal to None is set.
-
-    The argument may be a module, class, method, or function. The annotations
-    are returned as a dictionary. For classes, annotations include also
-    inherited members.
-
-    TypeError is raised if the argument is not of a type that can contain
-    annotations, and an empty dictionary is returned if no annotations are
-    present.
-
-    BEWARE -- the behavior of globalns and localns is counterintuitive
-    (unless you are familiar with how eval() and exec() work).  The
-    search order is locals first, then globals.
-
-    - If no dict arguments are passed, an attempt is made to use the
-      globals from obj (or the respective module's globals for classes),
-      and these are also used as the locals.  If the object does not appear
-      to have globals, an empty dictionary is used.
-
-    - If one dict argument is passed, it is used for both globals and
-      locals.
-
-    - If two dict arguments are passed, they specify globals and
-      locals, respectively.
-    """
-
-    if getattr(obj, '__no_type_check__', None):
-        return {}
-    # Classes require a special treatment.
-    if isinstance(obj, type):
-        hints = {}
-        for base in reversed(obj.__mro__):
-            if globalns is None:
-                base_globals = sys.modules[base.__module__].__dict__
-            else:
-                base_globals = globalns
-            ann = base.__dict__.get('__annotations__', {})
-            for name, value in ann.items():
-                if value is None:
-                    value = type(None)
-                if isinstance(value, str):
-                    value = _ForwardRef(value)
-                value = _eval_type(value, base_globals, localns)
-                hints[name] = value
-        return hints
-
-    if globalns is None:
-        if isinstance(obj, types.ModuleType):
-            globalns = obj.__dict__
-        else:
-            globalns = getattr(obj, '__globals__', {})
-        if localns is None:
-            localns = globalns
-    elif localns is None:
-        localns = globalns
-    hints = getattr(obj, '__annotations__', None)
-    if hints is None:
-        # Return empty annotations for something that _could_ have them.
-        if isinstance(obj, _allowed_types):
-            return {}
-        else:
-            raise TypeError('{!r} is not a module, class, method, '
-                            'or function.'.format(obj))
-    defaults = _get_defaults(obj)
-    hints = dict(hints)
-    for name, value in hints.items():
-        if value is None:
-            value = type(None)
-        if isinstance(value, str):
-            value = _ForwardRef(value)
-        value = _eval_type(value, globalns, localns)
-        if name in defaults and defaults[name] is None:
-            value = Optional[value]
-        hints[name] = value
-    return hints
-
-
-def no_type_check(arg):
-    """Decorator to indicate that annotations are not type hints.
-
-    The argument must be a class or function; if it is a class, it
-    applies recursively to all methods and classes defined in that class
-    (but not to methods defined in its superclasses or subclasses).
-
-    This mutates the function(s) or class(es) in place.
-    """
-    if isinstance(arg, type):
-        arg_attrs = arg.__dict__.copy()
-        for attr, val in arg.__dict__.items():
-            if val in arg.__bases__ + (arg,):
-                arg_attrs.pop(attr)
-        for obj in arg_attrs.values():
-            if isinstance(obj, types.FunctionType):
-                obj.__no_type_check__ = True
-            if isinstance(obj, type):
-                no_type_check(obj)
-    try:
-        arg.__no_type_check__ = True
-    except TypeError:  # built-in classes
-        pass
-    return arg
-
-
-def no_type_check_decorator(decorator):
-    """Decorator to give another decorator the @no_type_check effect.
-
-    This wraps the decorator with something that wraps the decorated
-    function in @no_type_check.
-    """
-
-    @functools.wraps(decorator)
-    def wrapped_decorator(*args, **kwds):
-        func = decorator(*args, **kwds)
-        func = no_type_check(func)
-        return func
-
-    return wrapped_decorator
-
-
-def _overload_dummy(*args, **kwds):
-    """Helper for @overload to raise when called."""
-    raise NotImplementedError(
-        "You should not call an overloaded function. "
-        "A series of @overload-decorated functions "
-        "outside a stub module should always be followed "
-        "by an implementation that is not @overload-ed.")
-
-
-def overload(func):
-    """Decorator for overloaded functions/methods.
-
-    In a stub file, place two or more stub definitions for the same
-    function in a row, each decorated with @overload.  For example:
-
-      @overload
-      def utf8(value: None) -> None: ...
-      @overload
-      def utf8(value: bytes) -> bytes: ...
-      @overload
-      def utf8(value: str) -> bytes: ...
-
-    In a non-stub file (i.e. a regular .py file), do the same but
-    follow it with an implementation.  The implementation should *not*
-    be decorated with @overload.  For example:
-
-      @overload
-      def utf8(value: None) -> None: ...
-      @overload
-      def utf8(value: bytes) -> bytes: ...
-      @overload
-      def utf8(value: str) -> bytes: ...
-      def utf8(value):
-          # implementation goes here
-    """
-    return _overload_dummy
-
-
-class _ProtocolMeta(GenericMeta):
-    """Internal metaclass for _Protocol.
-
-    This exists so _Protocol classes can be generic without deriving
-    from Generic.
-    """
-
-    def __instancecheck__(self, obj):
-        if _Protocol not in self.__bases__:
-            return super().__instancecheck__(obj)
-        raise TypeError("Protocols cannot be used with isinstance().")
-
-    def __subclasscheck__(self, cls):
-        if not self._is_protocol:
-            # No structural checks since this isn't a protocol.
-            return NotImplemented
-
-        if self is _Protocol:
-            # Every class is a subclass of the empty protocol.
-            return True
-
-        # Find all attributes defined in the protocol.
-        attrs = self._get_protocol_attrs()
-
-        for attr in attrs:
-            if not any(attr in d.__dict__ for d in cls.__mro__):
-                return False
-        return True
-
-    def _get_protocol_attrs(self):
-        # Get all Protocol base classes.
-        protocol_bases = []
-        for c in self.__mro__:
-            if getattr(c, '_is_protocol', False) and c.__name__ != '_Protocol':
-                protocol_bases.append(c)
-
-        # Get attributes included in protocol.
-        attrs = set()
-        for base in protocol_bases:
-            for attr in base.__dict__.keys():
-                # Include attributes not defined in any non-protocol bases.
-                for c in self.__mro__:
-                    if (c is not base and attr in c.__dict__ and
-                        not getattr(c, '_is_protocol', False)):
-                        break
-                else:
-                    if (not attr.startswith('_abc_') and
-                        attr != '__abstractmethods__' and
-                        attr != '__annotations__' and
-                        attr != '__weakref__' and
-                        attr != '_is_protocol' and
-                        attr != '_gorg' and
-                        attr != '__dict__' and
-                        attr != '__args__' and
-                        attr != '__slots__' and
-                        attr != '_get_protocol_attrs' and
-                        attr != '__next_in_mro__' and
-                        attr != '__parameters__' and
-                        attr != '__origin__' and
-                        attr != '__orig_bases__' and
-                        attr != '__extra__' and
-                        attr != '__tree_hash__' and
-                        attr != '__module__'):
-                        attrs.add(attr)
-
-        return attrs
-
-
-class _Protocol(metaclass=_ProtocolMeta):
-    """Internal base class for protocol classes.
-
-    This implements a simple-minded structural issubclass check
-    (similar but more general than the one-offs in collections.abc
-    such as Hashable).
-    """
-
-    __slots__ = ()
-
-    _is_protocol = True
-
-
-# Various ABCs mimicking those in collections.abc.
-# A few are simply re-exported for completeness.
-
-Hashable = collections_abc.Hashable  # Not generic.
-
-if hasattr(collections_abc, 'Awaitable'):
-    class Awaitable(Generic[T_co], extra=collections_abc.Awaitable):
-        __slots__ = ()
-
-
-    __all__.append('Awaitable')
-
-if hasattr(collections_abc, 'Coroutine'):
-    class Coroutine(Awaitable[V_co], Generic[T_co, T_contra, V_co],
-                    extra=collections_abc.Coroutine):
-        __slots__ = ()
-
-
-    __all__.append('Coroutine')
-
-if hasattr(collections_abc, 'AsyncIterable'):
-    class AsyncIterable(Generic[T_co], extra=collections_abc.AsyncIterable):
-        __slots__ = ()
-
-
-    class AsyncIterator(AsyncIterable[T_co],
-                        extra=collections_abc.AsyncIterator):
-        __slots__ = ()
-
-
-    __all__.append('AsyncIterable')
-    __all__.append('AsyncIterator')
-
-
-class Iterable(Generic[T_co], extra=collections_abc.Iterable):
-    __slots__ = ()
-
-
-class Iterator(Iterable[T_co], extra=collections_abc.Iterator):
-    __slots__ = ()
-
-
-class SupportsInt(_Protocol):
-    __slots__ = ()
-
-    @abstractmethod
-    def __int__(self) -> int:
-        pass
-
-
-class SupportsFloat(_Protocol):
-    __slots__ = ()
-
-    @abstractmethod
-    def __float__(self) -> float:
-        pass
-
-
-class SupportsComplex(_Protocol):
-    __slots__ = ()
-
-    @abstractmethod
-    def __complex__(self) -> complex:
-        pass
-
-
-class SupportsBytes(_Protocol):
-    __slots__ = ()
-
-    @abstractmethod
-    def __bytes__(self) -> bytes:
-        pass
-
-
-class SupportsAbs(_Protocol[T_co]):
-    __slots__ = ()
-
-    @abstractmethod
-    def __abs__(self) -> T_co:
-        pass
-
-
-class SupportsRound(_Protocol[T_co]):
-    __slots__ = ()
-
-    @abstractmethod
-    def __round__(self, ndigits: int = 0) -> T_co:
-        pass
-
-
-if hasattr(collections_abc, 'Reversible'):
-    class Reversible(Iterable[T_co], extra=collections_abc.Reversible):
-        __slots__ = ()
-else:
-    class Reversible(_Protocol[T_co]):
-        __slots__ = ()
-
-        @abstractmethod
-        def __reversed__(self) -> 'Iterator[T_co]':
-            pass
-
-Sized = collections_abc.Sized  # Not generic.
-
-
-class Container(Generic[T_co], extra=collections_abc.Container):
-    __slots__ = ()
-
-
-if hasattr(collections_abc, 'Collection'):
-    class Collection(Sized, Iterable[T_co], Container[T_co],
-                     extra=collections_abc.Collection):
-        __slots__ = ()
-
-
-    __all__.append('Collection')
-
-# Callable was defined earlier.
-
-if hasattr(collections_abc, 'Collection'):
-    class AbstractSet(Collection[T_co],
-                      extra=collections_abc.Set):
-        __slots__ = ()
-else:
-    class AbstractSet(Sized, Iterable[T_co], Container[T_co],
-                      extra=collections_abc.Set):
-        __slots__ = ()
-
-
-class MutableSet(AbstractSet[T], extra=collections_abc.MutableSet):
-    __slots__ = ()
-
-
-# NOTE: It is only covariant in the value type.
-if hasattr(collections_abc, 'Collection'):
-    class Mapping(Collection[KT], Generic[KT, VT_co],
-                  extra=collections_abc.Mapping):
-        __slots__ = ()
-else:
-    class Mapping(Sized, Iterable[KT], Container[KT], Generic[KT, VT_co],
-                  extra=collections_abc.Mapping):
-        __slots__ = ()
-
-
-class MutableMapping(Mapping[KT, VT], extra=collections_abc.MutableMapping):
-    __slots__ = ()
-
-
-if hasattr(collections_abc, 'Reversible'):
-    if hasattr(collections_abc, 'Collection'):
-        class Sequence(Reversible[T_co], Collection[T_co],
-                       extra=collections_abc.Sequence):
-            __slots__ = ()
-    else:
-        class Sequence(Sized, Reversible[T_co], Container[T_co],
-                       extra=collections_abc.Sequence):
-            __slots__ = ()
-else:
-    class Sequence(Sized, Iterable[T_co], Container[T_co],
-                   extra=collections_abc.Sequence):
-        __slots__ = ()
-
-
-class MutableSequence(Sequence[T], extra=collections_abc.MutableSequence):
-    __slots__ = ()
-
-
-class ByteString(Sequence[int], extra=collections_abc.ByteString):
-    __slots__ = ()
-
-
-class List(list, MutableSequence[T], extra=list):
-    __slots__ = ()
-
-    def __new__(cls, *args, **kwds):
-        if cls._gorg is List:
-            raise TypeError("Type List cannot be instantiated; "
-                            "use list() instead")
-        return _generic_new(list, cls, *args, **kwds)
-
-
-class Deque(collections.deque, MutableSequence[T], extra=collections.deque):
-    __slots__ = ()
-
-    def __new__(cls, *args, **kwds):
-        if cls._gorg is Deque:
-            return collections.deque(*args, **kwds)
-        return _generic_new(collections.deque, cls, *args, **kwds)
-
-
-class Set(set, MutableSet[T], extra=set):
-    __slots__ = ()
-
-    def __new__(cls, *args, **kwds):
-        if cls._gorg is Set:
-            raise TypeError("Type Set cannot be instantiated; "
-                            "use set() instead")
-        return _generic_new(set, cls, *args, **kwds)
-
-
-class FrozenSet(frozenset, AbstractSet[T_co], extra=frozenset):
-    __slots__ = ()
-
-    def __new__(cls, *args, **kwds):
-        if cls._gorg is FrozenSet:
-            raise TypeError("Type FrozenSet cannot be instantiated; "
-                            "use frozenset() instead")
-        return _generic_new(frozenset, cls, *args, **kwds)
-
-
-class MappingView(Sized, Iterable[T_co], extra=collections_abc.MappingView):
-    __slots__ = ()
-
-
-class KeysView(MappingView[KT], AbstractSet[KT],
-               extra=collections_abc.KeysView):
-    __slots__ = ()
-
-
-class ItemsView(MappingView[Tuple[KT, VT_co]],
-                AbstractSet[Tuple[KT, VT_co]],
-                Generic[KT, VT_co],
-                extra=collections_abc.ItemsView):
-    __slots__ = ()
-
-
-class ValuesView(MappingView[VT_co], extra=collections_abc.ValuesView):
-    __slots__ = ()
-
-
-if hasattr(contextlib, 'AbstractContextManager'):
-    class ContextManager(Generic[T_co], extra=contextlib.AbstractContextManager):
-        __slots__ = ()
-else:
-    class ContextManager(Generic[T_co]):
-        __slots__ = ()
-
-        def __enter__(self):
-            return self
-
-        @abc.abstractmethod
-        def __exit__(self, exc_type, exc_value, traceback):
-            return None
-
-        @classmethod
-        def __subclasshook__(cls, C):
-            if cls is ContextManager:
-                # In Python 3.6+, it is possible to set a method to None to
-                # explicitly indicate that the class does not implement an ABC
-                # (https://bugs.python.org/issue25958), but we do not support
-                # that pattern here because this fallback class is only used
-                # in Python 3.5 and earlier.
-                if (any("__enter__" in B.__dict__ for B in C.__mro__) and
-                    any("__exit__" in B.__dict__ for B in C.__mro__)):
-                    return True
-            return NotImplemented
-
-if hasattr(contextlib, 'AbstractAsyncContextManager'):
-    class AsyncContextManager(Generic[T_co],
-                              extra=contextlib.AbstractAsyncContextManager):
-        __slots__ = ()
-
-
-    __all__.append('AsyncContextManager')
-elif sys.version_info[:2] >= (3, 5):
-    exec("""
-class AsyncContextManager(Generic[T_co]):
-    __slots__ = ()
-
-    async def __aenter__(self):
-        return self
-
-    @abc.abstractmethod
-    async def __aexit__(self, exc_type, exc_value, traceback):
-        return None
-
-    @classmethod
-    def __subclasshook__(cls, C):
-        if cls is AsyncContextManager:
-            if sys.version_info[:2] >= (3, 6):
-                return _collections_abc._check_methods(C, "__aenter__", "__aexit__")
-            if (any("__aenter__" in B.__dict__ for B in C.__mro__) and
-                    any("__aexit__" in B.__dict__ for B in C.__mro__)):
-                return True
-        return NotImplemented
-
-__all__.append('AsyncContextManager')
-""")
-
-
-class Dict(dict, MutableMapping[KT, VT], extra=dict):
-    __slots__ = ()
-
-    def __new__(cls, *args, **kwds):
-        if cls._gorg is Dict:
-            raise TypeError("Type Dict cannot be instantiated; "
-                            "use dict() instead")
-        return _generic_new(dict, cls, *args, **kwds)
-
-
-class DefaultDict(collections.defaultdict, MutableMapping[KT, VT],
-                  extra=collections.defaultdict):
-    __slots__ = ()
-
-    def __new__(cls, *args, **kwds):
-        if cls._gorg is DefaultDict:
-            return collections.defaultdict(*args, **kwds)
-        return _generic_new(collections.defaultdict, cls, *args, **kwds)
-
-
-class Counter(collections.Counter, Dict[T, int], extra=collections.Counter):
-    __slots__ = ()
-
-    def __new__(cls, *args, **kwds):
-        if cls._gorg is Counter:
-            return collections.Counter(*args, **kwds)
-        return _generic_new(collections.Counter, cls, *args, **kwds)
-
-
-if hasattr(collections, 'ChainMap'):
-    # ChainMap only exists in 3.3+
-    __all__.append('ChainMap')
-
-
-    class ChainMap(collections.ChainMap, MutableMapping[KT, VT],
-                   extra=collections.ChainMap):
-
-        __slots__ = ()
-
-        def __new__(cls, *args, **kwds):
-            if cls._gorg is ChainMap:
-                return collections.ChainMap(*args, **kwds)
-            return _generic_new(collections.ChainMap, cls, *args, **kwds)
-
-# Determine what base class to use for Generator.
-if hasattr(collections_abc, 'Generator'):
-    # Sufficiently recent versions of 3.5 have a Generator ABC.
-    _G_base = collections_abc.Generator
-else:
-    # Fall back on the exact type.
-    _G_base = types.GeneratorType
-
-
-class Generator(Iterator[T_co], Generic[T_co, T_contra, V_co],
-                extra=_G_base):
-    __slots__ = ()
-
-    def __new__(cls, *args, **kwds):
-        if cls._gorg is Generator:
-            raise TypeError("Type Generator cannot be instantiated; "
-                            "create a subclass instead")
-        return _generic_new(_G_base, cls, *args, **kwds)
-
-
-if hasattr(collections_abc, 'AsyncGenerator'):
-    class AsyncGenerator(AsyncIterator[T_co], Generic[T_co, T_contra],
-                         extra=collections_abc.AsyncGenerator):
-        __slots__ = ()
-
-
-    __all__.append('AsyncGenerator')
-
-# Internal type variable used for Type[].
-CT_co = TypeVar('CT_co', covariant=True, bound=type)
-
-
-# This is not a real generic class.  Don't use outside annotations.
-class Type(Generic[CT_co], extra=type):
-    """A special construct usable to annotate class objects.
-
-    For example, suppose we have the following classes::
-
-      class User: ...  # Abstract base for User classes
-      class BasicUser(User): ...
-      class ProUser(User): ...
-      class TeamUser(User): ...
-
-    And a function that takes a class argument that's a subclass of
-    User and returns an instance of the corresponding class::
-
-      U = TypeVar('U', bound=User)
-      def new_user(user_class: Type[U]) -> U:
-          user = user_class()
-          # (Here we could write the user object to a database)
-          return user
-
-      joe = new_user(BasicUser)
-
-    At this point the type checker knows that joe has type BasicUser.
-    """
-
-    __slots__ = ()
-
-
-def _make_nmtuple(name, types):
-    msg = "NamedTuple('Name', [(f0, t0), (f1, t1), ...]); each t must be a type"
-    types = [(n, _type_check(t, msg)) for n, t in types]
-    nm_tpl = collections.namedtuple(name, [n for n, t in types])
-    # Prior to PEP 526, only _field_types attribute was assigned.
-    # Now, both __annotations__ and _field_types are used to maintain compatibility.
-    nm_tpl.__annotations__ = nm_tpl._field_types = collections.OrderedDict(types)
-    try:
-        nm_tpl.__module__ = sys._getframe(2).f_globals.get('__name__', '__main__')
-    except (AttributeError, ValueError):
-        pass
-    return nm_tpl
-
-
-_PY36 = sys.version_info[:2] >= (3, 6)
-
-# attributes prohibited to set in NamedTuple class syntax
-_prohibited = ('__new__', '__init__', '__slots__', '__getnewargs__',
-               '_fields', '_field_defaults', '_field_types',
-               '_make', '_replace', '_asdict', '_source')
-
-_special = ('__module__', '__name__', '__qualname__', '__annotations__')
-
-
-class NamedTupleMeta(type):
-
-    def __new__(cls, typename, bases, ns):
-        if ns.get('_root', False):
-            return super().__new__(cls, typename, bases, ns)
-        if not _PY36:
-            raise TypeError("Class syntax for NamedTuple is only supported"
-                            " in Python 3.6+")
-        types = ns.get('__annotations__', {})
-        nm_tpl = _make_nmtuple(typename, types.items())
-        defaults = []
-        defaults_dict = {}
-        for field_name in types:
-            if field_name in ns:
-                default_value = ns[field_name]
-                defaults.append(default_value)
-                defaults_dict[field_name] = default_value
-            elif defaults:
-                raise TypeError("Non-default namedtuple field {field_name} cannot "
-                                "follow default field(s) {default_names}"
-                                .format(field_name=field_name,
-                                        default_names=', '.join(defaults_dict.keys())))
-        nm_tpl.__new__.__annotations__ = collections.OrderedDict(types)
-        nm_tpl.__new__.__defaults__ = tuple(defaults)
-        nm_tpl._field_defaults = defaults_dict
-        # update from user namespace without overriding special namedtuple attributes
-        for key in ns:
-            if key in _prohibited:
-                raise AttributeError("Cannot overwrite NamedTuple attribute " + key)
-            elif key not in _special and key not in nm_tpl._fields:
-                setattr(nm_tpl, key, ns[key])
-        return nm_tpl
-
-
-class NamedTuple(metaclass=NamedTupleMeta):
-    """Typed version of namedtuple.
-
-    Usage in Python versions >= 3.6::
-
-        class Employee(NamedTuple):
-            name: str
-            id: int
-
-    This is equivalent to::
-
-        Employee = collections.namedtuple('Employee', ['name', 'id'])
-
-    The resulting class has extra __annotations__ and _field_types
-    attributes, giving an ordered dict mapping field names to types.
-    __annotations__ should be preferred, while _field_types
-    is kept to maintain pre PEP 526 compatibility. (The field names
-    are in the _fields attribute, which is part of the namedtuple
-    API.) Alternative equivalent keyword syntax is also accepted::
-
-        Employee = NamedTuple('Employee', name=str, id=int)
-
-    In Python versions <= 3.5 use::
-
-        Employee = NamedTuple('Employee', [('name', str), ('id', int)])
-    """
-    _root = True
-
-    def __new__(self, typename, fields=None, **kwargs):
-        if kwargs and not _PY36:
-            raise TypeError("Keyword syntax for NamedTuple is only supported"
-                            " in Python 3.6+")
-        if fields is None:
-            fields = kwargs.items()
-        elif kwargs:
-            raise TypeError("Either list of fields or keywords"
-                            " can be provided to NamedTuple, not both")
-        return _make_nmtuple(typename, fields)
-
-
-def NewType(name, tp):
-    """NewType creates simple unique types with almost zero
-    runtime overhead. NewType(name, tp) is considered a subtype of tp
-    by static type checkers. At runtime, NewType(name, tp) returns
-    a dummy function that simply returns its argument. Usage::
-
-        UserId = NewType('UserId', int)
-
-        def name_by_id(user_id: UserId) -> str:
-            ...
-
-        UserId('user')          # Fails type check
-
-        name_by_id(42)          # Fails type check
-        name_by_id(UserId(42))  # OK
-
-        num = UserId(5) + 1     # type: int
-    """
-
-    def new_type(x):
-        return x
-
-    new_type.__name__ = name
-    new_type.__supertype__ = tp
-    return new_type
-
-
-# Python-version-specific alias (Python 2: unicode; Python 3: str)
-Text = str
-
-# Constant that's True when type checking, but False here.
-TYPE_CHECKING = False
-
-
-class IO(Generic[AnyStr]):
-    """Generic base class for TextIO and BinaryIO.
-
-    This is an abstract, generic version of the return of open().
-
-    NOTE: This does not distinguish between the different possible
-    classes (text vs. binary, read vs. write vs. read/write,
-    append-only, unbuffered).  The TextIO and BinaryIO subclasses
-    below capture the distinctions between text vs. binary, which is
-    pervasive in the interface; however we currently do not offer a
-    way to track the other distinctions in the type system.
-    """
-
-    __slots__ = ()
-
-    @abstractproperty
-    def mode(self) -> str:
-        pass
-
-    @abstractproperty
-    def name(self) -> str:
-        pass
-
-    @abstractmethod
-    def close(self) -> None:
-        pass
-
-    @abstractproperty
-    def closed(self) -> bool:
-        pass
-
-    @abstractmethod
-    def fileno(self) -> int:
-        pass
-
-    @abstractmethod
-    def flush(self) -> None:
-        pass
-
-    @abstractmethod
-    def isatty(self) -> bool:
-        pass
-
-    @abstractmethod
-    def read(self, n: int = -1) -> AnyStr:
-        pass
-
-    @abstractmethod
-    def readable(self) -> bool:
-        pass
-
-    @abstractmethod
-    def readline(self, limit: int = -1) -> AnyStr:
-        pass
-
-    @abstractmethod
-    def readlines(self, hint: int = -1) -> List[AnyStr]:
-        pass
-
-    @abstractmethod
-    def seek(self, offset: int, whence: int = 0) -> int:
-        pass
-
-    @abstractmethod
-    def seekable(self) -> bool:
-        pass
-
-    @abstractmethod
-    def tell(self) -> int:
-        pass
-
-    @abstractmethod
-    def truncate(self, size: int = None) -> int:
-        pass
-
-    @abstractmethod
-    def writable(self) -> bool:
-        pass
-
-    @abstractmethod
-    def write(self, s: AnyStr) -> int:
-        pass
-
-    @abstractmethod
-    def writelines(self, lines: List[AnyStr]) -> None:
-        pass
-
-    @abstractmethod
-    def __enter__(self) -> 'IO[AnyStr]':
-        pass
-
-    @abstractmethod
-    def __exit__(self, type, value, traceback) -> None:
-        pass
-
-
-class BinaryIO(IO[bytes]):
-    """Typed version of the return of open() in binary mode."""
-
-    __slots__ = ()
-
-    @abstractmethod
-    def write(self, s: Union[bytes, bytearray]) -> int:
-        pass
-
-    @abstractmethod
-    def __enter__(self) -> 'BinaryIO':
-        pass
-
-
-class TextIO(IO[str]):
-    """Typed version of the return of open() in text mode."""
-
-    __slots__ = ()
-
-    @abstractproperty
-    def buffer(self) -> BinaryIO:
-        pass
-
-    @abstractproperty
-    def encoding(self) -> str:
-        pass
-
-    @abstractproperty
-    def errors(self) -> Optional[str]:
-        pass
-
-    @abstractproperty
-    def line_buffering(self) -> bool:
-        pass
-
-    @abstractproperty
-    def newlines(self) -> Any:
-        pass
-
-    @abstractmethod
-    def __enter__(self) -> 'TextIO':
-        pass
-
-
-class io:
-    """Wrapper namespace for IO generic classes."""
-
-    __all__ = ['IO', 'TextIO', 'BinaryIO']
-    IO = IO
-    TextIO = TextIO
-    BinaryIO = BinaryIO
-
-
-io.__name__ = __name__ + '.io'
-sys.modules[io.__name__] = io
-
-Pattern = _TypeAlias('Pattern', AnyStr, type(stdlib_re.compile('')),
-                     lambda p: p.pattern)
-Match = _TypeAlias('Match', AnyStr, type(stdlib_re.match('', '')),
-                   lambda m: m.re.pattern)
-
-
-class re:
-    """Wrapper namespace for re type aliases."""
-
-    __all__ = ['Pattern', 'Match']
-    Pattern = Pattern
-    Match = Match
-
-
-re.__name__ = __name__ + '.re'
-sys.modules[re.__name__] = re
diff --git a/requirements.txt b/requirements.txt
index 227aacf66a8..bf6641539c8 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,2 @@
 pyaes==1.6.1
-pysocks==1.6.8
-typing==3.6.6; python_version<"3.5"
\ No newline at end of file
+pysocks==1.7.1
diff --git a/setup.py b/setup.py
index ef02c428375..2cb7d7f6772 100644
--- a/setup.py
+++ b/setup.py
@@ -1,157 +1,66 @@
-# Pyrogram - Telegram MTProto API Client Library for Python
-# Copyright (C) 2017-2019 Dan Tès 
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
 #
-# This file is part of Pyrogram.
+#  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 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.
+#  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 .
+#  You should have received a copy of the GNU Lesser General Public License
+#  along with Pyrogram.  If not, see .
 
-import os
 import re
-import shutil
 from sys import argv
 
-from setuptools import setup, find_packages, Command
+from setuptools import setup, find_packages
 
 from compiler.api import compiler as api_compiler
-from compiler.docs import compiler as docs_compiler
-from compiler.error import compiler as error_compiler
+from compiler.errors import compiler as errors_compiler
 
+with open("requirements.txt", encoding="utf-8") as r:
+    requires = [i.strip() for i in r]
 
-def read(file: str) -> list:
-    with open(file, encoding="utf-8") as r:
-        return [i.strip() for i in r]
-
-
-def get_version():
-    with open("pyrogram/__init__.py", encoding="utf-8") as f:
-        return re.findall(r"__version__ = \"(.+)\"", f.read())[0]
-
-
-def get_readme():
-    # PyPI doesn't like raw html
-    with open("README.rst", encoding="utf-8") as f:
-        readme = re.sub(r"\.\. \|.+\| raw:: html(?:\s{4}.+)+\n\n", "", f.read())
-        return re.sub(r"\|header\|", "|logo|\n\n|description|\n\n|schema| |tgcrypto|", readme)
-
-
-class Clean(Command):
-    DIST = ["./build", "./dist", "./Pyrogram.egg-info"]
-    API = ["pyrogram/api/errors/exceptions", "pyrogram/api/functions", "pyrogram/api/types", "pyrogram/api/all.py"]
-    DOCS = ["docs/source/functions", "docs/source/types", "docs/build"]
-    ALL = DIST + API + DOCS
-
-    description = "Clean generated files"
-
-    user_options = [
-        ("dist", None, "Clean distribution files"),
-        ("api", None, "Clean generated API files"),
-        ("docs", None, "Clean generated docs files"),
-        ("all", None, "Clean all generated files"),
-    ]
-
-    def __init__(self, dist, **kw):
-        super().__init__(dist, **kw)
-
-        self.dist = None
-        self.api = None
-        self.docs = None
-        self.all = None
-
-    def initialize_options(self):
-        pass
-
-    def finalize_options(self):
-        pass
-
-    def run(self):
-        paths = set()
-
-        if self.dist:
-            paths.update(Clean.DIST)
-
-        if self.api:
-            paths.update(Clean.API)
-
-        if self.docs:
-            paths.update(Clean.DOCS)
-
-        if self.all or not paths:
-            paths.update(Clean.ALL)
-
-        for path in sorted(list(paths)):
-            try:
-                shutil.rmtree(path) if os.path.isdir(path) else os.remove(path)
-            except OSError:
-                print("skipping {}".format(path))
-            else:
-                print("removing {}".format(path))
-
-
-class Generate(Command):
-    description = "Generate Pyrogram files"
-
-    user_options = [
-        ("api", None, "Generate API files"),
-        ("docs", None, "Generate docs files")
-    ]
-
-    def __init__(self, dist, **kw):
-        super().__init__(dist, **kw)
-
-        self.api = None
-        self.docs = None
-
-    def initialize_options(self):
-        pass
-
-    def finalize_options(self):
-        pass
-
-    def run(self):
-        if self.api:
-            error_compiler.start()
-            api_compiler.start()
-
-        if self.docs:
-            docs_compiler.start()
+with open("pyrogram/__init__.py", encoding="utf-8") as f:
+    version = re.findall(r"__version__ = \"(.+)\"", f.read())[0]
 
+with open("README.md", encoding="utf-8") as f:
+    readme = f.read()
 
 if len(argv) > 1 and argv[1] in ["bdist_wheel", "install", "develop"]:
-    error_compiler.start()
     api_compiler.start()
-    docs_compiler.start()
+    errors_compiler.start()
 
 setup(
     name="Pyrogram",
-    version=get_version(),
-    description="Telegram MTProto API Client Library for Python",
-    long_description=get_readme(),
+    version=version,
+    description="Elegant, modern and asynchronous Telegram MTProto API framework in Python for users and bots",
+    long_description=readme,
+    long_description_content_type="text/markdown",
     url="https://github.com/pyrogram",
     download_url="https://github.com/pyrogram/pyrogram/releases/latest",
-    author="Dan Tès",
-    author_email="admin@pyrogram.ml",
-    license="LGPLv3+",
+    author="Dan",
+    author_email="dan@pyrogram.org",
+    license="LGPLv3",
     classifiers=[
-        "Development Status :: 3 - Alpha",
+        "Development Status :: 5 - Production/Stable",
         "Intended Audience :: Developers",
-        "License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)",
+        "Natural Language :: English",
+        "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
         "Operating System :: OS Independent",
         "Programming Language :: Python",
         "Programming Language :: Python :: 3",
-        "Programming Language :: Python :: 3.4",
-        "Programming Language :: Python :: 3.5",
-        "Programming Language :: Python :: 3.6",
+        "Programming Language :: Python :: 3.7",
+        "Programming Language :: Python :: 3.8",
+        "Programming Language :: Python :: 3.9",
+        "Programming Language :: Python :: 3.10",
+        "Programming Language :: Python :: 3.11",
         "Programming Language :: Python :: Implementation",
         "Programming Language :: Python :: Implementation :: CPython",
         "Programming Language :: Python :: Implementation :: PyPy",
@@ -165,23 +74,15 @@ def run(self):
     keywords="telegram chat messenger mtproto api client library python",
     project_urls={
         "Tracker": "https://github.com/pyrogram/pyrogram/issues",
-        "Community": "https://t.me/PyrogramChat",
+        "Community": "https://t.me/pyrogram",
         "Source": "https://github.com/pyrogram/pyrogram",
-        "Documentation": "https://docs.pyrogram.ml",
+        "Documentation": "https://docs.pyrogram.org",
     },
-    python_requires="~=3.4",
-    packages=find_packages(exclude=["compiler*"]),
+    python_requires="~=3.7",
     package_data={
-        "pyrogram.client.ext": ["mime.types"]
+        "pyrogram": ["py.typed"],
     },
+    packages=find_packages(exclude=["compiler*", "tests*"]),
     zip_safe=False,
-    install_requires=read("requirements.txt"),
-    extras_require={
-        "tgcrypto": ["tgcrypto==1.1.1"],  # TODO: Remove soon
-        "fast": ["tgcrypto==1.1.1"],
-    },
-    cmdclass={
-        "clean": Clean,
-        "generate": Generate
-    }
+    install_requires=requires
 )
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 00000000000..46887cb7a57
--- /dev/null
+++ b/tests/__init__.py
@@ -0,0 +1,17 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
diff --git a/tests/filters/__init__.py b/tests/filters/__init__.py
new file mode 100644
index 00000000000..6711b51b4cd
--- /dev/null
+++ b/tests/filters/__init__.py
@@ -0,0 +1,36 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 Client:
+    def __init__(self):
+        self.me = User("username")
+
+    async def get_me(self):
+        return self.me
+
+
+class User:
+    def __init__(self, username: str = None):
+        self.username = username
+
+
+class Message:
+    def __init__(self, text: str = None, caption: str = None):
+        self.text = text
+        self.caption = caption
+        self.command = None
diff --git a/tests/filters/test_command.py b/tests/filters/test_command.py
new file mode 100644
index 00000000000..234ed69fc3e
--- /dev/null
+++ b/tests/filters/test_command.py
@@ -0,0 +1,148 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import pytest
+
+from pyrogram import filters
+from tests.filters import Client, Message
+
+c = Client()
+
+
+@pytest.mark.asyncio
+async def test_single():
+    f = filters.command("start")
+
+    m = Message("/start")
+    assert await f(c, m)
+
+
+@pytest.mark.asyncio
+async def test_multiple():
+    f = filters.command(["start", "help"])
+
+    m = Message("/start")
+    assert await f(c, m)
+
+    m = Message("/help")
+    assert await f(c, m)
+
+    m = Message("/settings")
+    assert not await f(c, m)
+
+
+@pytest.mark.asyncio
+async def test_prefixes():
+    f = filters.command("start", prefixes=list(".!#"))
+
+    m = Message(".start")
+    assert await f(c, m)
+
+    m = Message("!start")
+    assert await f(c, m)
+
+    m = Message("#start")
+    assert await f(c, m)
+
+    m = Message("/start")
+    assert not await f(c, m)
+
+
+@pytest.mark.asyncio
+async def test_case_sensitive():
+    f = filters.command("start", case_sensitive=True)
+
+    m = Message("/start")
+    assert await f(c, m)
+
+    m = Message("/StArT")
+    assert not await f(c, m)
+
+
+@pytest.mark.asyncio
+async def test_case_insensitive():
+    f = filters.command("start", case_sensitive=False)
+
+    m = Message("/start")
+    assert await f(c, m)
+
+    m = Message("/StArT")
+    assert await f(c, m)
+
+
+@pytest.mark.asyncio
+async def test_with_mention():
+    f = filters.command("start")
+
+    m = Message("/start@username")
+    assert await f(c, m)
+
+    m = Message("/start@UserName")
+    assert await f(c, m)
+
+    m = Message("/start@another")
+    assert not await f(c, m)
+
+
+@pytest.mark.asyncio
+async def test_with_args():
+    f = filters.command("start")
+
+    m = Message("/start")
+    await f(c, m)
+    assert m.command == ["start"]
+
+    m = Message("/StArT")
+    await f(c, m)
+    assert m.command == ["start"]
+
+    m = Message("/start@username")
+    await f(c, m)
+    assert m.command == ["start"]
+
+    m = Message("/start a b c")
+    await f(c, m)
+    assert m.command == ["start"] + list("abc")
+
+    m = Message('/start@username a b c')
+    await f(c, m)
+    assert m.command == ["start"] + list("abc")
+
+    m = Message("/start 'a b' c")
+    await f(c, m)
+    assert m.command == ["start", "a b", "c"]
+
+    m = Message('/start     a     b     "c     d"')
+    await f(c, m)
+    assert m.command == ["start"] + list("ab") + ["c     d"]
+
+
+@pytest.mark.asyncio
+async def test_caption():
+    f = filters.command("start")
+
+    m = Message(caption="/start")
+    assert await f(c, m)
+
+
+@pytest.mark.asyncio
+async def test_no_text():
+    f = filters.command("start")
+
+    m = Message()
+    assert not await f(c, m)
diff --git a/tests/parser/__init__.py b/tests/parser/__init__.py
new file mode 100644
index 00000000000..46887cb7a57
--- /dev/null
+++ b/tests/parser/__init__.py
@@ -0,0 +1,17 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
diff --git a/tests/parser/test_html.py b/tests/parser/test_html.py
new file mode 100644
index 00000000000..b9138f3cf18
--- /dev/null
+++ b/tests/parser/test_html.py
@@ -0,0 +1,147 @@
+#  Pyrogram - Telegram MTProto API Client Library for Python
+#  Copyright (C) 2017-present Dan 
+#
+#  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 .
+
+import pyrogram
+from pyrogram.parser.html import HTML
+
+
+# expected: the expected unparsed HTML
+# text: original text without entities
+# entities: message entities coming from the server
+
+def test_html_unparse_bold():
+    expected = "bold"
+    text = "bold"
+    entities = pyrogram.types.List(
+        [pyrogram.types.MessageEntity(type=pyrogram.enums.MessageEntityType.BOLD, offset=0, length=4)])
+
+    assert HTML.unparse(text=text, entities=entities) == expected
+
+
+def test_html_unparse_italic():
+    expected = "italic"
+    text = "italic"
+    entities = pyrogram.types.List(
+        [pyrogram.types.MessageEntity(type=pyrogram.enums.MessageEntityType.ITALIC, offset=0, length=6)])
+
+    assert HTML.unparse(text=text, entities=entities) == expected
+
+
+def test_html_unparse_underline():
+    expected = "underline"
+    text = "underline"
+    entities = pyrogram.types.List(
+        [pyrogram.types.MessageEntity(type=pyrogram.enums.MessageEntityType.UNDERLINE, offset=0, length=9)])
+
+    assert HTML.unparse(text=text, entities=entities) == expected
+
+
+def test_html_unparse_strike():
+    expected = "strike"
+    text = "strike"
+    entities = pyrogram.types.List(
+        [pyrogram.types.MessageEntity(type=pyrogram.enums.MessageEntityType.STRIKETHROUGH, offset=0, length=6)])
+
+    assert HTML.unparse(text=text, entities=entities) == expected
+
+
+def test_html_unparse_spoiler():
+    expected = "spoiler"
+    text = "spoiler"
+    entities = pyrogram.types.List(
+        [pyrogram.types.MessageEntity(type=pyrogram.enums.MessageEntityType.SPOILER, offset=0, length=7)])
+
+    assert HTML.unparse(text=text, entities=entities) == expected
+
+
+def test_html_unparse_url():
+    expected = 'URL'
+    text = "URL"
+    entities = pyrogram.types.List([pyrogram.types.MessageEntity(type=pyrogram.enums.MessageEntityType.TEXT_LINK,
+                                                                 offset=0, length=3, url='https://pyrogram.org/')])
+
+    assert HTML.unparse(text=text, entities=entities) == expected
+
+
+def test_html_unparse_code():
+    expected = 'code'
+    text = "code"
+    entities = pyrogram.types.List(
+        [pyrogram.types.MessageEntity(type=pyrogram.enums.MessageEntityType.CODE, offset=0, length=4)])
+
+    assert HTML.unparse(text=text, entities=entities) == expected
+
+
+def test_html_unparse_pre():
+    expected = """
for i in range(10):
+    print(i)
""" + + text = """for i in range(10): + print(i)""" + + entities = pyrogram.types.List([pyrogram.types.MessageEntity(type=pyrogram.enums.MessageEntityType.PRE, offset=0, + length=32, language='python')]) + + assert HTML.unparse(text=text, entities=entities) == expected + + +def test_html_unparse_mixed(): + expected = "aaaaaaaaaabbbbbbbbbbccccccccccdddddddddd" \ + "eeeeeeeeeeffffffffffgggggggggghhhhhhhhhh" + text = "aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffffffffffgggggggggghhhhhhhhhh" + entities = pyrogram.types.List( + [pyrogram.types.MessageEntity(type=pyrogram.enums.MessageEntityType.BOLD, offset=0, length=14), + pyrogram.types.MessageEntity(type=pyrogram.enums.MessageEntityType.ITALIC, offset=7, length=7), + pyrogram.types.MessageEntity(type=pyrogram.enums.MessageEntityType.UNDERLINE, offset=10, length=4), + pyrogram.types.MessageEntity(type=pyrogram.enums.MessageEntityType.UNDERLINE, offset=14, length=9), + pyrogram.types.MessageEntity(type=pyrogram.enums.MessageEntityType.ITALIC, offset=14, length=9), + pyrogram.types.MessageEntity(type=pyrogram.enums.MessageEntityType.UNDERLINE, offset=23, length=10), + pyrogram.types.MessageEntity(type=pyrogram.enums.MessageEntityType.STRIKETHROUGH, offset=30, length=3), + pyrogram.types.MessageEntity(type=pyrogram.enums.MessageEntityType.STRIKETHROUGH, offset=33, length=10), + pyrogram.types.MessageEntity(type=pyrogram.enums.MessageEntityType.SPOILER, offset=38, length=5), + pyrogram.types.MessageEntity(type=pyrogram.enums.MessageEntityType.SPOILER, offset=43, length=10), + pyrogram.types.MessageEntity(type=pyrogram.enums.MessageEntityType.CODE, offset=57, length=10)]) + + assert HTML.unparse(text=text, entities=entities) == expected + + +def test_html_unparse_escaped(): + expected = "<b>bold</b>" + text = "bold" + entities = pyrogram.types.List( + [pyrogram.types.MessageEntity(type=pyrogram.enums.MessageEntityType.BOLD, offset=0, length=11)]) + + assert HTML.unparse(text=text, entities=entities) == expected + + +def test_html_unparse_escaped_nested(): + expected = "<b>bold <u>underline</u> bold</b>" + text = "bold underline bold" + entities = pyrogram.types.List( + [pyrogram.types.MessageEntity(type=pyrogram.enums.MessageEntityType.BOLD, offset=0, length=33), + pyrogram.types.MessageEntity(type=pyrogram.enums.MessageEntityType.UNDERLINE, offset=8, length=16)]) + + assert HTML.unparse(text=text, entities=entities) == expected + + +def test_html_unparse_no_entities(): + expected = "text" + text = "text" + entities = [] + + assert HTML.unparse(text=text, entities=entities) == expected diff --git a/tests/test_file_id.py b/tests/test_file_id.py new file mode 100644 index 00000000000..96152e48ca2 --- /dev/null +++ b/tests/test_file_id.py @@ -0,0 +1,194 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# +# 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 . + +import pytest + +from pyrogram.file_id import FileId, FileUniqueId, FileType, FileUniqueType + + +def check(file_id: str, expected_file_type: FileType): + decoded = FileId.decode(file_id) + + assert decoded.file_type == expected_file_type + assert decoded.encode() == file_id + + +def check_unique(file_unique_id: str, expected_file_unique_type: FileUniqueType): + decoded = FileUniqueId.decode(file_unique_id) + + assert decoded.file_unique_type == expected_file_unique_type + assert decoded.encode() == file_unique_id + + +def test_audio(): + audio = "CQACAgIAAx0CAAGgr9AAAgmQX7b4XPBstC1fFUuJBooHTHFd7HMAAgUAA4GkuUnVOGG5P196yR4E" + audio_unique = "AgADBQADgaS5SQ" + audio_thumb = "AAMCAgADHQIAAaCv0AACCZBftvhc8Gy0LV8VS4kGigdMcV3scwACBQADgaS5SdU4Ybk_X3rJIH3qihAAAwEAB20AA_OeAQABHgQ" + audio_thumb_unique = "AQADIH3qihAAA_OeAQAB" + + check(audio, FileType.AUDIO) + check_unique(audio_unique, FileUniqueType.DOCUMENT) + check(audio_thumb, FileType.THUMBNAIL) + check_unique(audio_thumb_unique, FileUniqueType.PHOTO) + + +def test_video(): + video = "BAACAgIAAx0CAAGgr9AAAgmRX7b4Xv9f-4BK5VR_5ppIOF6UIp0AAgYAA4GkuUmhnZz2xC37wR4E" + video_unique = "AgADBgADgaS5SQ" + video_thumb = "AAMCAgADHQIAAaCv0AACCZFftvhe_1_7gErlVH_mmkg4XpQinQACBgADgaS5SaGdnPbELfvBIH3qihAAAwEAB20AA_WeAQABHgQ" + video_thumb_unique = "AQADIH3qihAAA_WeAQAB" + + check(video, FileType.VIDEO) + check_unique(video_unique, FileUniqueType.DOCUMENT) + check(video_thumb, FileType.THUMBNAIL) + check_unique(video_thumb_unique, FileUniqueType.PHOTO) + + +def test_document(): + document = "BQACAgIAAx0CAAGgr9AAAgmPX7b4UxbjNoFEO_L0I4s6wrXNJA8AAgQAA4GkuUm9FFvIaOhXWR4E" + document_unique = "AgADBAADgaS5SQ" + document_thumb = "AAMCAgADHQIAAaCv0AACCY9ftvhTFuM2gUQ78vQjizrCtc0kDwACBAADgaS5Sb0UW8ho6FdZIH3qihAAAwEAB3MAA_GeAQABHgQ" + document_thumb_unique = "AQADIH3qihAAA_GeAQAB" + + check(document, FileType.DOCUMENT) + check_unique(document_unique, FileUniqueType.DOCUMENT) + check(document_thumb, FileType.THUMBNAIL) + check_unique(document_thumb_unique, FileUniqueType.PHOTO) + + +def test_animation(): + animation = "CgACAgIAAx0CAAGgr9AAAgmSX7b4Y2g8_QW2XFd49iUmRnHOyG8AAgcAA4GkuUnry9gWDzF_5R4E" + animation_unique = "AgADBwADgaS5SQ" + + check(animation, FileType.ANIMATION) + check_unique(animation_unique, FileUniqueType.DOCUMENT) + + +def test_voice(): + voice = "AwACAgIAAx0CAAGgr9AAAgmUX7b4c1KQyHVwzffxC2EnSYWsMAQAAgkAA4GkuUlsZUZ4_I97AR4E" + voice_unique = "AgADCQADgaS5SQ" + + check(voice, FileType.VOICE) + check_unique(voice_unique, FileUniqueType.DOCUMENT) + + +def test_video_note(): + video_note = "DQACAgIAAx0CAAGgr9AAAgmVX7b53qrRzCEO13BaLQJaYuFbdlwAAgoAA4GkuUmlqIzDy_PCsx4E" + video_note_unique = "AgADCgADgaS5SQ" + video_note_thumb = "AAMCAgADHQIAAaCv0AACCZVftvneqtHMIQ7XcFotAlpi4Vt2XAACCgADgaS5SaWojMPL88KzIH3qihAAAwEAB20AA_meAQABHgQ" + video_note_thumb_unique = "AQADIH3qihAAA_meAQAB" + + check(video_note, FileType.VIDEO_NOTE) + check_unique(video_note_unique, FileUniqueType.DOCUMENT) + check(video_note_thumb, FileType.THUMBNAIL) + check_unique(video_note_thumb_unique, FileUniqueType.PHOTO) + + +def test_sticker(): + sticker = "CAACAgEAAx0CAAGgr9AAAgmWX7b6uFeLlhXEgYrM8pIbGaQKRQ0AAswBAALjeAQAAbeooNv_tb6-HgQ" + sticker_unique = "AgADzAEAAuN4BAAB" + sticker_thumb = "AAMCAQADHQIAAaCv0AACCZZftvq4V4uWFcSBiszykhsZpApFDQACzAEAAuN4BAABt6ig2_-1vr5gWNkpAAQBAAdtAAM0BQACHgQ" + sticker_thumb_unique = "AQADYFjZKQAENAUAAg" + + check(sticker, FileType.STICKER) + check_unique(sticker_unique, FileUniqueType.DOCUMENT) + check(sticker_thumb, FileType.THUMBNAIL) + check_unique(sticker_thumb_unique, FileUniqueType.PHOTO) + + +def test_photo(): + photo_small = "AgACAgIAAx0CAAGgr9AAAgmZX7b7IPLRl8NcV3EJkzHwI1gwT-oAAq2nMRuBpLlJPJY-URZfhTkgfeqKEAADAQADAgADbQADAZ8BAAEeBA" + photo_small_unique = "AQADIH3qihAAAwGfAQAB" + photo_medium = "AgACAgIAAx0CAAGgr9AAAgmZX7b7IPLRl8NcV3EJkzHwI1gwT-oAAq2nMRuBpLlJPJY-URZfhTkgfeqKEAADAQADAgADeAADAp8BAAEeBA" + photo_medium_unique = "AQADIH3qihAAAwKfAQAB" + photo_big = "AgACAgIAAx0CAAGgr9AAAgmZX7b7IPLRl8NcV3EJkzHwI1gwT-oAAq2nMRuBpLlJPJY-URZfhTkgfeqKEAADAQADAgADeQAD_54BAAEeBA" + photo_big_unique = "AQADIH3qihAAA_-eAQAB" + + check(photo_small, FileType.PHOTO) + check_unique(photo_small_unique, FileUniqueType.PHOTO) + check(photo_medium, FileType.PHOTO) + check_unique(photo_medium_unique, FileUniqueType.PHOTO) + check(photo_big, FileType.PHOTO) + check_unique(photo_big_unique, FileUniqueType.PHOTO) + + +def test_chat_photo(): + user_photo_small = "AQADAgADrKcxGylBBQAJIH3qihAAAwIAAylBBQAF7bDHYwABnc983KcAAh4E" + user_photo_small_unique = "AQADIH3qihAAA9ynAAI" + user_photo_big = "AQADAgADrKcxGylBBQAJIH3qihAAAwMAAylBBQAF7bDHYwABnc983qcAAh4E" + user_photo_big_unique = "AQADIH3qihAAA96nAAI" + + chat_photo_small = "AQADAgATIH3qihAAAwIAA3t3-P______AAjhngEAAR4E" + chat_photo_small_unique = "AQADIH3qihAAA-GeAQAB" + chat_photo_big = "AQADAgATIH3qihAAAwMAA3t3-P______AAjjngEAAR4E" + chat_photo_big_unique = "AQADIH3qihAAA-OeAQAB" + + channel_photo_small = "AQADAgATIH3qihAAAwIAA-fFwCoX____MvARg8nvpc3RpwACHgQ" + channel_photo_small_unique = "AQADIH3qihAAA9GnAAI" + channel_photo_big = "AQADAgATIH3qihAAAwMAA-fFwCoX____MvARg8nvpc3TpwACHgQ" + channel_photo_big_unique = "AQADIH3qihAAA9OnAAI" + + check(user_photo_small, FileType.CHAT_PHOTO) + check_unique(user_photo_small_unique, FileUniqueType.PHOTO) + check(user_photo_big, FileType.CHAT_PHOTO) + check_unique(user_photo_big_unique, FileUniqueType.PHOTO) + + check(chat_photo_small, FileType.CHAT_PHOTO) + check_unique(chat_photo_small_unique, FileUniqueType.PHOTO) + check(chat_photo_big, FileType.CHAT_PHOTO) + check_unique(chat_photo_big_unique, FileUniqueType.PHOTO) + + check(channel_photo_small, FileType.CHAT_PHOTO) + check_unique(channel_photo_small_unique, FileUniqueType.PHOTO) + check(channel_photo_big, FileType.CHAT_PHOTO) + check_unique(channel_photo_big_unique, FileUniqueType.PHOTO) + + +def test_old_file_id(): + old = "BQADBAADQNKSZqjl5DcROGn_eu5JtgAEAgAEAg" + check(old, FileType.DOCUMENT) + + +def test_unknown_file_type(): + unknown = "RQACAgIAAx0CAAGgr9AAAgmPX7b4UxbjNoFEO_L0I4s6wrXNJA8AAgQAA4GkuUm9FFvIaOhXWR4E" + + with pytest.raises(ValueError, match=r"Unknown file_type \d+ of file_id \w+"): + check(unknown, FileType.DOCUMENT) + + +def test_unknown_thumbnail_source(): + unknown = "AAMCAgADHQIAAaCv0AACCY9ftvhTFuM2gUQ78vQjizrCtc0kDwACBAADgaS5Sb0UW8ho6FdZIH3qihAAA6QBAAIeBA" + + with pytest.raises(ValueError, match=r"Unknown thumbnail_source \d+ of file_id \w+"): + check(unknown, FileType.THUMBNAIL) + + +def test_stringify_file_id(): + file_id = "BQACAgIAAx0CAAGgr9AAAgmPX7b4UxbjNoFEO_L0I4s6wrXNJA8AAgQAA4GkuUm9FFvIaOhXWR4E" + string = "{'major': 4, 'minor': 30, 'file_type': , 'dc_id': 2, " \ + "'file_reference': b'\\x02\\x00\\xa0\\xaf\\xd0\\x00\\x00\\t\\x8f_\\xb6\\xf8S\\x16\\xe36\\x81D;\\xf2\\xf4#\\x8b:\\xc2\\xb5\\xcd$\\x0f', " \ + "'media_id': 5312458109417947140, 'access_hash': 6437869729085068477, 'thumbnail_size': ''}" + + assert str(FileId.decode(file_id)) == string + + +def test_stringify_file_unique_id(): + file_unique_id = "AgADBAADgaS5SQ" + string = "{'file_unique_type': , 'media_id': 5312458109417947140}" + + assert str(FileUniqueId.decode(file_unique_id)) == string diff --git a/tox.ini b/tox.ini new file mode 100644 index 00000000000..1795b1f2253 --- /dev/null +++ b/tox.ini @@ -0,0 +1,4 @@ +[testenv] +deps = -rdev-requirements.txt +commands = coverage run -m pytest {posargs} +skip_install = true \ No newline at end of file