mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2026-06-21 00:25:42 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 21ded420e2 | |||
| 7d893fd04b | |||
| 7015f8dedc | |||
| ac02bce109 | |||
| 3a9a0ab96d | |||
| aba17cb997 | |||
| 038a3b4452 | |||
| b03ebc5a65 |
+11
-14
@@ -50,6 +50,8 @@ Instructions for making a code change
|
||||
|
||||
The central development branch is ``master``, which should be clean and ready for release at any time. In general, all changes should be done as feature branches based off of ``master``.
|
||||
|
||||
If you want to do solely documentation changes, base them and PR to the branch ``doc-fixes``. This branch also has its own `RTD build`_.
|
||||
|
||||
Here's how to make a one-off code change.
|
||||
|
||||
1. **Choose a descriptive branch name.** It should be lowercase, hyphen-separated, and a noun describing the change (so, ``fuzzy-rules``, but not ``implement-fuzzy-rules``). Also, it shouldn't start with ``hotfix`` or ``release``.
|
||||
@@ -109,12 +111,6 @@ Here's how to make a one-off code change.
|
||||
|
||||
- Before making a commit ensure that all automated tests still pass:
|
||||
|
||||
.. code-block::
|
||||
|
||||
$ make test
|
||||
|
||||
If you don't have ``make``, do:
|
||||
|
||||
.. code-block::
|
||||
|
||||
$ pytest -v
|
||||
@@ -127,18 +123,18 @@ Here's how to make a one-off code change.
|
||||
|
||||
prior to running the tests.
|
||||
|
||||
- To actually make the commit (this will trigger tests for yapf, lint and pep8 automatically):
|
||||
- If you want run style & type checks before committing run
|
||||
|
||||
.. code-block::
|
||||
|
||||
$ pre-commit run -a
|
||||
|
||||
- To actually make the commit (this will trigger tests style & type checks automatically):
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ git add your-file-changed.py
|
||||
|
||||
- yapf may change code formatting, make sure to re-add them to your commit.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ git commit -a -m "your-commit-message-here"
|
||||
|
||||
- Finally, push it to your GitHub fork, run:
|
||||
|
||||
.. code-block:: bash
|
||||
@@ -196,7 +192,7 @@ Style commandments
|
||||
Assert comparison order
|
||||
#######################
|
||||
|
||||
- assert statements should compare in **actual** == **expected** order.
|
||||
Assert statements should compare in **actual** == **expected** order.
|
||||
For example (assuming ``test_call`` is the thing being tested):
|
||||
|
||||
.. code-block:: python
|
||||
@@ -256,3 +252,4 @@ break the API classes. For example:
|
||||
.. _`here`: https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html
|
||||
.. _`Black`: https://black.readthedocs.io/en/stable/index.html
|
||||
.. _`popular editors`: https://black.readthedocs.io/en/stable/editor_integration.html
|
||||
.. _`RTD build`: https://python-telegram-bot.readthedocs.io/en/doc-fixes
|
||||
|
||||
@@ -27,4 +27,4 @@ Hey! You're PRing? Cool! Please have a look at the below checklist. It's here to
|
||||
- [ ] Added new handlers for new update types
|
||||
- [ ] Added new filters for new message (sub)types
|
||||
- [ ] Added or updated documentation for the changed class(es) and/or method(s)
|
||||
- [ ] Updated the Bot API version number in all places in `README.rst` and `README_RAW.rst`, including the badge
|
||||
- [ ] Updated the Bot API version number in all places: `README.rst` and `README_RAW.rst` (including the badge), as well as `telegram.constants.BOT_API_VERSION`
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
name: Warning maintainers
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- requirements.txt
|
||||
- requirements-dev.txt
|
||||
- .pre-commit-config.yaml
|
||||
jobs:
|
||||
job:
|
||||
runs-on: ubuntu-latest
|
||||
name: about pre-commit and dependency change
|
||||
steps:
|
||||
- name: running the check
|
||||
uses: Poolitzer/notifier-action@master
|
||||
with:
|
||||
notify-message: Hey! Looks like you edited the (dev) requirements or the pre-commit hooks. I'm just a friendly reminder to keep the pre-commit hook versions in sync with the dev requirements and the additional dependencies for the hooks in sync with the requirements :)
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
+28
-6
@@ -1,6 +1,6 @@
|
||||
# Make sure that
|
||||
# * the revs specified here match requirements-dev.txt
|
||||
# * the makefile checks the same files as pre-commit
|
||||
# * the additional_dependencies here match requirements.txt
|
||||
repos:
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 20.8b1
|
||||
@@ -14,21 +14,43 @@ repos:
|
||||
hooks:
|
||||
- id: flake8
|
||||
- repo: https://github.com/PyCQA/pylint
|
||||
rev: pylint-2.6.0
|
||||
rev: pylint-2.7.2
|
||||
hooks:
|
||||
- id: pylint
|
||||
files: ^(telegram|examples)/.*\.py$
|
||||
args:
|
||||
- --rcfile=setup.cfg
|
||||
- --rcfile=setup.cfg
|
||||
additional_dependencies:
|
||||
- certifi
|
||||
- tornado>=5.1
|
||||
- APScheduler==3.6.3
|
||||
- . # this basically does `pip install -e .`
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: v0.800
|
||||
rev: v0.812
|
||||
hooks:
|
||||
- id: mypy
|
||||
files: ^(telegram|examples)/.*\.py$
|
||||
name: mypy-ptb
|
||||
files: ^telegram/.*\.py$
|
||||
additional_dependencies:
|
||||
- certifi
|
||||
- tornado>=5.1
|
||||
- APScheduler==3.6.3
|
||||
- . # this basically does `pip install -e .`
|
||||
- id: mypy
|
||||
name: mypy-examples
|
||||
files: ^examples/.*\.py$
|
||||
args:
|
||||
- --no-strict-optional
|
||||
- --follow-imports=silent
|
||||
additional_dependencies:
|
||||
- certifi
|
||||
- tornado>=5.1
|
||||
- APScheduler==3.6.3
|
||||
- . # this basically does `pip install -e .`
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v2.10.0
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
files: ^(telegram|examples|tests)/.*\.py$
|
||||
args:
|
||||
- --py36-plus
|
||||
- --py36-plus
|
||||
|
||||
+17
-5
@@ -1,10 +1,22 @@
|
||||
# syntax: https://docs.readthedocs.io/en/latest/yaml-config.html
|
||||
# .readthedocs.yaml
|
||||
# Read the Docs configuration file
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
|
||||
# Required
|
||||
version: 2
|
||||
|
||||
# Build documentation in the docs/ directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/source/conf.py
|
||||
|
||||
# Optionally build your docs in additional formats such as PDF
|
||||
formats:
|
||||
- pdf
|
||||
- pdf
|
||||
|
||||
# Optionally set the version of Python and requirements required to build your docs
|
||||
python:
|
||||
setup_py_install: true
|
||||
version: 3
|
||||
|
||||
requirements_file: docs/requirements-docs.txt
|
||||
install:
|
||||
- method: pip
|
||||
path: .
|
||||
- requirements: docs/requirements-docs.txt
|
||||
|
||||
+10
-2
@@ -2,12 +2,20 @@ Credits
|
||||
=======
|
||||
|
||||
``python-telegram-bot`` was originally created by
|
||||
`Leandro Toledo <https://github.com/leandrotoledo>`_ and is now maintained by `Hinrich Mahler <https://github.com/Bibo-Joshi>`_.
|
||||
`Leandro Toledo <https://github.com/leandrotoledo>`_.
|
||||
The current development team includes
|
||||
|
||||
- `Hinrich Mahler <https://github.com/Bibo-Joshi>`_ (maintainer)
|
||||
- `Poolitzer <https://github.com/Poolitzer>`_ (community liaison)
|
||||
- `Shivam <https://github.com/Starry69>`_
|
||||
- `Harshil <https://github.com/harshil21>`_
|
||||
|
||||
Emeritus maintainers include
|
||||
`Jannes Höke <https://github.com/jh0ker>`_ (`@jh0ker <https://t.me/jh0ker>`_ on Telegram),
|
||||
`Noam Meltzer <https://github.com/tsnoam>`_, `Pieter Schutz <https://github.com/eldinnie>`_ and `Jasmin Bom <https://github.com/jsmnbom>`_.
|
||||
|
||||
The maintainers are actively supported by `Poolitzer <https://github.com/Poolitzer>`_ in terms of development and community liaison.
|
||||
Vendored packages
|
||||
-----------------
|
||||
|
||||
We're vendoring urllib3 as part of ``python-telegram-bot`` which is distributed under the MIT
|
||||
license. For more info, full credits & license terms, the sources can be found here:
|
||||
|
||||
+25
@@ -2,6 +2,31 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
Version 13.4
|
||||
============
|
||||
*Released 2021-03-14*
|
||||
|
||||
**Major Changes:**
|
||||
|
||||
- Full support of Bot API 5.1 (`#2424`_)
|
||||
|
||||
**Minor changes, CI improvements, doc fixes and type hinting:**
|
||||
|
||||
- Improve ``Updater.set_webhook`` (`#2419`_)
|
||||
- Doc Fixes (`#2404`_)
|
||||
- Type Hinting Fixes (`#2425`_)
|
||||
- Update ``pre-commit`` Settings (`#2415`_)
|
||||
- Fix Logging for Vendored ``urllib3`` (`#2427`_)
|
||||
- Stabilize Tests (`#2409`_)
|
||||
|
||||
.. _`#2424`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2424
|
||||
.. _`#2419`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2419
|
||||
.. _`#2404`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2404
|
||||
.. _`#2425`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2425
|
||||
.. _`#2415`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2415
|
||||
.. _`#2427`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2427
|
||||
.. _`#2409`: https://github.com/python-telegram-bot/python-telegram-bot/pull/2409
|
||||
|
||||
Version 13.3
|
||||
============
|
||||
*Released 2021-02-19*
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
.DEFAULT_GOAL := help
|
||||
.PHONY: clean pep8 black lint test install
|
||||
|
||||
PYLINT := pylint
|
||||
PYTEST := pytest
|
||||
PEP8 := flake8
|
||||
BLACK := black
|
||||
MYPY := mypy
|
||||
PIP := pip
|
||||
|
||||
clean:
|
||||
rm -fr build
|
||||
rm -fr dist
|
||||
find . -name '*.pyc' -exec rm -f {} \;
|
||||
find . -name '*.pyo' -exec rm -f {} \;
|
||||
find . -name '*~' -exec rm -f {} \;
|
||||
find . -regex "./telegram[0-9]*.\(jpg\|mp3\|mp4\|ogg\|png\|webp\)" -exec rm {} \;
|
||||
|
||||
pep8:
|
||||
$(PEP8) telegram tests examples
|
||||
|
||||
black:
|
||||
$(BLACK) .
|
||||
|
||||
lint:
|
||||
$(PYLINT) --rcfile=setup.cfg telegram examples
|
||||
|
||||
mypy:
|
||||
$(MYPY) -p telegram
|
||||
$(MYPY) examples
|
||||
|
||||
test:
|
||||
$(PYTEST) -v
|
||||
|
||||
install:
|
||||
$(PIP) install -r requirements.txt -r requirements-dev.txt
|
||||
|
||||
help:
|
||||
@echo "Available targets:"
|
||||
@echo "- clean Clean up the source directory"
|
||||
@echo "- pep8 Check style with flake8"
|
||||
@echo "- lint Check style with pylint"
|
||||
@echo "- black Check style with black"
|
||||
@echo "- mypy Check type hinting with mypy"
|
||||
@echo "- test Run tests using pytest"
|
||||
@echo
|
||||
@echo "Available variables:"
|
||||
@echo "- PYLINT default: $(PYLINT)"
|
||||
@echo "- PYTEST default: $(PYTEST)"
|
||||
@echo "- PEP8 default: $(PEP8)"
|
||||
@echo "- BLACK default: $(BLACK)"
|
||||
@echo "- MYPY default: $(MYPY)"
|
||||
@echo "- PIP default: $(PIP)"
|
||||
+7
-2
@@ -20,7 +20,7 @@ We have a vibrant community of developers helping each other in our `Telegram gr
|
||||
:target: https://pypi.org/project/python-telegram-bot/
|
||||
:alt: Supported Python versions
|
||||
|
||||
.. image:: https://img.shields.io/badge/Bot%20API-5.0-blue?logo=telegram
|
||||
.. image:: https://img.shields.io/badge/Bot%20API-5.1-blue?logo=telegram
|
||||
:target: https://core.telegram.org/bots/api-changelog
|
||||
:alt: Supported Bot API versions
|
||||
|
||||
@@ -111,7 +111,7 @@ Installing both ``python-telegram-bot`` and ``python-telegram-bot-raw`` in conju
|
||||
Telegram API support
|
||||
====================
|
||||
|
||||
All types and methods of the Telegram Bot API **5.0** are supported.
|
||||
All types and methods of the Telegram Bot API **5.1** are supported.
|
||||
|
||||
==========
|
||||
Installing
|
||||
@@ -230,6 +230,11 @@ Contributing
|
||||
|
||||
Contributions of all sizes are welcome. Please review our `contribution guidelines <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/.github/CONTRIBUTING.rst>`_ to get started. You can also help by `reporting bugs <https://github.com/python-telegram-bot/python-telegram-bot/issues/new>`_.
|
||||
|
||||
========
|
||||
Donating
|
||||
========
|
||||
Occasionally we are asked if we accept donations to support the development. While we appreciate the thought, maintaining PTB is our hobby and we have almost no running costs for it. We therefore have nothing set up to accept donations. If you still want to donate, we kindly ask you to donate to another open source project/initiative of your choice instead.
|
||||
|
||||
=======
|
||||
License
|
||||
=======
|
||||
|
||||
+7
-2
@@ -20,7 +20,7 @@ We have a vibrant community of developers helping each other in our `Telegram gr
|
||||
:target: https://pypi.org/project/python-telegram-bot-raw/
|
||||
:alt: Supported Python versions
|
||||
|
||||
.. image:: https://img.shields.io/badge/Bot%20API-5.0-blue?logo=telegram
|
||||
.. image:: https://img.shields.io/badge/Bot%20API-5.1-blue?logo=telegram
|
||||
:target: https://core.telegram.org/bots/api-changelog
|
||||
:alt: Supported Bot API versions
|
||||
|
||||
@@ -105,7 +105,7 @@ Installing both ``python-telegram-bot`` and ``python-telegram-bot-raw`` in conju
|
||||
Telegram API support
|
||||
====================
|
||||
|
||||
All types and methods of the Telegram Bot API **5.0** are supported.
|
||||
All types and methods of the Telegram Bot API **5.1** are supported.
|
||||
|
||||
==========
|
||||
Installing
|
||||
@@ -212,6 +212,11 @@ Contributing
|
||||
|
||||
Contributions of all sizes are welcome. Please review our `contribution guidelines <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/.github/CONTRIBUTING.rst>`_ to get started. You can also help by `reporting bugs <https://github.com/python-telegram-bot/python-telegram-bot/issues/new>`_.
|
||||
|
||||
========
|
||||
Donating
|
||||
========
|
||||
Occasionally we are asked if we accept donations to support the development. While we appreciate the thought, maintaining PTB is our hobby and we have almost no running costs for it. We therefore have nothing set up to accept donations. If you still want to donate, we kindly ask you to donate to another open source project/initiative of your choice instead.
|
||||
|
||||
=======
|
||||
License
|
||||
=======
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
sphinx>=1.7.9
|
||||
sphinx_rtd_theme
|
||||
sphinx==3.5.2
|
||||
sphinx_rtd_theme==0.5.1
|
||||
sphinx-pypi-upload
|
||||
|
||||
+4
-4
@@ -24,7 +24,7 @@ sys.path.insert(0, os.path.abspath('../..'))
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
needs_sphinx = '1.7.9'
|
||||
needs_sphinx = '3.5.2'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
@@ -35,7 +35,7 @@ extensions = [
|
||||
]
|
||||
# Don't show type hints in the signature - that just makes it hardly readable
|
||||
# and we document the types anyway
|
||||
autodoc_typehints = 'description'
|
||||
autodoc_typehints = 'none'
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
@@ -61,9 +61,9 @@ author = u'Leandro Toledo'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '13.3' # telegram.__version__[:3]
|
||||
version = '13.4' # telegram.__version__[:3]
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '13.3' # telegram.__version__
|
||||
release = '13.4' # telegram.__version__
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
telegram.ChatInviteLink
|
||||
=======================
|
||||
|
||||
.. autoclass:: telegram.ChatInviteLink
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,6 @@
|
||||
telegram.ChatMemberUpdated
|
||||
==========================
|
||||
|
||||
.. autoclass:: telegram.ChatMemberUpdated
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,6 @@
|
||||
telegram.ext.ChatMemberHandler
|
||||
==============================
|
||||
|
||||
.. autoclass:: telegram.ext.ChatMemberHandler
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -21,6 +21,7 @@ Handlers
|
||||
telegram.ext.handler
|
||||
telegram.ext.callbackqueryhandler
|
||||
telegram.ext.choseninlineresulthandler
|
||||
telegram.ext.chatmemberhandler
|
||||
telegram.ext.commandhandler
|
||||
telegram.ext.conversationhandler
|
||||
telegram.ext.inlinequeryhandler
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
telegram.MessageAutoDeleteTimerChanged
|
||||
======================================
|
||||
|
||||
.. autoclass:: telegram.MessageAutoDeleteTimerChanged
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -13,8 +13,10 @@ telegram package
|
||||
telegram.callbackquery
|
||||
telegram.chat
|
||||
telegram.chataction
|
||||
telegram.chatinvitelink
|
||||
telegram.chatlocation
|
||||
telegram.chatmember
|
||||
telegram.chatmemberupdated
|
||||
telegram.chatpermissions
|
||||
telegram.chatphoto
|
||||
telegram.constants
|
||||
@@ -38,6 +40,7 @@ telegram package
|
||||
telegram.location
|
||||
telegram.loginurl
|
||||
telegram.message
|
||||
telegram.messageautodeletetimerchanged
|
||||
telegram.messageid
|
||||
telegram.messageentity
|
||||
telegram.parsemode
|
||||
@@ -57,6 +60,9 @@ telegram package
|
||||
telegram.video
|
||||
telegram.videonote
|
||||
telegram.voice
|
||||
telegram.voicechatstarted
|
||||
telegram.voicechatended
|
||||
telegram.voicechatparticipantsinvited
|
||||
telegram.webhookinfo
|
||||
|
||||
Stickers
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.VoiceChatEnded
|
||||
=======================
|
||||
|
||||
.. autoclass:: telegram.VoiceChatEnded
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.VoiceChatParticipantsInvited
|
||||
=====================================
|
||||
|
||||
.. autoclass:: telegram.VoiceChatParticipantsInvited
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
telegram.VoiceChatStarted
|
||||
=========================
|
||||
|
||||
.. autoclass:: telegram.VoiceChatStarted
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
+13
-14
@@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=W0613, C0116
|
||||
# type: ignore[union-attr]
|
||||
# pylint: disable=C0116
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
"""
|
||||
@@ -37,7 +36,7 @@ logger = logging.getLogger(__name__)
|
||||
GENDER, PHOTO, LOCATION, BIO = range(4)
|
||||
|
||||
|
||||
def start(update: Update, context: CallbackContext) -> int:
|
||||
def start(update: Update, _: CallbackContext) -> int:
|
||||
reply_keyboard = [['Boy', 'Girl', 'Other']]
|
||||
|
||||
update.message.reply_text(
|
||||
@@ -50,7 +49,7 @@ def start(update: Update, context: CallbackContext) -> int:
|
||||
return GENDER
|
||||
|
||||
|
||||
def gender(update: Update, context: CallbackContext) -> int:
|
||||
def gender(update: Update, _: CallbackContext) -> int:
|
||||
user = update.message.from_user
|
||||
logger.info("Gender of %s: %s", user.first_name, update.message.text)
|
||||
update.message.reply_text(
|
||||
@@ -62,52 +61,52 @@ def gender(update: Update, context: CallbackContext) -> int:
|
||||
return PHOTO
|
||||
|
||||
|
||||
def photo(update: Update, context: CallbackContext) -> int:
|
||||
def photo(update: Update, _: CallbackContext) -> int:
|
||||
user = update.message.from_user
|
||||
photo_file = update.message.photo[-1].get_file()
|
||||
photo_file.download('user_photo.jpg')
|
||||
logger.info("Photo of %s: %s", user.first_name, 'user_photo.jpg')
|
||||
update.message.reply_text(
|
||||
'Gorgeous! Now, send me your location please, ' 'or send /skip if you don\'t want to.'
|
||||
'Gorgeous! Now, send me your location please, or send /skip if you don\'t want to.'
|
||||
)
|
||||
|
||||
return LOCATION
|
||||
|
||||
|
||||
def skip_photo(update: Update, context: CallbackContext) -> int:
|
||||
def skip_photo(update: Update, _: CallbackContext) -> int:
|
||||
user = update.message.from_user
|
||||
logger.info("User %s did not send a photo.", user.first_name)
|
||||
update.message.reply_text(
|
||||
'I bet you look great! Now, send me your location please, ' 'or send /skip.'
|
||||
'I bet you look great! Now, send me your location please, or send /skip.'
|
||||
)
|
||||
|
||||
return LOCATION
|
||||
|
||||
|
||||
def location(update: Update, context: CallbackContext) -> int:
|
||||
def location(update: Update, _: CallbackContext) -> int:
|
||||
user = update.message.from_user
|
||||
user_location = update.message.location
|
||||
logger.info(
|
||||
"Location of %s: %f / %f", user.first_name, user_location.latitude, user_location.longitude
|
||||
)
|
||||
update.message.reply_text(
|
||||
'Maybe I can visit you sometime! ' 'At last, tell me something about yourself.'
|
||||
'Maybe I can visit you sometime! At last, tell me something about yourself.'
|
||||
)
|
||||
|
||||
return BIO
|
||||
|
||||
|
||||
def skip_location(update: Update, context: CallbackContext) -> int:
|
||||
def skip_location(update: Update, _: CallbackContext) -> int:
|
||||
user = update.message.from_user
|
||||
logger.info("User %s did not send a location.", user.first_name)
|
||||
update.message.reply_text(
|
||||
'You seem a bit paranoid! ' 'At last, tell me something about yourself.'
|
||||
'You seem a bit paranoid! At last, tell me something about yourself.'
|
||||
)
|
||||
|
||||
return BIO
|
||||
|
||||
|
||||
def bio(update: Update, context: CallbackContext) -> int:
|
||||
def bio(update: Update, _: CallbackContext) -> int:
|
||||
user = update.message.from_user
|
||||
logger.info("Bio of %s: %s", user.first_name, update.message.text)
|
||||
update.message.reply_text('Thank you! I hope we can talk again some day.')
|
||||
@@ -115,7 +114,7 @@ def bio(update: Update, context: CallbackContext) -> int:
|
||||
return ConversationHandler.END
|
||||
|
||||
|
||||
def cancel(update: Update, context: CallbackContext) -> int:
|
||||
def cancel(update: Update, _: CallbackContext) -> int:
|
||||
user = update.message.from_user
|
||||
logger.info("User %s canceled the conversation.", user.first_name)
|
||||
update.message.reply_text(
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=W0613, C0116
|
||||
# type: ignore[union-attr]
|
||||
# pylint: disable=C0116
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
"""
|
||||
@@ -54,7 +53,7 @@ def facts_to_str(user_data: Dict[str, str]) -> str:
|
||||
return "\n".join(facts).join(['\n', '\n'])
|
||||
|
||||
|
||||
def start(update: Update, context: CallbackContext) -> int:
|
||||
def start(update: Update, _: CallbackContext) -> int:
|
||||
update.message.reply_text(
|
||||
"Hi! My name is Doctor Botter. I will hold a more complex conversation with you. "
|
||||
"Why don't you tell me something about yourself?",
|
||||
@@ -72,9 +71,9 @@ def regular_choice(update: Update, context: CallbackContext) -> int:
|
||||
return TYPING_REPLY
|
||||
|
||||
|
||||
def custom_choice(update: Update, context: CallbackContext) -> int:
|
||||
def custom_choice(update: Update, _: CallbackContext) -> int:
|
||||
update.message.reply_text(
|
||||
'Alright, please send me the category first, ' 'for example "Most impressive skill"'
|
||||
'Alright, please send me the category first, for example "Most impressive skill"'
|
||||
)
|
||||
|
||||
return TYPING_CHOICE
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=W0613, C0116
|
||||
# type: ignore[union-attr]
|
||||
# pylint: disable=C0116
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
"""Bot that explains Telegram's "Deep Linking Parameters" functionality.
|
||||
@@ -79,7 +78,7 @@ def deep_linked_level_2(update: Update, context: CallbackContext) -> None:
|
||||
update.message.reply_text(text, parse_mode=ParseMode.MARKDOWN, disable_web_page_preview=True)
|
||||
|
||||
|
||||
def deep_linked_level_3(update: Update, context: CallbackContext) -> None:
|
||||
def deep_linked_level_3(update: Update, _: CallbackContext) -> None:
|
||||
"""Reached through the USING_ENTITIES payload"""
|
||||
update.message.reply_text(
|
||||
"It is also possible to make deep-linking using InlineKeyboardButtons.",
|
||||
@@ -104,7 +103,7 @@ def deep_linked_level_4(update: Update, context: CallbackContext) -> None:
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
def main() -> None:
|
||||
"""Start the bot."""
|
||||
# Create the Updater and pass it your bot's token.
|
||||
updater = Updater("TOKEN")
|
||||
|
||||
+5
-6
@@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=W0613, C0116
|
||||
# type: ignore[union-attr]
|
||||
# pylint: disable=C0116
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
"""
|
||||
@@ -31,22 +30,22 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
# Define a few command handlers. These usually take the two arguments update and
|
||||
# context. Error handlers also receive the raised TelegramError object in error.
|
||||
def start(update: Update, context: CallbackContext) -> None:
|
||||
def start(update: Update, _: CallbackContext) -> None:
|
||||
"""Send a message when the command /start is issued."""
|
||||
update.message.reply_text('Hi!')
|
||||
|
||||
|
||||
def help_command(update: Update, context: CallbackContext) -> None:
|
||||
def help_command(update: Update, _: CallbackContext) -> None:
|
||||
"""Send a message when the command /help is issued."""
|
||||
update.message.reply_text('Help!')
|
||||
|
||||
|
||||
def echo(update: Update, context: CallbackContext) -> None:
|
||||
def echo(update: Update, _: CallbackContext) -> None:
|
||||
"""Echo the user message."""
|
||||
update.message.reply_text(update.message.text)
|
||||
|
||||
|
||||
def main():
|
||||
def main() -> None:
|
||||
"""Start the bot."""
|
||||
# Create the Updater and pass it your bot's token.
|
||||
updater = Updater("TOKEN")
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=W0613, C0116
|
||||
# type: ignore[union-attr]
|
||||
# pylint: disable=C0116
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
"""
|
||||
@@ -28,7 +27,7 @@ BOT_TOKEN = "TOKEN"
|
||||
DEVELOPER_CHAT_ID = 123456789
|
||||
|
||||
|
||||
def error_handler(update: Update, context: CallbackContext) -> None:
|
||||
def error_handler(update: object, context: CallbackContext) -> None:
|
||||
"""Log the error and send a telegram message to notify the developer."""
|
||||
# Log the error before we do anything else, so we can see it even if something breaks.
|
||||
logger.error(msg="Exception while handling an update:", exc_info=context.error)
|
||||
@@ -40,9 +39,10 @@ def error_handler(update: Update, context: CallbackContext) -> None:
|
||||
|
||||
# Build the message with some markup and additional information about what happened.
|
||||
# You might need to add some logic to deal with messages longer than the 4096 character limit.
|
||||
update_str = update.to_dict() if isinstance(update, Update) else str(update)
|
||||
message = (
|
||||
f'An exception was raised while handling an update\n'
|
||||
f'<pre>update = {html.escape(json.dumps(update.to_dict(), indent=2, ensure_ascii=False))}'
|
||||
f'<pre>update = {html.escape(json.dumps(update_str, indent=2, ensure_ascii=False))}'
|
||||
'</pre>\n\n'
|
||||
f'<pre>context.chat_data = {html.escape(str(context.chat_data))}</pre>\n\n'
|
||||
f'<pre>context.user_data = {html.escape(str(context.user_data))}</pre>\n\n'
|
||||
@@ -53,19 +53,19 @@ def error_handler(update: Update, context: CallbackContext) -> None:
|
||||
context.bot.send_message(chat_id=DEVELOPER_CHAT_ID, text=message, parse_mode=ParseMode.HTML)
|
||||
|
||||
|
||||
def bad_command(update: Update, context: CallbackContext) -> None:
|
||||
def bad_command(_: Update, context: CallbackContext) -> None:
|
||||
"""Raise an error to trigger the error handler."""
|
||||
context.bot.wrong_method_name()
|
||||
context.bot.wrong_method_name() # type: ignore[attr-defined]
|
||||
|
||||
|
||||
def start(update: Update, context: CallbackContext) -> None:
|
||||
def start(update: Update, _: CallbackContext) -> None:
|
||||
update.effective_message.reply_html(
|
||||
'Use /bad_command to cause an error.\n'
|
||||
f'Your chat id is <code>{update.effective_chat.id}</code>.'
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
def main() -> None:
|
||||
# Create the Updater and pass it your bot's token.
|
||||
updater = Updater(BOT_TOKEN)
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=W0613, C0116
|
||||
# type: ignore[union-attr]
|
||||
# pylint: disable=C0116
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
"""
|
||||
@@ -30,32 +29,34 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
# Define a few command handlers. These usually take the two arguments update and
|
||||
# context. Error handlers also receive the raised TelegramError object in error.
|
||||
def start(update: Update, context: CallbackContext) -> None:
|
||||
def start(update: Update, _: CallbackContext) -> None:
|
||||
"""Send a message when the command /start is issued."""
|
||||
update.message.reply_text('Hi!')
|
||||
|
||||
|
||||
def help_command(update: Update, context: CallbackContext) -> None:
|
||||
def help_command(update: Update, _: CallbackContext) -> None:
|
||||
"""Send a message when the command /help is issued."""
|
||||
update.message.reply_text('Help!')
|
||||
|
||||
|
||||
def inlinequery(update: Update, context: CallbackContext) -> None:
|
||||
def inlinequery(update: Update, _: CallbackContext) -> None:
|
||||
"""Handle the inline query."""
|
||||
query = update.inline_query.query
|
||||
results = [
|
||||
InlineQueryResultArticle(
|
||||
id=uuid4(), title="Caps", input_message_content=InputTextMessageContent(query.upper())
|
||||
id=str(uuid4()),
|
||||
title="Caps",
|
||||
input_message_content=InputTextMessageContent(query.upper()),
|
||||
),
|
||||
InlineQueryResultArticle(
|
||||
id=uuid4(),
|
||||
id=str(uuid4()),
|
||||
title="Bold",
|
||||
input_message_content=InputTextMessageContent(
|
||||
f"*{escape_markdown(query)}*", parse_mode=ParseMode.MARKDOWN
|
||||
),
|
||||
),
|
||||
InlineQueryResultArticle(
|
||||
id=uuid4(),
|
||||
id=str(uuid4()),
|
||||
title="Italic",
|
||||
input_message_content=InputTextMessageContent(
|
||||
f"_{escape_markdown(query)}_", parse_mode=ParseMode.MARKDOWN
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=W0613, C0116
|
||||
# type: ignore[union-attr]
|
||||
# pylint: disable=C0116
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
"""
|
||||
@@ -17,7 +16,7 @@ logging.basicConfig(
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def start(update: Update, context: CallbackContext) -> None:
|
||||
def start(update: Update, _: CallbackContext) -> None:
|
||||
keyboard = [
|
||||
[
|
||||
InlineKeyboardButton("Option 1", callback_data='1'),
|
||||
@@ -31,7 +30,7 @@ def start(update: Update, context: CallbackContext) -> None:
|
||||
update.message.reply_text('Please choose:', reply_markup=reply_markup)
|
||||
|
||||
|
||||
def button(update: Update, context: CallbackContext) -> None:
|
||||
def button(update: Update, _: CallbackContext) -> None:
|
||||
query = update.callback_query
|
||||
|
||||
# CallbackQueries need to be answered, even if no notification to the user is needed
|
||||
@@ -41,11 +40,11 @@ def button(update: Update, context: CallbackContext) -> None:
|
||||
query.edit_message_text(text=f"Selected option: {query.data}")
|
||||
|
||||
|
||||
def help_command(update: Update, context: CallbackContext) -> None:
|
||||
def help_command(update: Update, _: CallbackContext) -> None:
|
||||
update.message.reply_text("Use /start to test this bot.")
|
||||
|
||||
|
||||
def main():
|
||||
def main() -> None:
|
||||
# Create the Updater and pass it your bot's token.
|
||||
updater = Updater("TOKEN")
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=W0613, C0116
|
||||
# type: ignore[union-attr]
|
||||
# pylint: disable=C0116
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
"""Simple inline keyboard bot with multiple CallbackQueryHandlers.
|
||||
@@ -38,7 +37,7 @@ FIRST, SECOND = range(2)
|
||||
ONE, TWO, THREE, FOUR = range(4)
|
||||
|
||||
|
||||
def start(update: Update, context: CallbackContext) -> None:
|
||||
def start(update: Update, _: CallbackContext) -> int:
|
||||
"""Send message on `/start`."""
|
||||
# Get user that sent /start and log his name
|
||||
user = update.message.from_user
|
||||
@@ -60,7 +59,7 @@ def start(update: Update, context: CallbackContext) -> None:
|
||||
return FIRST
|
||||
|
||||
|
||||
def start_over(update: Update, context: CallbackContext) -> None:
|
||||
def start_over(update: Update, _: CallbackContext) -> int:
|
||||
"""Prompt same text & keyboard as `start` does but not as new message"""
|
||||
# Get CallbackQuery from Update
|
||||
query = update.callback_query
|
||||
@@ -81,7 +80,7 @@ def start_over(update: Update, context: CallbackContext) -> None:
|
||||
return FIRST
|
||||
|
||||
|
||||
def one(update: Update, context: CallbackContext) -> None:
|
||||
def one(update: Update, _: CallbackContext) -> int:
|
||||
"""Show new choice of buttons"""
|
||||
query = update.callback_query
|
||||
query.answer()
|
||||
@@ -98,7 +97,7 @@ def one(update: Update, context: CallbackContext) -> None:
|
||||
return FIRST
|
||||
|
||||
|
||||
def two(update: Update, context: CallbackContext) -> None:
|
||||
def two(update: Update, _: CallbackContext) -> int:
|
||||
"""Show new choice of buttons"""
|
||||
query = update.callback_query
|
||||
query.answer()
|
||||
@@ -115,7 +114,7 @@ def two(update: Update, context: CallbackContext) -> None:
|
||||
return FIRST
|
||||
|
||||
|
||||
def three(update: Update, context: CallbackContext) -> None:
|
||||
def three(update: Update, _: CallbackContext) -> int:
|
||||
"""Show new choice of buttons"""
|
||||
query = update.callback_query
|
||||
query.answer()
|
||||
@@ -133,7 +132,7 @@ def three(update: Update, context: CallbackContext) -> None:
|
||||
return SECOND
|
||||
|
||||
|
||||
def four(update: Update, context: CallbackContext) -> None:
|
||||
def four(update: Update, _: CallbackContext) -> int:
|
||||
"""Show new choice of buttons"""
|
||||
query = update.callback_query
|
||||
query.answer()
|
||||
@@ -150,7 +149,7 @@ def four(update: Update, context: CallbackContext) -> None:
|
||||
return FIRST
|
||||
|
||||
|
||||
def end(update: Update, context: CallbackContext) -> None:
|
||||
def end(update: Update, _: CallbackContext) -> int:
|
||||
"""Returns `ConversationHandler.END`, which tells the
|
||||
ConversationHandler that the conversation is over"""
|
||||
query = update.callback_query
|
||||
@@ -159,7 +158,7 @@ def end(update: Update, context: CallbackContext) -> None:
|
||||
return ConversationHandler.END
|
||||
|
||||
|
||||
def main():
|
||||
def main() -> None:
|
||||
# Create the Updater and pass it your bot's token.
|
||||
updater = Updater("TOKEN")
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=W0613, C0116
|
||||
# type: ignore[union-attr]
|
||||
# pylint: disable=C0116
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
"""
|
||||
@@ -16,6 +15,7 @@ bot.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import Tuple, Dict, Any
|
||||
|
||||
from telegram import InlineKeyboardMarkup, InlineKeyboardButton, Update
|
||||
from telegram.ext import (
|
||||
@@ -64,14 +64,14 @@ END = ConversationHandler.END
|
||||
|
||||
|
||||
# Helper
|
||||
def _name_switcher(level):
|
||||
def _name_switcher(level: str) -> Tuple[str, str]:
|
||||
if level == PARENTS:
|
||||
return 'Father', 'Mother'
|
||||
return 'Brother', 'Sister'
|
||||
|
||||
|
||||
# Top level conversation callbacks
|
||||
def start(update: Update, context: CallbackContext) -> None:
|
||||
def start(update: Update, context: CallbackContext) -> str:
|
||||
"""Select an action: Adding parent/child or show data."""
|
||||
text = (
|
||||
'You may add a familiy member, yourself show the gathered data or end the '
|
||||
@@ -103,7 +103,7 @@ def start(update: Update, context: CallbackContext) -> None:
|
||||
return SELECTING_ACTION
|
||||
|
||||
|
||||
def adding_self(update: Update, context: CallbackContext) -> None:
|
||||
def adding_self(update: Update, context: CallbackContext) -> str:
|
||||
"""Add information about youself."""
|
||||
context.user_data[CURRENT_LEVEL] = SELF
|
||||
text = 'Okay, please tell me about yourself.'
|
||||
@@ -116,10 +116,10 @@ def adding_self(update: Update, context: CallbackContext) -> None:
|
||||
return DESCRIBING_SELF
|
||||
|
||||
|
||||
def show_data(update: Update, context: CallbackContext) -> None:
|
||||
def show_data(update: Update, context: CallbackContext) -> str:
|
||||
"""Pretty print gathered data."""
|
||||
|
||||
def prettyprint(user_data, level):
|
||||
def prettyprint(user_data: Dict[str, Any], level: str) -> str:
|
||||
people = user_data.get(level)
|
||||
if not people:
|
||||
return '\nNo information yet.'
|
||||
@@ -151,14 +151,14 @@ def show_data(update: Update, context: CallbackContext) -> None:
|
||||
return SHOWING
|
||||
|
||||
|
||||
def stop(update: Update, context: CallbackContext) -> None:
|
||||
def stop(update: Update, _: CallbackContext) -> int:
|
||||
"""End Conversation by command."""
|
||||
update.message.reply_text('Okay, bye.')
|
||||
|
||||
return END
|
||||
|
||||
|
||||
def end(update: Update, context: CallbackContext) -> None:
|
||||
def end(update: Update, _: CallbackContext) -> int:
|
||||
"""End conversation from InlineKeyboardButton."""
|
||||
update.callback_query.answer()
|
||||
|
||||
@@ -169,7 +169,7 @@ def end(update: Update, context: CallbackContext) -> None:
|
||||
|
||||
|
||||
# Second level conversation callbacks
|
||||
def select_level(update: Update, context: CallbackContext) -> None:
|
||||
def select_level(update: Update, _: CallbackContext) -> str:
|
||||
"""Choose to add a parent or a child."""
|
||||
text = 'You may add a parent or a child. Also you can show the gathered data or go back.'
|
||||
buttons = [
|
||||
@@ -190,7 +190,7 @@ def select_level(update: Update, context: CallbackContext) -> None:
|
||||
return SELECTING_LEVEL
|
||||
|
||||
|
||||
def select_gender(update: Update, context: CallbackContext) -> None:
|
||||
def select_gender(update: Update, context: CallbackContext) -> str:
|
||||
"""Choose to add mother or father."""
|
||||
level = update.callback_query.data
|
||||
context.user_data[CURRENT_LEVEL] = level
|
||||
@@ -217,7 +217,7 @@ def select_gender(update: Update, context: CallbackContext) -> None:
|
||||
return SELECTING_GENDER
|
||||
|
||||
|
||||
def end_second_level(update: Update, context: CallbackContext) -> None:
|
||||
def end_second_level(update: Update, context: CallbackContext) -> int:
|
||||
"""Return to top level conversation."""
|
||||
context.user_data[START_OVER] = True
|
||||
start(update, context)
|
||||
@@ -226,7 +226,7 @@ def end_second_level(update: Update, context: CallbackContext) -> None:
|
||||
|
||||
|
||||
# Third level callbacks
|
||||
def select_feature(update: Update, context: CallbackContext) -> None:
|
||||
def select_feature(update: Update, context: CallbackContext) -> str:
|
||||
"""Select a feature to update for the person."""
|
||||
buttons = [
|
||||
[
|
||||
@@ -253,7 +253,7 @@ def select_feature(update: Update, context: CallbackContext) -> None:
|
||||
return SELECTING_FEATURE
|
||||
|
||||
|
||||
def ask_for_input(update: Update, context: CallbackContext) -> None:
|
||||
def ask_for_input(update: Update, context: CallbackContext) -> str:
|
||||
"""Prompt user to input data for selected feature."""
|
||||
context.user_data[CURRENT_FEATURE] = update.callback_query.data
|
||||
text = 'Okay, tell me.'
|
||||
@@ -264,7 +264,7 @@ def ask_for_input(update: Update, context: CallbackContext) -> None:
|
||||
return TYPING
|
||||
|
||||
|
||||
def save_input(update: Update, context: CallbackContext) -> None:
|
||||
def save_input(update: Update, context: CallbackContext) -> str:
|
||||
"""Save input for feature and return to feature selection."""
|
||||
user_data = context.user_data
|
||||
user_data[FEATURES][user_data[CURRENT_FEATURE]] = update.message.text
|
||||
@@ -274,7 +274,7 @@ def save_input(update: Update, context: CallbackContext) -> None:
|
||||
return select_feature(update, context)
|
||||
|
||||
|
||||
def end_describing(update: Update, context: CallbackContext) -> None:
|
||||
def end_describing(update: Update, context: CallbackContext) -> int:
|
||||
"""End gathering of features and return to parent conversation."""
|
||||
user_data = context.user_data
|
||||
level = user_data[CURRENT_LEVEL]
|
||||
@@ -292,14 +292,14 @@ def end_describing(update: Update, context: CallbackContext) -> None:
|
||||
return END
|
||||
|
||||
|
||||
def stop_nested(update: Update, context: CallbackContext) -> None:
|
||||
def stop_nested(update: Update, _: CallbackContext) -> str:
|
||||
"""Completely end conversation from within nested conversation."""
|
||||
update.message.reply_text('Okay, bye.')
|
||||
|
||||
return STOPPING
|
||||
|
||||
|
||||
def main():
|
||||
def main() -> None:
|
||||
# Create the Updater and pass it your bot's token.
|
||||
updater = Updater("TOKEN")
|
||||
|
||||
|
||||
+12
-13
@@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=W0613, C0116
|
||||
# type: ignore[union-attr]
|
||||
# pylint: disable=C0116
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
"""
|
||||
@@ -24,7 +23,7 @@ logging.basicConfig(
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def msg(update: Update, context: CallbackContext) -> None:
|
||||
def msg(update: Update, _: CallbackContext) -> None:
|
||||
# If we received any passport data
|
||||
passport_data = update.message.passport_data
|
||||
if passport_data:
|
||||
@@ -65,19 +64,19 @@ def msg(update: Update, context: CallbackContext) -> None:
|
||||
actual_file.download()
|
||||
if data.type in ('passport', 'driver_license', 'identity_card', 'internal_passport'):
|
||||
if data.front_side:
|
||||
file = data.front_side.get_file()
|
||||
print(data.type, file)
|
||||
file.download()
|
||||
front_file = data.front_side.get_file()
|
||||
print(data.type, front_file)
|
||||
front_file.download()
|
||||
if data.type in ('driver_license' and 'identity_card'):
|
||||
if data.reverse_side:
|
||||
file = data.reverse_side.get_file()
|
||||
print(data.type, file)
|
||||
file.download()
|
||||
reverse_file = data.reverse_side.get_file()
|
||||
print(data.type, reverse_file)
|
||||
reverse_file.download()
|
||||
if data.type in ('passport', 'driver_license', 'identity_card', 'internal_passport'):
|
||||
if data.selfie:
|
||||
file = data.selfie.get_file()
|
||||
print(data.type, file)
|
||||
file.download()
|
||||
selfie_file = data.selfie.get_file()
|
||||
print(data.type, selfie_file)
|
||||
selfie_file.download()
|
||||
if data.type in (
|
||||
'passport',
|
||||
'driver_license',
|
||||
@@ -96,7 +95,7 @@ def msg(update: Update, context: CallbackContext) -> None:
|
||||
actual_file.download()
|
||||
|
||||
|
||||
def main():
|
||||
def main() -> None:
|
||||
"""Start the bot."""
|
||||
# Create the Updater and pass it your token and private key
|
||||
updater = Updater("TOKEN", private_key=open('private.key', 'rb').read())
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=W0613, C0116
|
||||
# type: ignore[union-attr]
|
||||
# pylint: disable=C0116
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
"""
|
||||
@@ -28,7 +27,7 @@ logging.basicConfig(
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def start_callback(update: Update, context: CallbackContext) -> None:
|
||||
def start_callback(update: Update, _: CallbackContext) -> None:
|
||||
msg = "Use /shipping to get an invoice for shipping-payment, "
|
||||
msg += "or /noshipping for an invoice without shipping."
|
||||
update.message.reply_text(msg)
|
||||
@@ -91,7 +90,7 @@ def start_without_shipping_callback(update: Update, context: CallbackContext) ->
|
||||
)
|
||||
|
||||
|
||||
def shipping_callback(update: Update, context: CallbackContext) -> None:
|
||||
def shipping_callback(update: Update, _: CallbackContext) -> None:
|
||||
query = update.shipping_query
|
||||
# check the payload, is this from your bot?
|
||||
if query.invoice_payload != 'Custom-Payload':
|
||||
@@ -109,7 +108,7 @@ def shipping_callback(update: Update, context: CallbackContext) -> None:
|
||||
|
||||
|
||||
# after (optional) shipping, it's the pre-checkout
|
||||
def precheckout_callback(update: Update, context: CallbackContext) -> None:
|
||||
def precheckout_callback(update: Update, _: CallbackContext) -> None:
|
||||
query = update.pre_checkout_query
|
||||
# check the payload, is this from your bot?
|
||||
if query.invoice_payload != 'Custom-Payload':
|
||||
@@ -120,12 +119,12 @@ def precheckout_callback(update: Update, context: CallbackContext) -> None:
|
||||
|
||||
|
||||
# finally, after contacting the payment provider...
|
||||
def successful_payment_callback(update: Update, context: CallbackContext) -> None:
|
||||
def successful_payment_callback(update: Update, _: CallbackContext) -> None:
|
||||
# do something after successfully receiving payment?
|
||||
update.message.reply_text("Thank you for your payment!")
|
||||
|
||||
|
||||
def main():
|
||||
def main() -> None:
|
||||
# Create the Updater and pass it your bot's token.
|
||||
updater = Updater("TOKEN")
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=W0613, C0116, C0103
|
||||
# type: ignore[union-attr]
|
||||
# pylint: disable=C0116
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
"""
|
||||
@@ -16,6 +15,7 @@ bot.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import Dict
|
||||
|
||||
from telegram import ReplyKeyboardMarkup, Update
|
||||
from telegram.ext import (
|
||||
@@ -46,7 +46,7 @@ reply_keyboard = [
|
||||
markup = ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True)
|
||||
|
||||
|
||||
def facts_to_str(user_data):
|
||||
def facts_to_str(user_data: Dict[str, str]) -> str:
|
||||
facts = list()
|
||||
|
||||
for key, value in user_data.items():
|
||||
@@ -55,7 +55,7 @@ def facts_to_str(user_data):
|
||||
return "\n".join(facts).join(['\n', '\n'])
|
||||
|
||||
|
||||
def start(update: Update, context: CallbackContext) -> None:
|
||||
def start(update: Update, context: CallbackContext) -> int:
|
||||
reply_text = "Hi! My name is Doctor Botter."
|
||||
if context.user_data:
|
||||
reply_text += (
|
||||
@@ -72,7 +72,7 @@ def start(update: Update, context: CallbackContext) -> None:
|
||||
return CHOOSING
|
||||
|
||||
|
||||
def regular_choice(update: Update, context: CallbackContext) -> None:
|
||||
def regular_choice(update: Update, context: CallbackContext) -> int:
|
||||
text = update.message.text.lower()
|
||||
context.user_data['choice'] = text
|
||||
if context.user_data.get(text):
|
||||
@@ -86,7 +86,7 @@ def regular_choice(update: Update, context: CallbackContext) -> None:
|
||||
return TYPING_REPLY
|
||||
|
||||
|
||||
def custom_choice(update: Update, context: CallbackContext) -> None:
|
||||
def custom_choice(update: Update, _: CallbackContext) -> int:
|
||||
update.message.reply_text(
|
||||
'Alright, please send me the category first, ' 'for example "Most impressive skill"'
|
||||
)
|
||||
@@ -94,7 +94,7 @@ def custom_choice(update: Update, context: CallbackContext) -> None:
|
||||
return TYPING_CHOICE
|
||||
|
||||
|
||||
def received_information(update: Update, context: CallbackContext) -> None:
|
||||
def received_information(update: Update, context: CallbackContext) -> int:
|
||||
text = update.message.text
|
||||
category = context.user_data['choice']
|
||||
context.user_data[category] = text.lower()
|
||||
@@ -117,7 +117,7 @@ def show_data(update: Update, context: CallbackContext) -> None:
|
||||
)
|
||||
|
||||
|
||||
def done(update: Update, context: CallbackContext) -> None:
|
||||
def done(update: Update, context: CallbackContext) -> int:
|
||||
if 'choice' in context.user_data:
|
||||
del context.user_data['choice']
|
||||
|
||||
@@ -127,13 +127,13 @@ def done(update: Update, context: CallbackContext) -> None:
|
||||
return ConversationHandler.END
|
||||
|
||||
|
||||
def main():
|
||||
def main() -> None:
|
||||
# Create the Updater and pass it your bot's token.
|
||||
pp = PicklePersistence(filename='conversationbot')
|
||||
updater = Updater("TOKEN", persistence=pp)
|
||||
persistence = PicklePersistence(filename='conversationbot')
|
||||
updater = Updater("TOKEN", persistence=persistence)
|
||||
|
||||
# Get the dispatcher to register handlers
|
||||
dp = updater.dispatcher
|
||||
dispatcher = updater.dispatcher
|
||||
|
||||
# Add conversation handler with the states CHOOSING, TYPING_CHOICE and TYPING_REPLY
|
||||
conv_handler = ConversationHandler(
|
||||
@@ -162,10 +162,10 @@ def main():
|
||||
persistent=True,
|
||||
)
|
||||
|
||||
dp.add_handler(conv_handler)
|
||||
dispatcher.add_handler(conv_handler)
|
||||
|
||||
show_data_handler = CommandHandler('show_data', show_data)
|
||||
dp.add_handler(show_data_handler)
|
||||
dispatcher.add_handler(show_data_handler)
|
||||
|
||||
# Start the Bot
|
||||
updater.start_polling()
|
||||
|
||||
+5
-6
@@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=W0613, C0116
|
||||
# type: ignore[union-attr]
|
||||
# pylint: disable=C0116
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
"""
|
||||
@@ -35,7 +34,7 @@ logging.basicConfig(
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def start(update: Update, context: CallbackContext) -> None:
|
||||
def start(update: Update, _: CallbackContext) -> None:
|
||||
"""Inform user about what this bot can do"""
|
||||
update.message.reply_text(
|
||||
'Please select /poll to get a Poll, /quiz to get a Quiz or /preview'
|
||||
@@ -121,7 +120,7 @@ def receive_quiz_answer(update: Update, context: CallbackContext) -> None:
|
||||
context.bot.stop_poll(quiz_data["chat_id"], quiz_data["message_id"])
|
||||
|
||||
|
||||
def preview(update: Update, context: CallbackContext) -> None:
|
||||
def preview(update: Update, _: CallbackContext) -> None:
|
||||
"""Ask user to create a poll and display a preview of it"""
|
||||
# using this without a type lets the user chooses what he wants (quiz or poll)
|
||||
button = [[KeyboardButton("Press me!", request_poll=KeyboardButtonPollType())]]
|
||||
@@ -132,7 +131,7 @@ def preview(update: Update, context: CallbackContext) -> None:
|
||||
)
|
||||
|
||||
|
||||
def receive_poll(update: Update, context: CallbackContext) -> None:
|
||||
def receive_poll(update: Update, _: CallbackContext) -> None:
|
||||
"""On receiving polls, reply to it by a closed poll copying the received poll"""
|
||||
actual_poll = update.effective_message.poll
|
||||
# Only need to set the question and options, since all other parameters don't matter for
|
||||
@@ -146,7 +145,7 @@ def receive_poll(update: Update, context: CallbackContext) -> None:
|
||||
)
|
||||
|
||||
|
||||
def help_handler(update: Update, context: CallbackContext) -> None:
|
||||
def help_handler(update: Update, _: CallbackContext) -> None:
|
||||
"""Display a help message"""
|
||||
update.message.reply_text("Use /quiz, /poll or /preview to test this bot.")
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ def main() -> NoReturn:
|
||||
sleep(1)
|
||||
except Unauthorized:
|
||||
# The user has removed or blocked the bot.
|
||||
UPDATE_ID += 1 # type: ignore[operator]
|
||||
UPDATE_ID += 1
|
||||
|
||||
|
||||
def echo(bot: telegram.Bot) -> None:
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=W0613, C0116
|
||||
# type: ignore[union-attr]
|
||||
# pylint: disable=C0116
|
||||
# This program is dedicated to the public domain under the CC0 license.
|
||||
|
||||
"""
|
||||
@@ -34,17 +33,17 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
# Define a few command handlers. These usually take the two arguments update and
|
||||
# context. Error handlers also receive the raised TelegramError object in error.
|
||||
def start(update: Update, context: CallbackContext) -> None:
|
||||
def start(update: Update, _: CallbackContext) -> None:
|
||||
update.message.reply_text('Hi! Use /set <seconds> to set a timer')
|
||||
|
||||
|
||||
def alarm(context):
|
||||
def alarm(context: CallbackContext) -> None:
|
||||
"""Send the alarm message."""
|
||||
job = context.job
|
||||
context.bot.send_message(job.context, text='Beep!')
|
||||
|
||||
|
||||
def remove_job_if_exists(name, context):
|
||||
def remove_job_if_exists(name: str, context: CallbackContext) -> bool:
|
||||
"""Remove job with given name. Returns whether job was removed."""
|
||||
current_jobs = context.job_queue.get_jobs_by_name(name)
|
||||
if not current_jobs:
|
||||
@@ -84,7 +83,7 @@ def unset(update: Update, context: CallbackContext) -> None:
|
||||
update.message.reply_text(text)
|
||||
|
||||
|
||||
def main():
|
||||
def main() -> None:
|
||||
"""Run bot."""
|
||||
# Create the Updater and pass it your bot's token.
|
||||
updater = Updater("TOKEN")
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
cryptography!=3.4,!=3.4.1,!=3.4.2,!=3.4.3
|
||||
|
||||
pre-commit
|
||||
# Make sure that the versions specified here match the pre-commit settings
|
||||
# Make sure that the versions specified here match the pre-commit settings!
|
||||
black==20.8b1
|
||||
flake8==3.8.4
|
||||
pylint==2.6.0
|
||||
mypy==0.800
|
||||
pylint==2.7.2
|
||||
mypy==0.812
|
||||
pyupgrade==2.10.0
|
||||
|
||||
pytest==6.2.2
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# Make sure to install those as additional_dependencies in the
|
||||
# pre-commit hooks for pylint & mypy
|
||||
certifi
|
||||
# only telegram.ext: # Keep this line here; used in setup(-raw).py
|
||||
tornado>=5.1
|
||||
|
||||
@@ -61,6 +61,10 @@ ignore_errors = True
|
||||
[mypy-telegram.callbackquery,telegram.chat,telegram.message,telegram.user,telegram.files.*,telegram.inline.inlinequery,telegram.payment.precheckoutquery,telegram.payment.shippingquery,telegram.passport.passportdata,telegram.passport.credentials,telegram.passport.passportfile,telegram.ext.filters]
|
||||
strict_optional = False
|
||||
|
||||
# type hinting for asyncio in webhookhandler is a bit tricky because it depends on the OS
|
||||
[mypy-telegram.ext.utils.webhookhandler]
|
||||
warn_unused_ignores = False
|
||||
|
||||
[mypy-urllib3.*]
|
||||
ignore_missing_imports = True
|
||||
|
||||
|
||||
+12
-2
@@ -24,7 +24,9 @@ from .user import User
|
||||
from .files.chatphoto import ChatPhoto
|
||||
from .chat import Chat
|
||||
from .chatlocation import ChatLocation
|
||||
from .chatinvitelink import ChatInviteLink
|
||||
from .chatmember import ChatMember
|
||||
from .chatmemberupdated import ChatMemberUpdated
|
||||
from .chatpermissions import ChatPermissions
|
||||
from .files.photosize import PhotoSize
|
||||
from .files.audio import Audio
|
||||
@@ -40,8 +42,8 @@ from .files.videonote import VideoNote
|
||||
from .chataction import ChatAction
|
||||
from .dice import Dice
|
||||
from .userprofilephotos import UserProfilePhotos
|
||||
from .keyboardbutton import KeyboardButton
|
||||
from .keyboardbuttonpolltype import KeyboardButtonPollType
|
||||
from .keyboardbutton import KeyboardButton
|
||||
from .replymarkup import ReplyMarkup
|
||||
from .replykeyboardmarkup import ReplyKeyboardMarkup
|
||||
from .replykeyboardremove import ReplyKeyboardRemove
|
||||
@@ -54,6 +56,7 @@ from .messageentity import MessageEntity
|
||||
from .messageid import MessageId
|
||||
from .games.game import Game
|
||||
from .poll import Poll, PollOption, PollAnswer
|
||||
from .voicechat import VoiceChatStarted, VoiceChatEnded, VoiceChatParticipantsInvited
|
||||
from .loginurl import LoginUrl
|
||||
from .proximityalerttriggered import ProximityAlertTriggered
|
||||
from .games.callbackgame import CallbackGame
|
||||
@@ -68,6 +71,7 @@ from .passport.encryptedpassportelement import EncryptedPassportElement
|
||||
from .passport.passportdata import PassportData
|
||||
from .inline.inlinekeyboardbutton import InlineKeyboardButton
|
||||
from .inline.inlinekeyboardmarkup import InlineKeyboardMarkup
|
||||
from .messageautodeletetimerchanged import MessageAutoDeleteTimerChanged
|
||||
from .message import Message
|
||||
from .callbackquery import CallbackQuery
|
||||
from .choseninlineresult import ChosenInlineResult
|
||||
@@ -144,7 +148,7 @@ from .passport.credentials import (
|
||||
TelegramDecryptionError,
|
||||
)
|
||||
from .bot import Bot
|
||||
from .version import __version__ # noqa: F401
|
||||
from .version import __version__, bot_api_version # noqa: F401
|
||||
|
||||
__author__ = 'devs@python-telegram-bot.org'
|
||||
|
||||
@@ -157,8 +161,10 @@ __all__ = ( # Keep this alphabetically ordered
|
||||
'CallbackQuery',
|
||||
'Chat',
|
||||
'ChatAction',
|
||||
'ChatInviteLink',
|
||||
'ChatLocation',
|
||||
'ChatMember',
|
||||
'ChatMemberUpdated',
|
||||
'ChatPermissions',
|
||||
'ChatPhoto',
|
||||
'ChosenInlineResult',
|
||||
@@ -226,6 +232,7 @@ __all__ = ( # Keep this alphabetically ordered
|
||||
'MAX_MESSAGE_LENGTH',
|
||||
'MaskPosition',
|
||||
'Message',
|
||||
'MessageAutoDeleteTimerChanged',
|
||||
'MessageEntity',
|
||||
'MessageId',
|
||||
'OrderInfo',
|
||||
@@ -272,5 +279,8 @@ __all__ = ( # Keep this alphabetically ordered
|
||||
'Video',
|
||||
'VideoNote',
|
||||
'Voice',
|
||||
'VoiceChatStarted',
|
||||
'VoiceChatEnded',
|
||||
'VoiceChatParticipantsInvited',
|
||||
'WebhookInfo',
|
||||
)
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
# pylint: disable=E0401, C0114
|
||||
# pylint: disable=C0114
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import Optional
|
||||
@@ -24,6 +24,7 @@ from typing import Optional
|
||||
import certifi
|
||||
|
||||
from . import __version__ as telegram_ver
|
||||
from .constants import BOT_API_VERSION
|
||||
|
||||
|
||||
def _git_revision() -> Optional[str]:
|
||||
@@ -39,6 +40,7 @@ def _git_revision() -> Optional[str]:
|
||||
def print_ver_info() -> None:
|
||||
git_revision = _git_revision()
|
||||
print(f'python-telegram-bot {telegram_ver}' + (f' ({git_revision})' if git_revision else ''))
|
||||
print(f'Bot API {BOT_API_VERSION}')
|
||||
print(f'certifi {certifi.__version__}') # type: ignore[attr-defined]
|
||||
sys_version = sys.version.replace('\n', ' ')
|
||||
print(f'Python {sys_version}')
|
||||
|
||||
+2
-4
@@ -46,15 +46,13 @@ class TelegramObject:
|
||||
|
||||
@staticmethod
|
||||
def parse_data(data: Optional[JSONDict]) -> Optional[JSONDict]:
|
||||
if not data:
|
||||
return None
|
||||
return data.copy()
|
||||
return None if data is None else data.copy()
|
||||
|
||||
@classmethod
|
||||
def de_json(cls: Type[TO], data: Optional[JSONDict], bot: 'Bot') -> Optional[TO]:
|
||||
data = cls.parse_data(data)
|
||||
|
||||
if not data:
|
||||
if data is None:
|
||||
return None
|
||||
|
||||
if cls == TelegramObject:
|
||||
|
||||
+364
-167
File diff suppressed because it is too large
Load Diff
@@ -47,7 +47,7 @@ class CallbackQuery(TelegramObject):
|
||||
considered equal, if their :attr:`id` is equal.
|
||||
|
||||
Note:
|
||||
* In Python `from` is a reserved word, use `from_user` instead.
|
||||
* In Python ``from`` is a reserved word, use ``from_user`` instead.
|
||||
* Exactly one of the fields :attr:`data` or :attr:`game_short_name` will be present.
|
||||
* After the user presses an inline button, Telegram clients will display a progress bar
|
||||
until you call :attr:`answer`. It is, therefore, necessary to react
|
||||
@@ -598,7 +598,7 @@ class CallbackQuery(TelegramObject):
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
caption_entities: Union[Tuple['MessageEntity', ...], List['MessageEntity']] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_markup: ReplyMarkup = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
|
||||
+154
-31
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=C0103,W0622
|
||||
# pylint: disable=W0622
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2021
|
||||
@@ -32,6 +32,7 @@ if TYPE_CHECKING:
|
||||
from telegram import (
|
||||
Bot,
|
||||
ChatMember,
|
||||
ChatInviteLink,
|
||||
Message,
|
||||
MessageId,
|
||||
ReplyMarkup,
|
||||
@@ -80,10 +81,8 @@ class Chat(TelegramObject):
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
description (:obj:`str`, optional): Description, for groups, supergroups and channel chats.
|
||||
Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
invite_link (:obj:`str`, optional): Chat invite link, for groups, supergroups and channel
|
||||
chats. Each administrator in a chat generates their own invite links, so the bot must
|
||||
first generate the link using ``export_chat_invite_link()``. Returned only
|
||||
in :meth:`telegram.Bot.get_chat`.
|
||||
invite_link (:obj:`str`, optional): Primary invite link, for groups, supergroups and
|
||||
channel. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
pinned_message (:class:`telegram.Message`, optional): The most recent pinned message
|
||||
(by sending date). Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
permissions (:class:`telegram.ChatPermissions`): Optional. Default chat member permissions,
|
||||
@@ -91,6 +90,11 @@ class Chat(TelegramObject):
|
||||
slow_mode_delay (:obj:`int`, optional): For supergroups, the minimum allowed delay between
|
||||
consecutive messages sent by each unprivileged user.
|
||||
Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
message_auto_delete_time (:obj:`int`, optional): The time after which all messages sent to
|
||||
the chat will be automatically deleted; in seconds. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods.
|
||||
sticker_set_name (:obj:`str`, optional): For supergroups, name of group sticker set.
|
||||
Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
@@ -114,7 +118,8 @@ class Chat(TelegramObject):
|
||||
bio (:obj:`str`): Optional. Bio of the other party in a private chat. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
description (:obj:`str`): Optional. Description, for groups, supergroups and channel chats.
|
||||
invite_link (:obj:`str`): Optional. Chat invite link, for supergroups and channel chats.
|
||||
invite_link (:obj:`str`): Optional. Primary invite link, for groups, supergroups and
|
||||
channel. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
pinned_message (:class:`telegram.Message`): Optional. The most recent pinned message
|
||||
(by sending date). Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
permissions (:class:`telegram.ChatPermissions`): Optional. Default chat member permissions,
|
||||
@@ -122,6 +127,11 @@ class Chat(TelegramObject):
|
||||
slow_mode_delay (:obj:`int`): Optional. For supergroups, the minimum allowed delay between
|
||||
consecutive messages sent by each unprivileged user. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
message_auto_delete_time (:obj:`int`): Optional. The time after which all messages sent to
|
||||
the chat will be automatically deleted; in seconds. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
sticker_set_name (:obj:`str`): Optional. For supergroups, name of Group sticker set.
|
||||
can_set_sticker_set (:obj:`bool`): Optional. :obj:`True`, if the bot can change group the
|
||||
sticker set.
|
||||
@@ -162,10 +172,11 @@ class Chat(TelegramObject):
|
||||
bio: str = None,
|
||||
linked_chat_id: int = None,
|
||||
location: ChatLocation = None,
|
||||
message_auto_delete_time: int = None,
|
||||
**_kwargs: Any,
|
||||
):
|
||||
# Required
|
||||
self.id = int(id)
|
||||
self.id = int(id) # pylint: disable=C0103
|
||||
self.type = type
|
||||
# Optionals
|
||||
self.title = title
|
||||
@@ -181,6 +192,9 @@ class Chat(TelegramObject):
|
||||
self.pinned_message = pinned_message
|
||||
self.permissions = permissions
|
||||
self.slow_mode_delay = slow_mode_delay
|
||||
self.message_auto_delete_time = (
|
||||
int(message_auto_delete_time) if message_auto_delete_time is not None else None
|
||||
)
|
||||
self.sticker_set_name = sticker_set_name
|
||||
self.can_set_sticker_set = can_set_sticker_set
|
||||
self.linked_chat_id = linked_chat_id
|
||||
@@ -216,7 +230,7 @@ class Chat(TelegramObject):
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: JSONDict, bot: 'Bot') -> Optional['Chat']:
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Chat']:
|
||||
data = cls.parse_data(data)
|
||||
|
||||
if not data:
|
||||
@@ -320,6 +334,7 @@ class Chat(TelegramObject):
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
until_date: Union[int, datetime] = None,
|
||||
api_kwargs: JSONDict = None,
|
||||
revoke_messages: bool = None,
|
||||
) -> bool:
|
||||
"""Shortcut for::
|
||||
|
||||
@@ -343,6 +358,7 @@ class Chat(TelegramObject):
|
||||
timeout=timeout,
|
||||
until_date=until_date,
|
||||
api_kwargs=api_kwargs,
|
||||
revoke_messages=revoke_messages,
|
||||
)
|
||||
|
||||
def unban_member(
|
||||
@@ -384,6 +400,8 @@ class Chat(TelegramObject):
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
is_anonymous: bool = None,
|
||||
can_manage_chat: bool = None,
|
||||
can_manage_voice_chats: bool = None,
|
||||
) -> bool:
|
||||
"""Shortcut for::
|
||||
|
||||
@@ -412,6 +430,8 @@ class Chat(TelegramObject):
|
||||
timeout=timeout,
|
||||
api_kwargs=api_kwargs,
|
||||
is_anonymous=is_anonymous,
|
||||
can_manage_chat=can_manage_chat,
|
||||
can_manage_voice_chats=can_manage_voice_chats,
|
||||
)
|
||||
|
||||
def restrict_member(
|
||||
@@ -496,7 +516,7 @@ class Chat(TelegramObject):
|
||||
|
||||
def pin_message(
|
||||
self,
|
||||
message_id: Union[str, int],
|
||||
message_id: int,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
@@ -526,7 +546,7 @@ class Chat(TelegramObject):
|
||||
self,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
message_id: Union[str, int] = None,
|
||||
message_id: int = None,
|
||||
) -> bool:
|
||||
"""Shortcut for::
|
||||
|
||||
@@ -578,7 +598,7 @@ class Chat(TelegramObject):
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: 'ReplyMarkup' = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
@@ -615,7 +635,7 @@ class Chat(TelegramObject):
|
||||
Union['InputMediaAudio', 'InputMediaDocument', 'InputMediaPhoto', 'InputMediaVideo']
|
||||
],
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
timeout: DVInput[float] = DEFAULT_20,
|
||||
api_kwargs: JSONDict = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -671,7 +691,7 @@ class Chat(TelegramObject):
|
||||
photo: Union[FileInput, 'PhotoSize'],
|
||||
caption: str = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: 'ReplyMarkup' = None,
|
||||
timeout: DVInput[float] = DEFAULT_20,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
@@ -711,7 +731,7 @@ class Chat(TelegramObject):
|
||||
first_name: str = None,
|
||||
last_name: str = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: 'ReplyMarkup' = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
contact: 'Contact' = None,
|
||||
@@ -752,7 +772,7 @@ class Chat(TelegramObject):
|
||||
title: str = None,
|
||||
caption: str = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: 'ReplyMarkup' = None,
|
||||
timeout: DVInput[float] = DEFAULT_20,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
@@ -797,7 +817,7 @@ class Chat(TelegramObject):
|
||||
filename: str = None,
|
||||
caption: str = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: 'ReplyMarkup' = None,
|
||||
timeout: DVInput[float] = DEFAULT_20,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
@@ -837,7 +857,7 @@ class Chat(TelegramObject):
|
||||
def send_dice(
|
||||
self,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: 'ReplyMarkup' = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
emoji: str = None,
|
||||
@@ -869,7 +889,7 @@ class Chat(TelegramObject):
|
||||
self,
|
||||
game_short_name: str,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: 'InlineKeyboardMarkup' = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
@@ -915,7 +935,7 @@ class Chat(TelegramObject):
|
||||
need_shipping_address: bool = None,
|
||||
is_flexible: bool = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: 'InlineKeyboardMarkup' = None,
|
||||
provider_data: Union[str, object] = None,
|
||||
send_phone_number_to_provider: bool = None,
|
||||
@@ -968,7 +988,7 @@ class Chat(TelegramObject):
|
||||
latitude: float = None,
|
||||
longitude: float = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: 'ReplyMarkup' = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
location: 'Location' = None,
|
||||
@@ -1016,7 +1036,7 @@ class Chat(TelegramObject):
|
||||
caption: str = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: 'ReplyMarkup' = None,
|
||||
timeout: DVInput[float] = DEFAULT_20,
|
||||
api_kwargs: JSONDict = None,
|
||||
@@ -1057,7 +1077,7 @@ class Chat(TelegramObject):
|
||||
self,
|
||||
sticker: Union[FileInput, 'Sticker'],
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: 'ReplyMarkup' = None,
|
||||
timeout: DVInput[float] = DEFAULT_20,
|
||||
api_kwargs: JSONDict = None,
|
||||
@@ -1092,7 +1112,7 @@ class Chat(TelegramObject):
|
||||
address: str = None,
|
||||
foursquare_id: str = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: 'ReplyMarkup' = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
venue: 'Venue' = None,
|
||||
@@ -1137,7 +1157,7 @@ class Chat(TelegramObject):
|
||||
duration: int = None,
|
||||
caption: str = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: 'ReplyMarkup' = None,
|
||||
timeout: DVInput[float] = DEFAULT_20,
|
||||
width: int = None,
|
||||
@@ -1186,7 +1206,7 @@ class Chat(TelegramObject):
|
||||
duration: int = None,
|
||||
length: int = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: 'ReplyMarkup' = None,
|
||||
timeout: DVInput[float] = DEFAULT_20,
|
||||
thumb: FileInput = None,
|
||||
@@ -1225,7 +1245,7 @@ class Chat(TelegramObject):
|
||||
duration: int = None,
|
||||
caption: str = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: 'ReplyMarkup' = None,
|
||||
timeout: DVInput[float] = DEFAULT_20,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
@@ -1271,7 +1291,7 @@ class Chat(TelegramObject):
|
||||
correct_option_id: int = None,
|
||||
is_closed: bool = None,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: 'ReplyMarkup' = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
explanation: str = None,
|
||||
@@ -1317,12 +1337,12 @@ class Chat(TelegramObject):
|
||||
def send_copy(
|
||||
self,
|
||||
from_chat_id: Union[str, int],
|
||||
message_id: Union[str, int],
|
||||
message_id: int,
|
||||
caption: str = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
caption_entities: Union[Tuple['MessageEntity', ...], List['MessageEntity']] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_markup: 'ReplyMarkup' = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -1356,12 +1376,12 @@ class Chat(TelegramObject):
|
||||
def copy_message(
|
||||
self,
|
||||
chat_id: Union[int, str],
|
||||
message_id: Union[str, int],
|
||||
message_id: int,
|
||||
caption: str = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
caption_entities: Union[Tuple['MessageEntity', ...], List['MessageEntity']] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_markup: 'ReplyMarkup' = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -1391,3 +1411,106 @@ class Chat(TelegramObject):
|
||||
timeout=timeout,
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
|
||||
def export_invite_link(
|
||||
self,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
) -> str:
|
||||
"""Shortcut for::
|
||||
|
||||
bot.export_chat_invite_link(chat_id=update.effective_chat.id, *args, **kwargs)
|
||||
|
||||
For the documentation of the arguments, please see
|
||||
:meth:`telegram.Bot.export_chat_invite_link`.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
|
||||
Returns:
|
||||
:obj:`str`: New invite link on success.
|
||||
|
||||
"""
|
||||
return self.bot.export_chat_invite_link(
|
||||
chat_id=self.id, timeout=timeout, api_kwargs=api_kwargs
|
||||
)
|
||||
|
||||
def create_invite_link(
|
||||
self,
|
||||
expire_date: Union[int, datetime] = None,
|
||||
member_limit: int = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
) -> 'ChatInviteLink':
|
||||
"""Shortcut for::
|
||||
|
||||
bot.create_chat_invite_link(chat_id=update.effective_chat.id, *args, **kwargs)
|
||||
|
||||
For the documentation of the arguments, please see
|
||||
:meth:`telegram.Bot.create_chat_invite_link`.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
|
||||
Returns:
|
||||
:class:`telegram.ChatInviteLink`
|
||||
|
||||
"""
|
||||
return self.bot.create_chat_invite_link(
|
||||
chat_id=self.id,
|
||||
expire_date=expire_date,
|
||||
member_limit=member_limit,
|
||||
timeout=timeout,
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
|
||||
def edit_invite_link(
|
||||
self,
|
||||
invite_link: str,
|
||||
expire_date: Union[int, datetime] = None,
|
||||
member_limit: int = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
) -> 'ChatInviteLink':
|
||||
"""Shortcut for::
|
||||
|
||||
bot.edit_chat_invite_link(chat_id=update.effective_chat.id, *args, **kwargs)
|
||||
|
||||
For the documentation of the arguments, please see
|
||||
:meth:`telegram.Bot.edit_chat_invite_link`.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
|
||||
Returns:
|
||||
:class:`telegram.ChatInviteLink`
|
||||
|
||||
"""
|
||||
return self.bot.edit_chat_invite_link(
|
||||
chat_id=self.id,
|
||||
invite_link=invite_link,
|
||||
expire_date=expire_date,
|
||||
member_limit=member_limit,
|
||||
timeout=timeout,
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
|
||||
def revoke_invite_link(
|
||||
self,
|
||||
invite_link: str,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
) -> 'ChatInviteLink':
|
||||
"""Shortcut for::
|
||||
|
||||
bot.revoke_chat_invite_link(chat_id=update.effective_chat.id, *args, **kwargs)
|
||||
|
||||
For the documentation of the arguments, please see
|
||||
:meth:`telegram.Bot.revoke_chat_invite_link`.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
|
||||
Returns:
|
||||
:class:`telegram.ChatInviteLink`
|
||||
|
||||
"""
|
||||
return self.bot.revoke_chat_invite_link(
|
||||
chat_id=self.id, invite_link=invite_link, timeout=timeout, api_kwargs=api_kwargs
|
||||
)
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2021
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents an invite link for a chat."""
|
||||
import datetime
|
||||
from typing import TYPE_CHECKING, Any, Optional
|
||||
|
||||
from telegram import TelegramObject, User
|
||||
from telegram.utils.helpers import from_timestamp, to_timestamp
|
||||
from telegram.utils.types import JSONDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Bot
|
||||
|
||||
|
||||
class ChatInviteLink(TelegramObject):
|
||||
"""This object represents an invite link for a chat.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`invite_link`, :attr:`creator`, :attr:`is_primary` and
|
||||
:attr:`is_revoked` are equal.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
|
||||
Args:
|
||||
invite_link (:obj:`str`): The invite link.
|
||||
creator (:class:`telegram.User`): Creator of the link.
|
||||
is_primary (:obj:`bool`): :obj:`True`, if the link is primary.
|
||||
is_revoked (:obj:`bool`): :obj:`True`, if the link is revoked.
|
||||
expire_date (:class:`datetime.datetime`, optional): Date when the link will expire or
|
||||
has been expired.
|
||||
member_limit (:obj:`int`, optional): Maximum number of users that can be members of the
|
||||
chat simultaneously after joining the chat via this invite link; 1-99999.
|
||||
|
||||
Attributes:
|
||||
invite_link (:obj:`str`): The invite link. If the link was created by another chat
|
||||
administrator, then the second part of the link will be replaced with ``'…'``.
|
||||
creator (:class:`telegram.User`): Creator of the link.
|
||||
is_primary (:obj:`bool`): :obj:`True`, if the link is primary.
|
||||
is_revoked (:obj:`bool`): :obj:`True`, if the link is revoked.
|
||||
expire_date (:class:`datetime.datetime`): Optional. Date when the link will expire or
|
||||
has been expired.
|
||||
member_limit (:obj:`int`): Optional. Maximum number of users that can be members
|
||||
of the chat simultaneously after joining the chat via this invite link; 1-99999.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
invite_link: str,
|
||||
creator: User,
|
||||
is_primary: bool,
|
||||
is_revoked: bool,
|
||||
expire_date: datetime.datetime = None,
|
||||
member_limit: int = None,
|
||||
**_kwargs: Any,
|
||||
):
|
||||
# Required
|
||||
self.invite_link = invite_link
|
||||
self.creator = creator
|
||||
self.is_primary = is_primary
|
||||
self.is_revoked = is_revoked
|
||||
|
||||
# Optionals
|
||||
self.expire_date = expire_date
|
||||
self.member_limit = int(member_limit) if member_limit is not None else None
|
||||
|
||||
self._id_attrs = (self.invite_link, self.creator, self.is_primary, self.is_revoked)
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ChatInviteLink']:
|
||||
data = cls.parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data['creator'] = User.de_json(data.get('creator'), bot)
|
||||
data['expire_date'] = from_timestamp(data.get('expire_date', None))
|
||||
|
||||
return cls(**data)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
data = super().to_dict()
|
||||
|
||||
data['expire_date'] = to_timestamp(self.expire_date)
|
||||
|
||||
return data
|
||||
@@ -46,6 +46,18 @@ class ChatMember(TelegramObject):
|
||||
restrictions will be lifted for this user.
|
||||
can_be_edited (:obj:`bool`, optional): Administrators only. :obj:`True`, if the bot is
|
||||
allowed to edit administrator privileges of that user.
|
||||
can_manage_chat (:obj:`bool`, optional): Administrators only. :obj:`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.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
|
||||
can_manage_voice_chats (:obj:`bool`, optional): Administrators only. :obj:`True`, if the
|
||||
administrator can manage voice chats.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
|
||||
can_change_info (:obj:`bool`, optional): Administrators and restricted only. :obj:`True`,
|
||||
if the user can change the chat title, photo and other settings.
|
||||
can_post_messages (:obj:`bool`, optional): Administrators only. :obj:`True`, if the
|
||||
@@ -87,6 +99,17 @@ class ChatMember(TelegramObject):
|
||||
for this user.
|
||||
can_be_edited (:obj:`bool`): Optional. If the bot is allowed to edit administrator
|
||||
privileges of that user.
|
||||
can_manage_chat (:obj:`bool`): Optional. 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.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
|
||||
can_manage_voice_chats (:obj:`bool`): Optional. if the administrator can manage
|
||||
voice chats.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
|
||||
can_change_info (:obj:`bool`): Optional. If the user can change the chat title, photo and
|
||||
other settings.
|
||||
can_post_messages (:obj:`bool`): Optional. If the administrator can post in the channel.
|
||||
@@ -150,6 +173,8 @@ class ChatMember(TelegramObject):
|
||||
is_member: bool = None,
|
||||
custom_title: str = None,
|
||||
is_anonymous: bool = None,
|
||||
can_manage_chat: bool = None,
|
||||
can_manage_voice_chats: bool = None,
|
||||
**_kwargs: Any,
|
||||
):
|
||||
# Required
|
||||
@@ -175,6 +200,8 @@ class ChatMember(TelegramObject):
|
||||
self.can_send_other_messages = can_send_other_messages
|
||||
self.can_add_web_page_previews = can_add_web_page_previews
|
||||
self.is_member = is_member
|
||||
self.can_manage_chat = can_manage_chat
|
||||
self.can_manage_voice_chats = can_manage_voice_chats
|
||||
|
||||
self._id_attrs = (self.user, self.status)
|
||||
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2021
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents a Telegram ChatMemberUpdated."""
|
||||
import datetime
|
||||
from typing import TYPE_CHECKING, Any, Optional
|
||||
|
||||
from telegram import TelegramObject, User, Chat, ChatMember, ChatInviteLink
|
||||
from telegram.utils.helpers import from_timestamp, to_timestamp
|
||||
from telegram.utils.types import JSONDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Bot
|
||||
|
||||
|
||||
class ChatMemberUpdated(TelegramObject):
|
||||
"""This object represents changes in the status of a chat member.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`chat`, :attr:`from_user`, :attr:`date`,
|
||||
:attr:`old_chat_member` and :attr:`new_chat_member` are equal.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
|
||||
Note:
|
||||
In Python ``from`` is a reserved word, use ``from_user`` instead.
|
||||
|
||||
Args:
|
||||
chat (:class:`telegram.Chat`): Chat the user belongs to.
|
||||
from_user (:class:`telegram.User`): Performer of the action, which resulted in the change.
|
||||
date (:class:`datetime.datetime`): Date the change was done in Unix time. Converted to
|
||||
:class:`datetime.datetime`.
|
||||
old_chat_member (:class:`telegram.ChatMember`): Previous information about the chat member.
|
||||
new_chat_member (:class:`telegram.ChatMember`): New information about the chat member.
|
||||
invite_link (:class:`telegram.ChatInviteLink`, optional): Chat invite link, which was used
|
||||
by the user to join the chat. For joining by invite link events only.
|
||||
|
||||
Attributes:
|
||||
chat (:class:`telegram.Chat`): Chat the user belongs to.
|
||||
from_user (:class:`telegram.User`): Performer of the action, which resulted in the change.
|
||||
date (:class:`datetime.datetime`): Date the change was done in Unix time. Converted to
|
||||
:class:`datetime.datetime`.
|
||||
old_chat_member (:class:`telegram.ChatMember`): Previous information about the chat member.
|
||||
new_chat_member (:class:`telegram.ChatMember`): New information about the chat member.
|
||||
invite_link (:class:`telegram.ChatInviteLink`): Optional. Chat invite link, which was used
|
||||
by the user to join the chat.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
chat: Chat,
|
||||
from_user: User,
|
||||
date: datetime.datetime,
|
||||
old_chat_member: ChatMember,
|
||||
new_chat_member: ChatMember,
|
||||
invite_link: ChatInviteLink = None,
|
||||
**_kwargs: Any,
|
||||
):
|
||||
# Required
|
||||
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
|
||||
|
||||
# Optionals
|
||||
self.invite_link = invite_link
|
||||
|
||||
self._id_attrs = (
|
||||
self.chat,
|
||||
self.from_user,
|
||||
self.date,
|
||||
self.old_chat_member,
|
||||
self.new_chat_member,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ChatMemberUpdated']:
|
||||
data = cls.parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data['chat'] = Chat.de_json(data.get('chat'), bot)
|
||||
data['from_user'] = User.de_json(data.get('from'), bot)
|
||||
data['date'] = from_timestamp(data.get('date'))
|
||||
data['old_chat_member'] = ChatMember.de_json(data.get('old_chat_member'), bot)
|
||||
data['new_chat_member'] = ChatMember.de_json(data.get('new_chat_member'), bot)
|
||||
data['invite_link'] = ChatInviteLink.de_json(data.get('invite_link'), bot)
|
||||
|
||||
return cls(**data)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
data = super().to_dict()
|
||||
|
||||
# Required
|
||||
data['date'] = to_timestamp(self.date)
|
||||
|
||||
return data
|
||||
@@ -37,7 +37,7 @@ class ChosenInlineResult(TelegramObject):
|
||||
considered equal, if their :attr:`result_id` is equal.
|
||||
|
||||
Note:
|
||||
* In Python `from` is a reserved word, use `from_user` instead.
|
||||
* In Python ``from`` is a reserved word, use ``from_user`` instead.
|
||||
* It is necessary to enable inline feedback via `@Botfather <https://t.me/BotFather>`_ in
|
||||
order to receive these objects in updates.
|
||||
|
||||
|
||||
@@ -21,6 +21,10 @@ The following constants were extracted from the
|
||||
`Telegram Bots API <https://core.telegram.org/bots/api>`_.
|
||||
|
||||
Attributes:
|
||||
BOT_API_VERSION (:obj:`str`): `5.1`. Telegram Bot API version supported by this
|
||||
version of `python-telegram-bot`. Also available as ``telegram.bot_api_version``.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
MAX_MESSAGE_LENGTH (:obj:`int`): 4096
|
||||
MAX_CAPTION_LENGTH (:obj:`int`): 1024
|
||||
SUPPORTED_WEBHOOK_PORTS (List[:obj:`int`]): [443, 80, 88, 8443]
|
||||
@@ -88,8 +92,14 @@ Attributes:
|
||||
DICE_BASKETBALL (:obj:`str`): '🏀'
|
||||
DICE_FOOTBALL (:obj:`str`): '⚽'
|
||||
DICE_SLOT_MACHINE (:obj:`str`): '🎰'
|
||||
DICE_BOWLING (:obj:`str`): '🎳'
|
||||
|
||||
.. versionadded:: 13.4
|
||||
DICE_ALL_EMOJI (List[:obj:`str`]): List of all supported base emoji.
|
||||
|
||||
.. versionchanged:: 13.4
|
||||
Added :attr:`DICE_BOWLING`
|
||||
|
||||
:class:`telegram.MessageEntity`:
|
||||
|
||||
Attributes:
|
||||
@@ -136,6 +146,7 @@ Attributes:
|
||||
"""
|
||||
from typing import List
|
||||
|
||||
BOT_API_VERSION: str = '5.1'
|
||||
MAX_MESSAGE_LENGTH: int = 4096
|
||||
MAX_CAPTION_LENGTH: int = 1024
|
||||
ANONYMOUS_ADMIN_ID: int = 1087968824
|
||||
@@ -182,12 +193,14 @@ DICE_DARTS: str = '🎯'
|
||||
DICE_BASKETBALL: str = '🏀'
|
||||
DICE_FOOTBALL: str = '⚽'
|
||||
DICE_SLOT_MACHINE: str = '🎰'
|
||||
DICE_BOWLING: str = '🎳'
|
||||
DICE_ALL_EMOJI: List[str] = [
|
||||
DICE_DICE,
|
||||
DICE_DARTS,
|
||||
DICE_BASKETBALL,
|
||||
DICE_FOOTBALL,
|
||||
DICE_SLOT_MACHINE,
|
||||
DICE_BOWLING,
|
||||
]
|
||||
|
||||
MESSAGEENTITY_MENTION: str = 'mention'
|
||||
|
||||
+12
-2
@@ -45,13 +45,17 @@ class Dice(TelegramObject):
|
||||
3 indicates that the goal was missed. However, this behaviour is undocumented and might
|
||||
be changed by Telegram.
|
||||
|
||||
If :attr:`emoji` is "🎳", a value of 6 knocks all the pins, while a value of 1 means all
|
||||
the pins were missed. However, this behaviour is undocumented and might be changed by
|
||||
Telegram.
|
||||
|
||||
If :attr:`emoji` is "🎰", each value corresponds to a unique combination of symbols, which
|
||||
can be found at our `wiki <https://git.io/JkeC6>`_. However, this behaviour is undocumented
|
||||
and might be changed by Telegram.
|
||||
|
||||
Args:
|
||||
value (:obj:`int`): Value of the dice. 1-6 for dice and darts, 1-5 for basketball and
|
||||
football/soccer ball, 1-64 for slot machine.
|
||||
value (:obj:`int`): Value of the dice. 1-6 for dice, darts and bowling balls, 1-5 for
|
||||
basketball and football/soccer ball, 1-64 for slot machine.
|
||||
emoji (:obj:`str`): Emoji on which the dice throw animation is based.
|
||||
|
||||
Attributes:
|
||||
@@ -76,5 +80,11 @@ class Dice(TelegramObject):
|
||||
""":const:`telegram.constants.DICE_FOOTBALL`"""
|
||||
SLOT_MACHINE: ClassVar[str] = constants.DICE_SLOT_MACHINE
|
||||
""":const:`telegram.constants.DICE_SLOT_MACHINE`"""
|
||||
BOWLING: ClassVar[str] = constants.DICE_BOWLING
|
||||
"""
|
||||
:const:`telegram.constants.DICE_BOWLING`
|
||||
|
||||
.. versionadded:: 13.4
|
||||
"""
|
||||
ALL_EMOJI: ClassVar[List[str]] = constants.DICE_ALL_EMOJI
|
||||
""":const:`telegram.constants.DICE_ALL_EMOJI`"""
|
||||
|
||||
@@ -43,6 +43,7 @@ from .messagequeue import MessageQueue
|
||||
from .messagequeue import DelayQueue
|
||||
from .pollanswerhandler import PollAnswerHandler
|
||||
from .pollhandler import PollHandler
|
||||
from .chatmemberhandler import ChatMemberHandler
|
||||
from .defaults import Defaults
|
||||
|
||||
__all__ = (
|
||||
@@ -78,5 +79,6 @@ __all__ = (
|
||||
'PrefixHandler',
|
||||
'PollAnswerHandler',
|
||||
'PollHandler',
|
||||
'ChatMemberHandler',
|
||||
'Defaults',
|
||||
)
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2019-2021
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains the ChatMemberHandler classes."""
|
||||
from typing import ClassVar, TypeVar, Union, Callable, TYPE_CHECKING
|
||||
|
||||
from telegram import Update
|
||||
from telegram.utils.helpers import DefaultValue, DEFAULT_FALSE
|
||||
from .handler import Handler
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram.ext import CallbackContext
|
||||
|
||||
RT = TypeVar('RT')
|
||||
|
||||
|
||||
class ChatMemberHandler(Handler[Update]):
|
||||
"""Handler class to handle Telegram updates that contain a chat member update.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
|
||||
Note:
|
||||
:attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you
|
||||
can use to keep any data in will be sent to the :attr:`callback` function. Related to
|
||||
either the user or the chat that the update was sent in. For each update from the same user
|
||||
or in the same chat, it will be the same ``dict``.
|
||||
|
||||
Note that this is DEPRECATED, and you should use context based callbacks. See
|
||||
https://git.io/fxJuV for more info.
|
||||
|
||||
Warning:
|
||||
When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom
|
||||
attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info.
|
||||
|
||||
Args:
|
||||
callback (:obj:`callable`): The callback function for this handler. Will be called when
|
||||
:attr:`check_update` has determined that an update should be processed by this handler.
|
||||
Callback signature for context based API:
|
||||
|
||||
``def callback(update: Update, context: CallbackContext)``
|
||||
|
||||
The return value of the callback is usually ignored except for the special case of
|
||||
:class:`telegram.ext.ConversationHandler`.
|
||||
chat_member_types (:obj:`int`, optional): Pass one of :attr:`MY_CHAT_MEMBER`,
|
||||
:attr:`CHAT_MEMBER` or :attr:`ANY_CHAT_MEMBER` to specify if this handler should handle
|
||||
only updates with :attr:`telegram.Update.my_chat_member`,
|
||||
:attr:`telegram.Update.chat_member` or both. Defaults to :attr:`MY_CHAT_MEMBER`.
|
||||
pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called
|
||||
``update_queue`` will be passed to the callback function. It will be the ``Queue``
|
||||
instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher`
|
||||
that contains new updates which can be used to insert updates. Default is :obj:`False`.
|
||||
DEPRECATED: Please switch to context based callbacks.
|
||||
pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called
|
||||
``job_queue`` will be passed to the callback function. It will be a
|
||||
:class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater`
|
||||
which can be used to schedule new jobs. Default is :obj:`False`.
|
||||
DEPRECATED: Please switch to context based callbacks.
|
||||
pass_user_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called
|
||||
``user_data`` will be passed to the callback function. Default is :obj:`False`.
|
||||
DEPRECATED: Please switch to context based callbacks.
|
||||
pass_chat_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called
|
||||
``chat_data`` will be passed to the callback function. Default is :obj:`False`.
|
||||
DEPRECATED: Please switch to context based callbacks.
|
||||
run_async (:obj:`bool`): Determines whether the callback will run asynchronously.
|
||||
Defaults to :obj:`False`.
|
||||
|
||||
Attributes:
|
||||
callback (:obj:`callable`): The callback function for this handler.
|
||||
chat_member_types (:obj:`int`, optional): Specifies if this handler should handle
|
||||
only updates with :attr:`telegram.Update.my_chat_member`,
|
||||
:attr:`telegram.Update.chat_member` or both.
|
||||
pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be
|
||||
passed to the callback function.
|
||||
pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to
|
||||
the callback function.
|
||||
pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to
|
||||
the callback function.
|
||||
pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to
|
||||
the callback function.
|
||||
run_async (:obj:`bool`): Determines whether the callback will run asynchronously.
|
||||
|
||||
"""
|
||||
|
||||
MY_CHAT_MEMBER: ClassVar[int] = -1
|
||||
""":obj:`int`: Used as a constant to handle only :attr:`telegram.Update.my_chat_member`."""
|
||||
CHAT_MEMBER: ClassVar[int] = 0
|
||||
""":obj:`int`: Used as a constant to handle only :attr:`telegram.Update.chat_member`."""
|
||||
ANY_CHAT_MEMBER: ClassVar[int] = 1
|
||||
""":obj:`int`: Used as a constant to handle bot :attr:`telegram.Update.my_chat_member`
|
||||
and :attr:`telegram.Update.chat_member`."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
callback: Callable[[Update, 'CallbackContext'], RT],
|
||||
chat_member_types: int = MY_CHAT_MEMBER,
|
||||
pass_update_queue: bool = False,
|
||||
pass_job_queue: bool = False,
|
||||
pass_user_data: bool = False,
|
||||
pass_chat_data: bool = False,
|
||||
run_async: Union[bool, DefaultValue] = DEFAULT_FALSE,
|
||||
):
|
||||
super().__init__(
|
||||
callback,
|
||||
pass_update_queue=pass_update_queue,
|
||||
pass_job_queue=pass_job_queue,
|
||||
pass_user_data=pass_user_data,
|
||||
pass_chat_data=pass_chat_data,
|
||||
run_async=run_async,
|
||||
)
|
||||
|
||||
self.chat_member_types = chat_member_types
|
||||
|
||||
def check_update(self, update: object) -> bool:
|
||||
"""Determines whether an update should be passed to this handlers :attr:`callback`.
|
||||
|
||||
Args:
|
||||
update (:class:`telegram.Update` | :obj:`object`): Incoming update.
|
||||
|
||||
Returns:
|
||||
:obj:`bool`
|
||||
|
||||
"""
|
||||
if isinstance(update, Update):
|
||||
if not (update.my_chat_member or update.chat_member):
|
||||
return False
|
||||
if self.chat_member_types == self.ANY_CHAT_MEMBER:
|
||||
return True
|
||||
if self.chat_member_types == self.CHAT_MEMBER:
|
||||
return bool(update.chat_member)
|
||||
return bool(update.my_chat_member)
|
||||
return False
|
||||
@@ -16,7 +16,7 @@
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
# pylint: disable=R0201, E0401
|
||||
# pylint: disable=R0201
|
||||
"""This module contains the class Defaults, which allows to pass default values to Updater."""
|
||||
from typing import NoReturn, Optional, Dict, Any
|
||||
|
||||
|
||||
+76
-7
@@ -497,7 +497,7 @@ class Filters:
|
||||
def filter(self, message: Message) -> bool:
|
||||
return bool(
|
||||
message.entities
|
||||
and any([e.type == MessageEntity.BOT_COMMAND for e in message.entities])
|
||||
and any(e.type == MessageEntity.BOT_COMMAND for e in message.entities)
|
||||
)
|
||||
|
||||
def __call__( # type: ignore[override]
|
||||
@@ -1005,6 +1005,15 @@ officedocument.wordprocessingml.document")``.
|
||||
:attr: `telegram.Message.supergroup_chat_created` or
|
||||
:attr: `telegram.Message.channel_chat_created`."""
|
||||
|
||||
class _MessageAutoDeleteTimerChanged(MessageFilter):
|
||||
name = 'MessageAutoDeleteTimerChanged'
|
||||
|
||||
def filter(self, message: Message) -> bool:
|
||||
return bool(message.message_auto_delete_timer_changed)
|
||||
|
||||
message_auto_delete_timer_changed = _MessageAutoDeleteTimerChanged()
|
||||
"""Messages that contain :attr:`message_auto_delete_timer_changed`"""
|
||||
|
||||
class _Migrate(MessageFilter):
|
||||
name = 'Filters.status_update.migrate'
|
||||
|
||||
@@ -1042,6 +1051,33 @@ officedocument.wordprocessingml.document")``.
|
||||
proximity_alert_triggered = _ProximityAlertTriggered()
|
||||
"""Messages that contain :attr:`telegram.Message.proximity_alert_triggered`."""
|
||||
|
||||
class _VoiceChatStarted(MessageFilter):
|
||||
name = 'Filters.status_update.voice_chat_started'
|
||||
|
||||
def filter(self, message: Message) -> bool:
|
||||
return bool(message.voice_chat_started)
|
||||
|
||||
voice_chat_started = _VoiceChatStarted()
|
||||
"""Messages that contain :attr:`telegram.Message.voice_chat_started`."""
|
||||
|
||||
class _VoiceChatEnded(MessageFilter):
|
||||
name = 'Filters.status_update.voice_chat_ended'
|
||||
|
||||
def filter(self, message: Message) -> bool:
|
||||
return bool(message.voice_chat_ended)
|
||||
|
||||
voice_chat_ended = _VoiceChatEnded()
|
||||
"""Messages that contain :attr:`telegram.Message.voice_chat_ended`."""
|
||||
|
||||
class _VoiceChatParticipantsInvited(MessageFilter):
|
||||
name = 'Filters.status_update.voice_chat_participants_invited'
|
||||
|
||||
def filter(self, message: Message) -> bool:
|
||||
return bool(message.voice_chat_participants_invited)
|
||||
|
||||
voice_chat_participants_invited = _VoiceChatParticipantsInvited()
|
||||
"""Messages that contain :attr:`telegram.Message.voice_chat_participants_invited`."""
|
||||
|
||||
name = 'Filters.status_update'
|
||||
|
||||
def filter(self, message: Update) -> bool:
|
||||
@@ -1052,10 +1088,14 @@ officedocument.wordprocessingml.document")``.
|
||||
or self.new_chat_photo(message)
|
||||
or self.delete_chat_photo(message)
|
||||
or self.chat_created(message)
|
||||
or self.message_auto_delete_timer_changed(message)
|
||||
or self.migrate(message)
|
||||
or self.pinned_message(message)
|
||||
or self.connected_website(message)
|
||||
or self.proximity_alert_triggered(message)
|
||||
or self.voice_chat_started(message)
|
||||
or self.voice_chat_ended(message)
|
||||
or self.voice_chat_participants_invited(message)
|
||||
)
|
||||
|
||||
status_update = _StatusUpdate()
|
||||
@@ -1077,18 +1117,35 @@ officedocument.wordprocessingml.document")``.
|
||||
left_chat_member: Messages that contain
|
||||
:attr:`telegram.Message.left_chat_member`.
|
||||
migrate: Messages that contain
|
||||
:attr:`telegram.Message.migrate_from_chat_id` or
|
||||
:attr: `telegram.Message.migrate_from_chat_id`.
|
||||
:attr:`telegram.Message.migrate_to_chat_id` or
|
||||
:attr:`telegram.Message.migrate_from_chat_id`.
|
||||
new_chat_members: Messages that contain
|
||||
:attr:`telegram.Message.new_chat_members`.
|
||||
new_chat_photo: Messages that contain
|
||||
:attr:`telegram.Message.new_chat_photo`.
|
||||
new_chat_title: Messages that contain
|
||||
:attr:`telegram.Message.new_chat_title`.
|
||||
message_auto_delete_timer_changed: Messages that contain
|
||||
:attr:`message_auto_delete_timer_changed`.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
pinned_message: Messages that contain
|
||||
:attr:`telegram.Message.pinned_message`.
|
||||
proximity_alert_triggered: Messages that contain
|
||||
:attr:`telegram.Message.proximity_alert_triggered`.
|
||||
voice_chat_started: Messages that contain
|
||||
:attr:`telegram.Message.voice_chat_started`.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
voice_chat_ended: Messages that contain
|
||||
:attr:`telegram.Message.voice_chat_ended`.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
voice_chat_participants_invited: Messages that contain
|
||||
:attr:`telegram.Message.voice_chat_participants_invited`.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
|
||||
"""
|
||||
|
||||
class _Forwarded(MessageFilter):
|
||||
@@ -1681,16 +1738,22 @@ officedocument.wordprocessingml.document")``.
|
||||
username.
|
||||
|
||||
Examples:
|
||||
* To filter for messages forwarded from a channel with ID ``-1234``, use
|
||||
``MessageHandler(Filters.sender_chat(-1234), callback_method)``.
|
||||
* To filter for messages forwarded to a discussion group from a channel with ID
|
||||
``-1234``, use ``MessageHandler(Filters.sender_chat(-1234), callback_method)``.
|
||||
* To filter for messages of anonymous admins in a super group with username
|
||||
``@anonymous``, use
|
||||
``MessageHandler(Filters.sender_chat(username='anonymous'), callback_method)``.
|
||||
* To filter for messages forwarded from *any* channel, use
|
||||
* To filter for messages forwarded to a discussion group from *any* channel, use
|
||||
``MessageHandler(Filters.sender_chat.channel, callback_method)``.
|
||||
* To filter for messages of anonymous admins in *any* super group, use
|
||||
``MessageHandler(Filters.sender_chat.super_group, callback_method)``.
|
||||
|
||||
Note:
|
||||
Remember, ``sender_chat`` is also set for messages in a channel as the channel itself,
|
||||
so when your bot is an admin in a channel and the linked discussion group, you would
|
||||
receive the message twice (once from inside the channel, once inside the discussion
|
||||
group).
|
||||
|
||||
Warning:
|
||||
:attr:`chat_ids` will return a *copy* of the saved chat ids as :class:`frozenset`. This
|
||||
is to ensure thread safety. To add/remove a chat, you should use :meth:`add_usernames`,
|
||||
@@ -1831,6 +1894,7 @@ officedocument.wordprocessingml.document")``.
|
||||
basketball = _DiceEmoji('🏀', 'basketball')
|
||||
football = _DiceEmoji('⚽')
|
||||
slot_machine = _DiceEmoji('🎰')
|
||||
bowling = _DiceEmoji('🎳', 'bowling')
|
||||
|
||||
dice = _Dice()
|
||||
"""Dice Messages. If an integer or a list of integers is passed, it filters messages to only
|
||||
@@ -1863,6 +1927,11 @@ officedocument.wordprocessingml.document")``.
|
||||
as for :attr:`Filters.dice`.
|
||||
slot_machine: Dice messages with the emoji 🎰. Passing a list of integers is supported just
|
||||
as for :attr:`Filters.dice`.
|
||||
bowling: Dice messages with the emoji 🎳. Passing a list of integers is supported just
|
||||
as for :attr:`Filters.dice`.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
|
||||
"""
|
||||
|
||||
class language(MessageFilter):
|
||||
@@ -1896,7 +1965,7 @@ officedocument.wordprocessingml.document")``.
|
||||
"""""" # remove method from docs
|
||||
return bool(
|
||||
message.from_user.language_code
|
||||
and any([message.from_user.language_code.startswith(x) for x in self.lang])
|
||||
and any(message.from_user.language_code.startswith(x) for x in self.lang)
|
||||
)
|
||||
|
||||
class _UpdateType(UpdateFilter):
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
# pylint: disable=E0401
|
||||
"""This module contains the classes JobQueue and Job."""
|
||||
|
||||
import datetime
|
||||
|
||||
+112
-61
@@ -244,36 +244,56 @@ class Updater:
|
||||
self,
|
||||
poll_interval: float = 0.0,
|
||||
timeout: float = 10,
|
||||
clean: bool = False,
|
||||
clean: bool = None,
|
||||
bootstrap_retries: int = -1,
|
||||
read_latency: float = 2.0,
|
||||
allowed_updates: List[str] = None,
|
||||
drop_pending_updates: bool = None,
|
||||
) -> Optional[Queue]:
|
||||
"""Starts polling updates from Telegram.
|
||||
|
||||
Args:
|
||||
poll_interval (:obj:`float`, optional): Time to wait between polling updates from
|
||||
Telegram in seconds. Default is 0.0.
|
||||
timeout (:obj:`float`, optional): Passed to :attr:`telegram.Bot.get_updates`.
|
||||
clean (:obj:`bool`, optional): Whether to clean any pending updates on Telegram servers
|
||||
before actually starting to poll. Default is :obj:`False`.
|
||||
timeout (:obj:`float`, optional): Passed to :meth:`telegram.Bot.get_updates`.
|
||||
drop_pending_updates (:obj:`bool`, optional): Whether to clean any pending updates on
|
||||
Telegram servers before actually starting to poll. Default is :obj:`False`.
|
||||
|
||||
.. versionadded :: 13.4
|
||||
clean (:obj:`bool`, optional): Alias for ``drop_pending_updates``.
|
||||
|
||||
.. deprecated:: 13.4
|
||||
Use ``drop_pending_updates`` instead.
|
||||
bootstrap_retries (:obj:`int`, optional): Whether the bootstrapping phase of the
|
||||
`Updater` will retry on failures on the Telegram server.
|
||||
:class:`telegram.ext.Updater` will retry on failures on the Telegram server.
|
||||
|
||||
* < 0 - retry indefinitely (default)
|
||||
* 0 - no retries
|
||||
* > 0 - retry up to X times
|
||||
|
||||
allowed_updates (List[:obj:`str`], optional): Passed to
|
||||
:attr:`telegram.Bot.get_updates`.
|
||||
:meth:`telegram.Bot.get_updates`.
|
||||
read_latency (:obj:`float` | :obj:`int`, optional): Grace time in seconds for receiving
|
||||
the reply from server. Will be added to the `timeout` value and used as the read
|
||||
the reply from server. Will be added to the ``timeout`` value and used as the read
|
||||
timeout from server (Default: 2).
|
||||
|
||||
Returns:
|
||||
:obj:`Queue`: The update queue that can be filled from the main thread.
|
||||
|
||||
"""
|
||||
if (clean is not None) and (drop_pending_updates is not None):
|
||||
raise TypeError('`clean` and `drop_pending_updates` are mutually exclusive.')
|
||||
|
||||
if clean is not None:
|
||||
warnings.warn(
|
||||
'The argument `clean` of `start_polling` is deprecated. Please use '
|
||||
'`drop_pending_updates` instead.',
|
||||
category=TelegramDeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
drop_pending_updates = drop_pending_updates if drop_pending_updates is not None else clean
|
||||
|
||||
with self.__lock:
|
||||
if not self.running:
|
||||
self.running = True
|
||||
@@ -290,7 +310,7 @@ class Updater:
|
||||
timeout,
|
||||
read_latency,
|
||||
bootstrap_retries,
|
||||
clean,
|
||||
drop_pending_updates,
|
||||
allowed_updates,
|
||||
ready=polling_ready,
|
||||
)
|
||||
@@ -310,18 +330,20 @@ class Updater:
|
||||
url_path: str = '',
|
||||
cert: str = None,
|
||||
key: str = None,
|
||||
clean: bool = False,
|
||||
clean: bool = None,
|
||||
bootstrap_retries: int = 0,
|
||||
webhook_url: str = None,
|
||||
allowed_updates: List[str] = None,
|
||||
force_event_loop: bool = False,
|
||||
drop_pending_updates: bool = None,
|
||||
ip_address: str = None,
|
||||
) -> Optional[Queue]:
|
||||
"""
|
||||
Starts a small http server to listen for updates via webhook. If cert
|
||||
and key are not provided, the webhook will be started directly on
|
||||
http://listen:port/url_path, so SSL can be handled by another
|
||||
application. Else, the webhook will be started on
|
||||
https://listen:port/url_path
|
||||
https://listen:port/url_path. Also calls :meth:`telegram.Bot.set_webhook` as required.
|
||||
|
||||
Note:
|
||||
Due to an incompatibility of the Tornado library PTB uses for the webhook with Python
|
||||
@@ -338,19 +360,29 @@ class Updater:
|
||||
url_path (:obj:`str`, optional): Path inside url.
|
||||
cert (:obj:`str`, optional): Path to the SSL certificate file.
|
||||
key (:obj:`str`, optional): Path to the SSL key file.
|
||||
clean (:obj:`bool`, optional): Whether to clean any pending updates on Telegram servers
|
||||
before actually starting the webhook. Default is :obj:`False`.
|
||||
drop_pending_updates (:obj:`bool`, optional): Whether to clean any pending updates on
|
||||
Telegram servers before actually starting to poll. Default is :obj:`False`.
|
||||
|
||||
.. versionadded :: 13.4
|
||||
clean (:obj:`bool`, optional): Alias for ``drop_pending_updates``.
|
||||
|
||||
.. deprecated:: 13.4
|
||||
Use ``drop_pending_updates`` instead.
|
||||
bootstrap_retries (:obj:`int`, optional): Whether the bootstrapping phase of the
|
||||
`Updater` will retry on failures on the Telegram server.
|
||||
:class:`telegram.ext.Updater` will retry on failures on the Telegram server.
|
||||
|
||||
* < 0 - retry indefinitely (default)
|
||||
* 0 - no retries
|
||||
* > 0 - retry up to X times
|
||||
|
||||
webhook_url (:obj:`str`, optional): Explicitly specify the webhook url. Useful behind
|
||||
NAT, reverse proxy, etc. Default is derived from `listen`, `port` & `url_path`.
|
||||
NAT, reverse proxy, etc. Default is derived from ``listen``, ``port`` &
|
||||
``url_path``.
|
||||
ip_address (:obj:`str`, optional): Passed to :meth:`telegram.Bot.set_webhook`.
|
||||
|
||||
.. versionadded :: 13.4
|
||||
allowed_updates (List[:obj:`str`], optional): Passed to
|
||||
:attr:`telegram.Bot.set_webhook`.
|
||||
:meth:`telegram.Bot.set_webhook`.
|
||||
force_event_loop (:obj:`bool`, optional): Force using the current event loop. See above
|
||||
note for details. Defaults to :obj:`False`
|
||||
|
||||
@@ -358,6 +390,19 @@ class Updater:
|
||||
:obj:`Queue`: The update queue that can be filled from the main thread.
|
||||
|
||||
"""
|
||||
if (clean is not None) and (drop_pending_updates is not None):
|
||||
raise TypeError('`clean` and `drop_pending_updates` are mutually exclusive.')
|
||||
|
||||
if clean is not None:
|
||||
warnings.warn(
|
||||
'The argument `clean` of `start_webhook` is deprecated. Please use '
|
||||
'`drop_pending_updates` instead.',
|
||||
category=TelegramDeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
drop_pending_updates = drop_pending_updates if drop_pending_updates is not None else clean
|
||||
|
||||
with self.__lock:
|
||||
if not self.running:
|
||||
self.running = True
|
||||
@@ -376,11 +421,12 @@ class Updater:
|
||||
cert,
|
||||
key,
|
||||
bootstrap_retries,
|
||||
clean,
|
||||
drop_pending_updates,
|
||||
webhook_url,
|
||||
allowed_updates,
|
||||
ready=webhook_ready,
|
||||
force_event_loop=force_event_loop,
|
||||
ip_address=ip_address,
|
||||
)
|
||||
|
||||
self.logger.debug('Waiting for Dispatcher and Webhook to start')
|
||||
@@ -398,7 +444,7 @@ class Updater:
|
||||
timeout,
|
||||
read_latency,
|
||||
bootstrap_retries,
|
||||
clean,
|
||||
drop_pending_updates,
|
||||
allowed_updates,
|
||||
ready=None,
|
||||
): # pragma: no cover
|
||||
@@ -408,7 +454,12 @@ class Updater:
|
||||
|
||||
self.logger.debug('Updater thread started (polling)')
|
||||
|
||||
self._bootstrap(bootstrap_retries, clean=clean, webhook_url='', allowed_updates=None)
|
||||
self._bootstrap(
|
||||
bootstrap_retries,
|
||||
drop_pending_updates=drop_pending_updates,
|
||||
webhook_url='',
|
||||
allowed_updates=None,
|
||||
)
|
||||
|
||||
self.logger.debug('Bootstrap done')
|
||||
|
||||
@@ -504,14 +555,20 @@ class Updater:
|
||||
cert,
|
||||
key,
|
||||
bootstrap_retries,
|
||||
clean,
|
||||
drop_pending_updates,
|
||||
webhook_url,
|
||||
allowed_updates,
|
||||
ready=None,
|
||||
force_event_loop=False,
|
||||
ip_address=None,
|
||||
):
|
||||
self.logger.debug('Updater thread started (webhook)')
|
||||
|
||||
# Note that we only use the SSL certificate for the WebhookServer, if the key is also
|
||||
# present. This is because the WebhookServer may not actually be in charge of performing
|
||||
# the SSL handshake, e.g. in case a reverse proxy is used
|
||||
use_ssl = cert is not None and key is not None
|
||||
|
||||
if not url_path.startswith('/'):
|
||||
url_path = f'/{url_path}'
|
||||
|
||||
@@ -532,23 +589,18 @@ class Updater:
|
||||
# Create and start server
|
||||
self.httpd = WebhookServer(listen, port, app, ssl_ctx)
|
||||
|
||||
if use_ssl:
|
||||
# DO NOT CHANGE: Only set webhook if SSL is handled by library
|
||||
if not webhook_url:
|
||||
webhook_url = self._gen_webhook_url(listen, port, url_path)
|
||||
if not webhook_url:
|
||||
webhook_url = self._gen_webhook_url(listen, port, url_path)
|
||||
|
||||
self._bootstrap(
|
||||
max_retries=bootstrap_retries,
|
||||
clean=clean,
|
||||
webhook_url=webhook_url,
|
||||
cert=open(cert, 'rb'),
|
||||
allowed_updates=allowed_updates,
|
||||
)
|
||||
elif clean:
|
||||
self.logger.warning(
|
||||
"cleaning updates is not supported if "
|
||||
"SSL-termination happens elsewhere; skipping"
|
||||
)
|
||||
# We pass along the cert to the webhook if present.
|
||||
self._bootstrap(
|
||||
max_retries=bootstrap_retries,
|
||||
drop_pending_updates=drop_pending_updates,
|
||||
webhook_url=webhook_url,
|
||||
allowed_updates=allowed_updates,
|
||||
cert=open(cert, 'rb') if cert is not None else None,
|
||||
ip_address=ip_address,
|
||||
)
|
||||
|
||||
self.httpd.serve_forever(force_event_loop=force_event_loop, ready=ready)
|
||||
|
||||
@@ -558,24 +610,34 @@ class Updater:
|
||||
|
||||
@no_type_check
|
||||
def _bootstrap(
|
||||
self, max_retries, clean, webhook_url, allowed_updates, cert=None, bootstrap_interval=5
|
||||
self,
|
||||
max_retries,
|
||||
drop_pending_updates,
|
||||
webhook_url,
|
||||
allowed_updates,
|
||||
cert=None,
|
||||
bootstrap_interval=5,
|
||||
ip_address=None,
|
||||
):
|
||||
retries = [0]
|
||||
|
||||
def bootstrap_del_webhook():
|
||||
self.bot.delete_webhook()
|
||||
return False
|
||||
|
||||
def bootstrap_clean_updates():
|
||||
self.logger.debug('Cleaning updates from Telegram server')
|
||||
updates = self.bot.get_updates()
|
||||
while updates:
|
||||
updates = self.bot.get_updates(updates[-1].update_id + 1)
|
||||
self.logger.debug('Deleting webhook')
|
||||
if drop_pending_updates:
|
||||
self.logger.debug('Dropping pending updates from Telegram server')
|
||||
self.bot.delete_webhook(drop_pending_updates=drop_pending_updates)
|
||||
return False
|
||||
|
||||
def bootstrap_set_webhook():
|
||||
self.logger.debug('Setting webhook')
|
||||
if drop_pending_updates:
|
||||
self.logger.debug('Dropping pending updates from Telegram server')
|
||||
self.bot.set_webhook(
|
||||
url=webhook_url, certificate=cert, allowed_updates=allowed_updates
|
||||
url=webhook_url,
|
||||
certificate=cert,
|
||||
allowed_updates=allowed_updates,
|
||||
ip_address=ip_address,
|
||||
drop_pending_updates=drop_pending_updates,
|
||||
)
|
||||
return False
|
||||
|
||||
@@ -589,11 +651,11 @@ class Updater:
|
||||
self.logger.error('Failed bootstrap phase after %s retries (%s)', retries[0], exc)
|
||||
raise exc
|
||||
|
||||
# Cleaning pending messages is done by polling for them - so we need to delete webhook if
|
||||
# one is configured.
|
||||
# We also take this chance to delete pre-configured webhook if this is a polling Updater.
|
||||
# NOTE: We don't know ahead if a webhook is configured, so we just delete.
|
||||
if clean or not webhook_url:
|
||||
# Dropping pending updates from TG can be efficiently done with the drop_pending_updates
|
||||
# parameter of delete/start_webhook, even in the case of polling. Also we want to make
|
||||
# sure that no webhook is configured in case of polling, so we just always call
|
||||
# delete_webhook for polling
|
||||
if drop_pending_updates or not webhook_url:
|
||||
self._network_loop_retry(
|
||||
bootstrap_del_webhook,
|
||||
bootstrap_onerr_cb,
|
||||
@@ -602,17 +664,6 @@ class Updater:
|
||||
)
|
||||
retries[0] = 0
|
||||
|
||||
# Clean pending messages, if requested.
|
||||
if clean:
|
||||
self._network_loop_retry(
|
||||
bootstrap_clean_updates,
|
||||
bootstrap_onerr_cb,
|
||||
'bootstrap clean updates',
|
||||
bootstrap_interval,
|
||||
)
|
||||
retries[0] = 0
|
||||
sleep(1)
|
||||
|
||||
# Restore/set webhook settings, if needed. Again, we don't know ahead if a webhook is set,
|
||||
# so we set it anyhow.
|
||||
if webhook_url:
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
# pylint: disable=E0401, C0114
|
||||
# pylint: disable=C0114
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
@@ -95,7 +95,7 @@ class WebhookServer:
|
||||
not force_event_loop
|
||||
and os.name == 'nt'
|
||||
and sys.version_info >= (3, 8)
|
||||
and isinstance(loop, asyncio.ProactorEventLoop)
|
||||
and isinstance(loop, asyncio.ProactorEventLoop) # type: ignore[attr-defined]
|
||||
):
|
||||
raise TypeError(
|
||||
'`ProactorEventLoop` is incompatible with '
|
||||
@@ -123,7 +123,7 @@ class WebhookServer:
|
||||
and (
|
||||
isinstance(
|
||||
asyncio.get_event_loop_policy(),
|
||||
asyncio.WindowsProactorEventLoopPolicy, # pylint: disable=E1101
|
||||
asyncio.WindowsProactorEventLoopPolicy, # type: ignore # pylint: disable
|
||||
)
|
||||
)
|
||||
): # pylint: disable=E1101
|
||||
@@ -140,7 +140,7 @@ class WebhookAppClass(tornado.web.Application):
|
||||
def __init__(self, webhook_path: str, bot: 'Bot', update_queue: Queue):
|
||||
self.shared_objects = {"bot": bot, "update_queue": update_queue}
|
||||
handlers = [(rf"{webhook_path}/?", WebhookHandler, self.shared_objects)] # noqa
|
||||
tornado.web.Application.__init__(self, handlers)
|
||||
tornado.web.Application.__init__(self, handlers) # type: ignore
|
||||
|
||||
def log_request(self, handler: tornado.web.RequestHandler) -> None:
|
||||
pass
|
||||
@@ -149,7 +149,7 @@ class WebhookAppClass(tornado.web.Application):
|
||||
# WebhookHandler, process webhook calls
|
||||
# pylint: disable=W0223
|
||||
class WebhookHandler(tornado.web.RequestHandler):
|
||||
SUPPORTED_METHODS = ["POST"]
|
||||
SUPPORTED_METHODS = ["POST"] # type: ignore
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents a Telegram InlineQuery."""
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Optional, List, Union, Callable, ClassVar
|
||||
from typing import TYPE_CHECKING, Any, Optional, Union, Callable, ClassVar, Sequence
|
||||
|
||||
from telegram import Location, TelegramObject, User, constants
|
||||
from telegram.utils.helpers import DEFAULT_NONE
|
||||
@@ -38,7 +38,7 @@ class InlineQuery(TelegramObject):
|
||||
considered equal, if their :attr:`id` is equal.
|
||||
|
||||
Note:
|
||||
* In Python `from` is a reserved word, use `from_user` instead.
|
||||
* In Python ``from`` is a reserved word, use ``from_user`` instead.
|
||||
|
||||
Args:
|
||||
id (:obj:`str`): Unique identifier for this query.
|
||||
@@ -97,7 +97,7 @@ class InlineQuery(TelegramObject):
|
||||
def answer(
|
||||
self,
|
||||
results: Union[
|
||||
List['InlineQueryResult'], Callable[[int], Optional[List['InlineQueryResult']]]
|
||||
Sequence['InlineQueryResult'], Callable[[int], Optional[Sequence['InlineQueryResult']]]
|
||||
],
|
||||
cache_time: int = 300,
|
||||
is_personal: bool = None,
|
||||
@@ -125,7 +125,7 @@ class InlineQuery(TelegramObject):
|
||||
Defaults to :obj:`False`.
|
||||
|
||||
Raises:
|
||||
TypeError: If both :attr:`current_offset` and attr:`auto_pagination` are supplied.
|
||||
TypeError: If both :attr:`current_offset` and :attr:`auto_pagination` are supplied.
|
||||
"""
|
||||
if current_offset and auto_pagination:
|
||||
# We raise TypeError instead of ValueError for backwards compatibility with versions
|
||||
|
||||
@@ -31,6 +31,10 @@ class InlineQueryResult(TelegramObject):
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`id` is equal.
|
||||
|
||||
Note:
|
||||
All URLs passed in inline query results will be available to end users and therefore must
|
||||
be assumed to be public.
|
||||
|
||||
Args:
|
||||
type (:obj:`str`): Type of the result.
|
||||
id (:obj:`str`): Unique identifier for this result, 1-64 Bytes.
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
from typing import Any
|
||||
|
||||
from telegram import TelegramObject
|
||||
from telegram import TelegramObject, KeyboardButtonPollType
|
||||
|
||||
|
||||
class KeyboardButton(TelegramObject):
|
||||
@@ -63,7 +63,7 @@ class KeyboardButton(TelegramObject):
|
||||
text: str,
|
||||
request_contact: bool = None,
|
||||
request_location: bool = None,
|
||||
request_poll: bool = None,
|
||||
request_poll: KeyboardButtonPollType = None,
|
||||
**_kwargs: Any,
|
||||
):
|
||||
# Required
|
||||
|
||||
+81
-28
@@ -47,8 +47,12 @@ from telegram import (
|
||||
Video,
|
||||
VideoNote,
|
||||
Voice,
|
||||
VoiceChatStarted,
|
||||
VoiceChatEnded,
|
||||
VoiceChatParticipantsInvited,
|
||||
ProximityAlertTriggered,
|
||||
ReplyMarkup,
|
||||
MessageAutoDeleteTimerChanged,
|
||||
)
|
||||
from telegram.utils.helpers import (
|
||||
escape_markdown,
|
||||
@@ -83,7 +87,7 @@ class Message(TelegramObject):
|
||||
considered equal, if their :attr:`message_id` and :attr:`chat` are equal.
|
||||
|
||||
Note:
|
||||
In Python `from` is a reserved word, use `from_user` instead.
|
||||
In Python ``from`` is a reserved word, use ``from_user`` instead.
|
||||
|
||||
Args:
|
||||
message_id (:obj:`int`): Unique message identifier inside this chat.
|
||||
@@ -165,6 +169,10 @@ class Message(TelegramObject):
|
||||
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
|
||||
:attr:`reply_to_message` if someone replies to a very first message in a channel.
|
||||
message_auto_delete_timer_changed (:class:`telegram.MessageAutoDeleteTimerChanged`, \
|
||||
optional): Service message: auto-delete timer settings changed in the chat.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
migrate_to_chat_id (:obj:`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
|
||||
@@ -196,6 +204,18 @@ class Message(TelegramObject):
|
||||
proximity_alert_triggered (:class:`telegram.ProximityAlertTriggered`, optional): Service
|
||||
message. A user in the chat triggered another user's proximity alert while sharing
|
||||
Live Location.
|
||||
voice_chat_started (:class:`telegram.VoiceChatStarted`, optional): Service message: voice
|
||||
chat started.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
voice_chat_ended (:class:`telegram.VoiceChatEnded`, optional): Service message: voice chat
|
||||
ended.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
voice_chat_participants_invited (:class:`telegram.VoiceChatParticipantsInvited` optional):
|
||||
Service message: new participants invited to a voice chat.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached
|
||||
to the message. ``login_url`` buttons are represented as ordinary url buttons.
|
||||
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods.
|
||||
@@ -257,6 +277,10 @@ class Message(TelegramObject):
|
||||
group_chat_created (:obj:`bool`): Optional. The group has been created.
|
||||
supergroup_chat_created (:obj:`bool`): Optional. The supergroup has been created.
|
||||
channel_chat_created (:obj:`bool`): Optional. The channel has been created.
|
||||
message_auto_delete_timer_changed (:class:`telegram.MessageAutoDeleteTimerChanged`):
|
||||
Optional. Service message: auto-delete timer settings changed in the chat.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
migrate_to_chat_id (:obj:`int`): Optional. The group has been migrated to a supergroup with
|
||||
the specified identifier.
|
||||
migrate_from_chat_id (:obj:`int`): Optional. The supergroup has been migrated from a group
|
||||
@@ -281,6 +305,18 @@ class Message(TelegramObject):
|
||||
proximity_alert_triggered (:class:`telegram.ProximityAlertTriggered`): Optional. Service
|
||||
message. A user in the chat triggered another user's proximity alert while sharing
|
||||
Live Location.
|
||||
voice_chat_started (:class:`telegram.VoiceChatStarted`): Optional. Service message: voice
|
||||
chat started
|
||||
|
||||
.. versionadded:: 13.4
|
||||
voice_chat_ended (:class:`telegram.VoiceChatEnded`): Optional. Service message: voice chat
|
||||
ended.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
voice_chat_participants_invited (:class:`telegram.VoiceChatParticipantsInvited`): Optional.
|
||||
Service message: new participants invited to a voice chat.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached
|
||||
to the message.
|
||||
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
|
||||
@@ -316,6 +352,7 @@ class Message(TelegramObject):
|
||||
'group_chat_created',
|
||||
'supergroup_chat_created',
|
||||
'channel_chat_created',
|
||||
'message_auto_delete_timer_changed',
|
||||
'migrate_to_chat_id',
|
||||
'migrate_from_chat_id',
|
||||
'pinned_message',
|
||||
@@ -323,6 +360,9 @@ class Message(TelegramObject):
|
||||
'dice',
|
||||
'passport_data',
|
||||
'proximity_alert_triggered',
|
||||
'voice_chat_started',
|
||||
'voice_chat_ended',
|
||||
'voice_chat_participants_invited',
|
||||
] + ATTACHMENT_TYPES
|
||||
|
||||
def __init__(
|
||||
@@ -379,6 +419,10 @@ class Message(TelegramObject):
|
||||
via_bot: User = None,
|
||||
proximity_alert_triggered: ProximityAlertTriggered = None,
|
||||
sender_chat: Chat = None,
|
||||
voice_chat_started: VoiceChatStarted = None,
|
||||
voice_chat_ended: VoiceChatEnded = None,
|
||||
voice_chat_participants_invited: VoiceChatParticipantsInvited = None,
|
||||
message_auto_delete_timer_changed: MessageAutoDeleteTimerChanged = None,
|
||||
**_kwargs: Any,
|
||||
):
|
||||
# Required
|
||||
@@ -418,6 +462,7 @@ class Message(TelegramObject):
|
||||
self.migrate_to_chat_id = migrate_to_chat_id
|
||||
self.migrate_from_chat_id = migrate_from_chat_id
|
||||
self.channel_chat_created = bool(channel_chat_created)
|
||||
self.message_auto_delete_timer_changed = message_auto_delete_timer_changed
|
||||
self.pinned_message = pinned_message
|
||||
self.forward_from_message_id = forward_from_message_id
|
||||
self.invoice = invoice
|
||||
@@ -433,6 +478,9 @@ class Message(TelegramObject):
|
||||
self.dice = dice
|
||||
self.via_bot = via_bot
|
||||
self.proximity_alert_triggered = proximity_alert_triggered
|
||||
self.voice_chat_started = voice_chat_started
|
||||
self.voice_chat_ended = voice_chat_ended
|
||||
self.voice_chat_participants_invited = voice_chat_participants_invited
|
||||
self.reply_markup = reply_markup
|
||||
self.bot = bot
|
||||
|
||||
@@ -489,6 +537,9 @@ class Message(TelegramObject):
|
||||
data['new_chat_members'] = User.de_list(data.get('new_chat_members'), bot)
|
||||
data['left_chat_member'] = User.de_json(data.get('left_chat_member'), bot)
|
||||
data['new_chat_photo'] = PhotoSize.de_list(data.get('new_chat_photo'), bot)
|
||||
data['message_auto_delete_timer_changed'] = MessageAutoDeleteTimerChanged.de_json(
|
||||
data.get('message_auto_delete_timer_changed'), bot
|
||||
)
|
||||
data['pinned_message'] = Message.de_json(data.get('pinned_message'), bot)
|
||||
data['invoice'] = Invoice.de_json(data.get('invoice'), bot)
|
||||
data['successful_payment'] = SuccessfulPayment.de_json(data.get('successful_payment'), bot)
|
||||
@@ -500,7 +551,11 @@ class Message(TelegramObject):
|
||||
data.get('proximity_alert_triggered'), bot
|
||||
)
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
|
||||
|
||||
data['voice_chat_started'] = VoiceChatStarted.de_json(data.get('voice_chat_started'), bot)
|
||||
data['voice_chat_ended'] = VoiceChatEnded.de_json(data.get('voice_chat_ended'), bot)
|
||||
data['voice_chat_participants_invited'] = VoiceChatParticipantsInvited.de_json(
|
||||
data.get('voice_chat_participants_invited'), bot
|
||||
)
|
||||
return cls(bot=bot, **data)
|
||||
|
||||
@property
|
||||
@@ -581,9 +636,7 @@ class Message(TelegramObject):
|
||||
|
||||
return data
|
||||
|
||||
def _quote(
|
||||
self, quote: Optional[bool], reply_to_message_id: Optional[Union[int, str]]
|
||||
) -> Optional[Union[int, str]]:
|
||||
def _quote(self, quote: Optional[bool], reply_to_message_id: Optional[int]) -> Optional[int]:
|
||||
"""Modify kwargs for replying with or without quoting."""
|
||||
if reply_to_message_id is not None:
|
||||
return reply_to_message_id
|
||||
@@ -608,7 +661,7 @@ class Message(TelegramObject):
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: ReplyMarkup = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
@@ -652,7 +705,7 @@ class Message(TelegramObject):
|
||||
text: str,
|
||||
disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: ReplyMarkup = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
@@ -706,7 +759,7 @@ class Message(TelegramObject):
|
||||
text: str,
|
||||
disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: ReplyMarkup = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
@@ -756,7 +809,7 @@ class Message(TelegramObject):
|
||||
text: str,
|
||||
disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: ReplyMarkup = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
@@ -807,7 +860,7 @@ class Message(TelegramObject):
|
||||
Union['InputMediaAudio', 'InputMediaDocument', 'InputMediaPhoto', 'InputMediaVideo']
|
||||
],
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
timeout: DVInput[float] = DEFAULT_20,
|
||||
api_kwargs: JSONDict = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -847,7 +900,7 @@ class Message(TelegramObject):
|
||||
photo: Union[FileInput, 'PhotoSize'],
|
||||
caption: str = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: ReplyMarkup = None,
|
||||
timeout: DVInput[float] = DEFAULT_20,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
@@ -897,7 +950,7 @@ class Message(TelegramObject):
|
||||
title: str = None,
|
||||
caption: str = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: ReplyMarkup = None,
|
||||
timeout: DVInput[float] = DEFAULT_20,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
@@ -950,7 +1003,7 @@ class Message(TelegramObject):
|
||||
filename: str = None,
|
||||
caption: str = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: ReplyMarkup = None,
|
||||
timeout: DVInput[float] = DEFAULT_20,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
@@ -1005,7 +1058,7 @@ class Message(TelegramObject):
|
||||
caption: str = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: ReplyMarkup = None,
|
||||
timeout: DVInput[float] = DEFAULT_20,
|
||||
api_kwargs: JSONDict = None,
|
||||
@@ -1054,7 +1107,7 @@ class Message(TelegramObject):
|
||||
self,
|
||||
sticker: Union[FileInput, 'Sticker'],
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: ReplyMarkup = None,
|
||||
timeout: DVInput[float] = DEFAULT_20,
|
||||
api_kwargs: JSONDict = None,
|
||||
@@ -1095,7 +1148,7 @@ class Message(TelegramObject):
|
||||
duration: int = None,
|
||||
caption: str = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: ReplyMarkup = None,
|
||||
timeout: DVInput[float] = DEFAULT_20,
|
||||
width: int = None,
|
||||
@@ -1152,7 +1205,7 @@ class Message(TelegramObject):
|
||||
duration: int = None,
|
||||
length: int = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: ReplyMarkup = None,
|
||||
timeout: DVInput[float] = DEFAULT_20,
|
||||
thumb: FileInput = None,
|
||||
@@ -1199,7 +1252,7 @@ class Message(TelegramObject):
|
||||
duration: int = None,
|
||||
caption: str = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: ReplyMarkup = None,
|
||||
timeout: DVInput[float] = DEFAULT_20,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
@@ -1247,7 +1300,7 @@ class Message(TelegramObject):
|
||||
latitude: float = None,
|
||||
longitude: float = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: ReplyMarkup = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
location: Location = None,
|
||||
@@ -1301,7 +1354,7 @@ class Message(TelegramObject):
|
||||
address: str = None,
|
||||
foursquare_id: str = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: ReplyMarkup = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
venue: Venue = None,
|
||||
@@ -1354,7 +1407,7 @@ class Message(TelegramObject):
|
||||
first_name: str = None,
|
||||
last_name: str = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: ReplyMarkup = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
contact: Contact = None,
|
||||
@@ -1405,7 +1458,7 @@ class Message(TelegramObject):
|
||||
correct_option_id: int = None,
|
||||
is_closed: bool = None,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: ReplyMarkup = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
explanation: str = None,
|
||||
@@ -1459,7 +1512,7 @@ class Message(TelegramObject):
|
||||
def reply_dice(
|
||||
self,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: ReplyMarkup = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
emoji: str = None,
|
||||
@@ -1524,7 +1577,7 @@ class Message(TelegramObject):
|
||||
self,
|
||||
game_short_name: str,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: 'InlineKeyboardMarkup' = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
@@ -1580,7 +1633,7 @@ class Message(TelegramObject):
|
||||
need_shipping_address: bool = None,
|
||||
is_flexible: bool = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: 'InlineKeyboardMarkup' = None,
|
||||
provider_data: Union[str, object] = None,
|
||||
send_phone_number_to_provider: bool = None,
|
||||
@@ -1675,7 +1728,7 @@ class Message(TelegramObject):
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
caption_entities: Union[Tuple['MessageEntity', ...], List['MessageEntity']] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_markup: ReplyMarkup = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -1713,12 +1766,12 @@ class Message(TelegramObject):
|
||||
def reply_copy(
|
||||
self,
|
||||
from_chat_id: Union[str, int],
|
||||
message_id: Union[str, int],
|
||||
message_id: int,
|
||||
caption: str = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
caption_entities: Union[Tuple['MessageEntity', ...], List['MessageEntity']] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_markup: ReplyMarkup = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2021
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents a change in the Telegram message auto
|
||||
deletion."""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from telegram import TelegramObject
|
||||
|
||||
|
||||
class MessageAutoDeleteTimerChanged(TelegramObject):
|
||||
"""This object represents a service message about a change in auto-delete timer settings.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`message_auto_delete_time` is equal.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
|
||||
Args:
|
||||
message_auto_delete_time (:obj:`int`): New auto-delete time for messages in the
|
||||
chat.
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
Attributes:
|
||||
message_auto_delete_time (:obj:`int`): New auto-delete time for messages in the
|
||||
chat.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message_auto_delete_time: int,
|
||||
**_kwargs: Any,
|
||||
):
|
||||
self.message_auto_delete_time = int(message_auto_delete_time)
|
||||
|
||||
self._id_attrs = (self.message_auto_delete_time,)
|
||||
@@ -16,7 +16,7 @@
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
# pylint: disable=C0114, E0401, W0622
|
||||
# pylint: disable=C0114, W0622
|
||||
try:
|
||||
import ujson as json
|
||||
except ImportError:
|
||||
|
||||
@@ -35,7 +35,7 @@ class PreCheckoutQuery(TelegramObject):
|
||||
considered equal, if their :attr:`id` is equal.
|
||||
|
||||
Note:
|
||||
In Python `from` is a reserved word, use `from_user` instead.
|
||||
In Python ``from`` is a reserved word, use ``from_user`` instead.
|
||||
|
||||
Args:
|
||||
id (:obj:`str`): Unique query identifier.
|
||||
|
||||
@@ -35,7 +35,7 @@ class ShippingQuery(TelegramObject):
|
||||
considered equal, if their :attr:`id` is equal.
|
||||
|
||||
Note:
|
||||
In Python `from` is a reserved word, use `from_user` instead.
|
||||
In Python ``from`` is a reserved word, use ``from_user`` instead.
|
||||
|
||||
Args:
|
||||
id (:obj:`str`): Unique query identifier.
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents a Telegram ReplyKeyboardMarkup."""
|
||||
|
||||
from typing import Any, List, Union
|
||||
from typing import Any, List, Union, Sequence
|
||||
|
||||
from telegram import KeyboardButton, ReplyMarkup
|
||||
from telegram.utils.types import JSONDict
|
||||
@@ -67,7 +67,7 @@ class ReplyKeyboardMarkup(ReplyMarkup):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
keyboard: List[List[Union[str, KeyboardButton]]],
|
||||
keyboard: Sequence[Sequence[Union[str, KeyboardButton]]],
|
||||
resize_keyboard: bool = False,
|
||||
one_time_keyboard: bool = False,
|
||||
selective: bool = False,
|
||||
|
||||
@@ -29,6 +29,7 @@ from telegram import (
|
||||
PreCheckoutQuery,
|
||||
ShippingQuery,
|
||||
TelegramObject,
|
||||
ChatMemberUpdated,
|
||||
)
|
||||
from telegram.poll import PollAnswer
|
||||
from telegram.utils.types import JSONDict
|
||||
@@ -74,6 +75,19 @@ class Update(TelegramObject):
|
||||
poll_answer (:class:`telegram.PollAnswer`, optional): A user changed their answer
|
||||
in a non-anonymous poll. Bots receive new votes only in polls that were sent
|
||||
by the bot itself.
|
||||
my_chat_member (:class:`telegram.ChatMemberUpdated`, optional): The bot's chat member
|
||||
status was updated in a chat. For private chats, this update is received only when the
|
||||
bot is blocked or unblocked by the user.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
chat_member (:class:`telegram.ChatMemberUpdated`, optional): A chat member's status was
|
||||
updated in a chat. The bot must be an administrator in the chat and must explicitly
|
||||
specify ``'chat_member'`` in the list of ``'allowed_updates'`` to receive these
|
||||
updates (see :meth:`telegram.Bot.get_updates`, :meth:`telegram.Bot.set_webhook`,
|
||||
:meth:`telegram.ext.Updater.start_polling` and
|
||||
:meth:`telegram.ext.Updater.start_webhook`).
|
||||
|
||||
.. versionadded:: 13.4
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
Attributes:
|
||||
@@ -94,6 +108,19 @@ class Update(TelegramObject):
|
||||
poll_answer (:class:`telegram.PollAnswer`): Optional. A user changed their answer
|
||||
in a non-anonymous poll. Bots receive new votes only in polls that were sent
|
||||
by the bot itself.
|
||||
my_chat_member (:class:`telegram.ChatMemberUpdated`): Optional. The bot's chat member
|
||||
status was updated in a chat. For private chats, this update is received only when the
|
||||
bot is blocked or unblocked by the user.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
chat_member (:class:`telegram.ChatMemberUpdated`): Optional. A chat member's status was
|
||||
updated in a chat. The bot must be an administrator in the chat and must explicitly
|
||||
specify ``'chat_member'`` in the list of ``'allowed_updates'`` to receive these
|
||||
updates (see :meth:`telegram.Bot.get_updates`, :meth:`telegram.Bot.set_webhook`,
|
||||
:meth:`telegram.ext.Updater.start_polling` and
|
||||
:meth:`telegram.ext.Updater.start_webhook`).
|
||||
|
||||
.. versionadded:: 13.4
|
||||
|
||||
"""
|
||||
|
||||
@@ -111,6 +138,8 @@ class Update(TelegramObject):
|
||||
pre_checkout_query: PreCheckoutQuery = None,
|
||||
poll: Poll = None,
|
||||
poll_answer: PollAnswer = None,
|
||||
my_chat_member: ChatMemberUpdated = None,
|
||||
chat_member: ChatMemberUpdated = None,
|
||||
**_kwargs: Any,
|
||||
):
|
||||
# Required
|
||||
@@ -127,6 +156,8 @@ class Update(TelegramObject):
|
||||
self.edited_channel_post = edited_channel_post
|
||||
self.poll = poll
|
||||
self.poll_answer = poll_answer
|
||||
self.my_chat_member = my_chat_member
|
||||
self.chat_member = chat_member
|
||||
|
||||
self._effective_user: Optional['User'] = None
|
||||
self._effective_chat: Optional['Chat'] = None
|
||||
@@ -170,6 +201,12 @@ class Update(TelegramObject):
|
||||
elif self.poll_answer:
|
||||
user = self.poll_answer.user
|
||||
|
||||
elif self.my_chat_member:
|
||||
user = self.my_chat_member.from_user
|
||||
|
||||
elif self.chat_member:
|
||||
user = self.chat_member.from_user
|
||||
|
||||
self._effective_user = user
|
||||
return user
|
||||
|
||||
@@ -203,6 +240,12 @@ class Update(TelegramObject):
|
||||
elif self.edited_channel_post:
|
||||
chat = self.edited_channel_post.chat
|
||||
|
||||
elif self.my_chat_member:
|
||||
chat = self.my_chat_member.chat
|
||||
|
||||
elif self.chat_member:
|
||||
chat = self.chat_member.chat
|
||||
|
||||
self._effective_chat = chat
|
||||
return chat
|
||||
|
||||
@@ -259,5 +302,7 @@ class Update(TelegramObject):
|
||||
data['edited_channel_post'] = Message.de_json(data.get('edited_channel_post'), bot)
|
||||
data['poll'] = Poll.de_json(data.get('poll'), bot)
|
||||
data['poll_answer'] = PollAnswer.de_json(data.get('poll_answer'), bot)
|
||||
data['my_chat_member'] = ChatMemberUpdated.de_json(data.get('my_chat_member'), bot)
|
||||
data['chat_member'] = ChatMemberUpdated.de_json(data.get('chat_member'), bot)
|
||||
|
||||
return cls(**data)
|
||||
|
||||
+26
-26
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=C0103,W0622
|
||||
# pylint: disable=W0622
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2021
|
||||
@@ -111,7 +111,7 @@ class User(TelegramObject):
|
||||
**_kwargs: Any,
|
||||
):
|
||||
# Required
|
||||
self.id = int(id)
|
||||
self.id = int(id) # pylint: disable=C0103
|
||||
self.first_name = first_name
|
||||
self.is_bot = is_bot
|
||||
# Optionals
|
||||
@@ -161,7 +161,7 @@ class User(TelegramObject):
|
||||
"""
|
||||
Shortcut for::
|
||||
|
||||
bot.send_message(update.effective_user.id, *args, **kwargs)
|
||||
bot.get_user_profile_photos(update.effective_user.id, *args, **kwargs)
|
||||
|
||||
For the documentation of the arguments, please see
|
||||
:meth:`telegram.Bot.get_user_profile_photos`.
|
||||
@@ -221,7 +221,7 @@ class User(TelegramObject):
|
||||
|
||||
def pin_message(
|
||||
self,
|
||||
message_id: Union[str, int],
|
||||
message_id: int,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
@@ -250,7 +250,7 @@ class User(TelegramObject):
|
||||
self,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
message_id: Union[str, int] = None,
|
||||
message_id: int = None,
|
||||
) -> bool:
|
||||
"""Shortcut for::
|
||||
|
||||
@@ -301,7 +301,7 @@ class User(TelegramObject):
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: 'ReplyMarkup' = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
@@ -337,7 +337,7 @@ class User(TelegramObject):
|
||||
photo: Union[FileInput, 'PhotoSize'],
|
||||
caption: str = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: 'ReplyMarkup' = None,
|
||||
timeout: DVInput[float] = DEFAULT_20,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
@@ -377,7 +377,7 @@ class User(TelegramObject):
|
||||
Union['InputMediaAudio', 'InputMediaDocument', 'InputMediaPhoto', 'InputMediaVideo']
|
||||
],
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
timeout: DVInput[float] = DEFAULT_20,
|
||||
api_kwargs: JSONDict = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
@@ -410,7 +410,7 @@ class User(TelegramObject):
|
||||
title: str = None,
|
||||
caption: str = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: 'ReplyMarkup' = None,
|
||||
timeout: DVInput[float] = DEFAULT_20,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
@@ -481,7 +481,7 @@ class User(TelegramObject):
|
||||
first_name: str = None,
|
||||
last_name: str = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: 'ReplyMarkup' = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
contact: 'Contact' = None,
|
||||
@@ -517,7 +517,7 @@ class User(TelegramObject):
|
||||
def send_dice(
|
||||
self,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: 'ReplyMarkup' = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
emoji: str = None,
|
||||
@@ -551,7 +551,7 @@ class User(TelegramObject):
|
||||
filename: str = None,
|
||||
caption: str = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: 'ReplyMarkup' = None,
|
||||
timeout: DVInput[float] = DEFAULT_20,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
@@ -592,7 +592,7 @@ class User(TelegramObject):
|
||||
self,
|
||||
game_short_name: str,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: 'InlineKeyboardMarkup' = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
@@ -638,7 +638,7 @@ class User(TelegramObject):
|
||||
need_shipping_address: bool = None,
|
||||
is_flexible: bool = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: 'InlineKeyboardMarkup' = None,
|
||||
provider_data: Union[str, object] = None,
|
||||
send_phone_number_to_provider: bool = None,
|
||||
@@ -691,7 +691,7 @@ class User(TelegramObject):
|
||||
latitude: float = None,
|
||||
longitude: float = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: 'ReplyMarkup' = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
location: 'Location' = None,
|
||||
@@ -739,7 +739,7 @@ class User(TelegramObject):
|
||||
caption: str = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: 'ReplyMarkup' = None,
|
||||
timeout: DVInput[float] = DEFAULT_20,
|
||||
api_kwargs: JSONDict = None,
|
||||
@@ -780,7 +780,7 @@ class User(TelegramObject):
|
||||
self,
|
||||
sticker: Union[FileInput, 'Sticker'],
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: 'ReplyMarkup' = None,
|
||||
timeout: DVInput[float] = DEFAULT_20,
|
||||
api_kwargs: JSONDict = None,
|
||||
@@ -813,7 +813,7 @@ class User(TelegramObject):
|
||||
duration: int = None,
|
||||
caption: str = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: 'ReplyMarkup' = None,
|
||||
timeout: DVInput[float] = DEFAULT_20,
|
||||
width: int = None,
|
||||
@@ -864,7 +864,7 @@ class User(TelegramObject):
|
||||
address: str = None,
|
||||
foursquare_id: str = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: 'ReplyMarkup' = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
venue: 'Venue' = None,
|
||||
@@ -909,7 +909,7 @@ class User(TelegramObject):
|
||||
duration: int = None,
|
||||
length: int = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: 'ReplyMarkup' = None,
|
||||
timeout: DVInput[float] = DEFAULT_20,
|
||||
thumb: FileInput = None,
|
||||
@@ -948,7 +948,7 @@ class User(TelegramObject):
|
||||
duration: int = None,
|
||||
caption: str = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: 'ReplyMarkup' = None,
|
||||
timeout: DVInput[float] = DEFAULT_20,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
@@ -994,7 +994,7 @@ class User(TelegramObject):
|
||||
correct_option_id: int = None,
|
||||
is_closed: bool = None,
|
||||
disable_notification: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
reply_markup: 'ReplyMarkup' = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
explanation: str = None,
|
||||
@@ -1040,12 +1040,12 @@ class User(TelegramObject):
|
||||
def send_copy(
|
||||
self,
|
||||
from_chat_id: Union[str, int],
|
||||
message_id: Union[str, int],
|
||||
message_id: int,
|
||||
caption: str = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
caption_entities: Union[Tuple['MessageEntity', ...], List['MessageEntity']] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_markup: 'ReplyMarkup' = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
@@ -1079,12 +1079,12 @@ class User(TelegramObject):
|
||||
def copy_message(
|
||||
self,
|
||||
chat_id: Union[int, str],
|
||||
message_id: Union[str, int],
|
||||
message_id: int,
|
||||
caption: str = None,
|
||||
parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
caption_entities: Union[Tuple['MessageEntity', ...], List['MessageEntity']] = None,
|
||||
disable_notification: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Union[int, str] = None,
|
||||
reply_to_message_id: int = None,
|
||||
allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE,
|
||||
reply_markup: 'ReplyMarkup' = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
|
||||
@@ -51,7 +51,7 @@ if TYPE_CHECKING:
|
||||
# in PTB-Raw we don't have pytz, so we make a little workaround here
|
||||
DTM_UTC = dtm.timezone.utc
|
||||
try:
|
||||
import pytz # pylint: disable=E0401
|
||||
import pytz
|
||||
|
||||
UTC = pytz.utc
|
||||
except ImportError:
|
||||
|
||||
@@ -30,7 +30,7 @@ except ImportError:
|
||||
|
||||
from typing import Any, Union
|
||||
|
||||
import certifi # pylint: disable=E0401
|
||||
import certifi
|
||||
|
||||
try:
|
||||
import telegram.vendor.ptb_urllib3.urllib3 as urllib3
|
||||
@@ -85,7 +85,7 @@ def _render_part(self: RequestField, name: str, value: str) -> str: # pylint: d
|
||||
|
||||
RequestField._render_part = _render_part # type: ignore # pylint: disable=W0212
|
||||
|
||||
logging.getLogger('urllib3').setLevel(logging.WARNING)
|
||||
logging.getLogger('telegram.vendor.ptb_urllib3.urllib3').setLevel(logging.WARNING)
|
||||
|
||||
USER_AGENT = 'Python Telegram Bot (https://github.com/python-telegram-bot/python-telegram-bot)'
|
||||
|
||||
|
||||
+4
-1
@@ -18,4 +18,7 @@
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
# pylint: disable=C0114
|
||||
|
||||
__version__ = '13.3'
|
||||
from telegram import constants
|
||||
|
||||
__version__ = '13.4'
|
||||
bot_api_version = constants.BOT_API_VERSION # pylint: disable=C0103
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=R0903
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2021
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains objects related to Telegram voice chats."""
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Optional, List
|
||||
|
||||
from telegram import TelegramObject, User
|
||||
from telegram.utils.types import JSONDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Bot
|
||||
|
||||
|
||||
class VoiceChatStarted(TelegramObject):
|
||||
"""
|
||||
This object represents a service message about a voice
|
||||
chat started in the chat. Currently holds no information.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
"""
|
||||
|
||||
def __init__(self, **_kwargs: Any):
|
||||
pass
|
||||
|
||||
|
||||
class VoiceChatEnded(TelegramObject):
|
||||
"""
|
||||
This object represents a service message about a
|
||||
voice chat ended in the chat.
|
||||
|
||||
Objects of this class are comparable in terms of equality.
|
||||
Two objects of this class are considered equal, if their
|
||||
:attr:`duration` are equal.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
|
||||
Args:
|
||||
duration (:obj:`int`): Voice chat duration in seconds.
|
||||
|
||||
Attributes:
|
||||
duration (:obj:`int`): Voice chat duration in seconds.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, duration: int, **_kwargs: Any) -> None:
|
||||
self.duration = int(duration) if duration is not None else None
|
||||
self._id_attrs = (self.duration,)
|
||||
|
||||
|
||||
class VoiceChatParticipantsInvited(TelegramObject):
|
||||
"""
|
||||
This object represents a service message about
|
||||
new members invited to a voice chat.
|
||||
|
||||
Objects of this class are comparable in terms of equality.
|
||||
Two objects of this class are considered equal, if their
|
||||
:attr:`users` are equal.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
|
||||
Args:
|
||||
users (List[:class:`telegram.User`]): New members that
|
||||
were invited to the voice chat.
|
||||
|
||||
Attributes:
|
||||
users (List[:class:`telegram.User`]): New members that
|
||||
were invited to the voice chat.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, users: List[User], **_kwargs: Any) -> None:
|
||||
self.users = users
|
||||
self._id_attrs = (self.users,)
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(tuple(self.users))
|
||||
|
||||
@classmethod
|
||||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: 'Bot'
|
||||
) -> Optional['VoiceChatParticipantsInvited']:
|
||||
data = cls.parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data['users'] = User.de_list(data.get('users', []), bot)
|
||||
return cls(**data)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
data = super().to_dict()
|
||||
|
||||
data["users"] = [u.to_dict() for u in self.users]
|
||||
return data
|
||||
+115
-5
@@ -23,6 +23,7 @@ from pathlib import Path
|
||||
from platform import python_implementation
|
||||
|
||||
import pytest
|
||||
import pytz
|
||||
from flaky import flaky
|
||||
|
||||
from telegram import (
|
||||
@@ -868,13 +869,14 @@ class TestBot:
|
||||
assert bot.token not in resulting_path
|
||||
assert resulting_path == path
|
||||
|
||||
# TODO: Needs improvement. No feasable way to test until bots can add members.
|
||||
# TODO: Needs improvement. No feasible way to test until bots can add members.
|
||||
def test_kick_chat_member(self, monkeypatch, bot):
|
||||
def test(url, data, *args, **kwargs):
|
||||
chat_id = data['chat_id'] == 2
|
||||
user_id = data['user_id'] == 32
|
||||
until_date = data.get('until_date', 1577887200) == 1577887200
|
||||
return chat_id and user_id and until_date
|
||||
revoke_msgs = data.get('revoke_messages', True) is True
|
||||
return chat_id and user_id and until_date and revoke_msgs
|
||||
|
||||
monkeypatch.setattr(bot.request, 'post', test)
|
||||
until = from_timestamp(1577887200)
|
||||
@@ -882,6 +884,7 @@ class TestBot:
|
||||
assert bot.kick_chat_member(2, 32)
|
||||
assert bot.kick_chat_member(2, 32, until_date=until)
|
||||
assert bot.kick_chat_member(2, 32, until_date=1577887200)
|
||||
assert bot.kick_chat_member(2, 32, revoke_messages=True)
|
||||
|
||||
def test_kick_chat_member_default_tz(self, monkeypatch, tz_bot):
|
||||
until = dtm.datetime(2020, 1, 11, 16, 13)
|
||||
@@ -1497,7 +1500,7 @@ class TestBot:
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
def test_promote_chat_member(self, bot, channel_id):
|
||||
def test_promote_chat_member(self, bot, channel_id, monkeypatch):
|
||||
# TODO: Add bot to supergroup so this can be tested properly / give bot perms
|
||||
with pytest.raises(BadRequest, match='Not enough rights'):
|
||||
assert bot.promote_chat_member(
|
||||
@@ -1512,8 +1515,46 @@ class TestBot:
|
||||
can_restrict_members=True,
|
||||
can_pin_messages=True,
|
||||
can_promote_members=True,
|
||||
can_manage_chat=True,
|
||||
can_manage_voice_chats=True,
|
||||
)
|
||||
|
||||
# Test that we pass the correct params to TG
|
||||
def make_assertion(*args, **_):
|
||||
data = args[1]
|
||||
return (
|
||||
data.get('chat_id') == channel_id
|
||||
and data.get('user_id') == 95205500
|
||||
and data.get('is_anonymous') == 1
|
||||
and data.get('can_change_info') == 2
|
||||
and data.get('can_post_messages') == 3
|
||||
and data.get('can_edit_messages') == 4
|
||||
and data.get('can_delete_messages') == 5
|
||||
and data.get('can_invite_users') == 6
|
||||
and data.get('can_restrict_members') == 7
|
||||
and data.get('can_pin_messages') == 8
|
||||
and data.get('can_promote_members') == 9
|
||||
and data.get('can_manage_chat') == 10
|
||||
and data.get('can_manage_voice_chats') == 11
|
||||
)
|
||||
|
||||
monkeypatch.setattr(bot, '_post', make_assertion)
|
||||
assert bot.promote_chat_member(
|
||||
channel_id,
|
||||
95205500,
|
||||
is_anonymous=1,
|
||||
can_change_info=2,
|
||||
can_post_messages=3,
|
||||
can_edit_messages=4,
|
||||
can_delete_messages=5,
|
||||
can_invite_users=6,
|
||||
can_restrict_members=7,
|
||||
can_pin_messages=8,
|
||||
can_promote_members=9,
|
||||
can_manage_chat=10,
|
||||
can_manage_voice_chats=11,
|
||||
)
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
def test_export_chat_invite_link(self, bot, channel_id):
|
||||
@@ -1522,6 +1563,72 @@ class TestBot:
|
||||
assert isinstance(invite_link, str)
|
||||
assert invite_link != ''
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
@pytest.mark.parametrize('datetime', argvalues=[True, False], ids=['datetime', 'integer'])
|
||||
def test_advanced_chat_invite_links(self, bot, channel_id, datetime):
|
||||
# we are testing this all in one function in order to save api calls
|
||||
timestamp = dtm.datetime.utcnow()
|
||||
add_seconds = dtm.timedelta(0, 70)
|
||||
time_in_future = timestamp + add_seconds
|
||||
expire_time = time_in_future if datetime else to_timestamp(time_in_future)
|
||||
aware_time_in_future = pytz.UTC.localize(time_in_future)
|
||||
|
||||
invite_link = bot.create_chat_invite_link(
|
||||
channel_id, expire_date=expire_time, member_limit=10
|
||||
)
|
||||
assert invite_link.invite_link != ''
|
||||
assert not invite_link.invite_link.endswith('...')
|
||||
assert pytest.approx(invite_link.expire_date == aware_time_in_future)
|
||||
assert invite_link.member_limit == 10
|
||||
|
||||
add_seconds = dtm.timedelta(0, 80)
|
||||
time_in_future = timestamp + add_seconds
|
||||
expire_time = time_in_future if datetime else to_timestamp(time_in_future)
|
||||
aware_time_in_future = pytz.UTC.localize(time_in_future)
|
||||
|
||||
edited_invite_link = bot.edit_chat_invite_link(
|
||||
channel_id, invite_link.invite_link, expire_date=expire_time, member_limit=20
|
||||
)
|
||||
assert edited_invite_link.invite_link == invite_link.invite_link
|
||||
assert pytest.approx(edited_invite_link.expire_date == aware_time_in_future)
|
||||
assert edited_invite_link.member_limit == 20
|
||||
|
||||
revoked_invite_link = bot.revoke_chat_invite_link(channel_id, invite_link.invite_link)
|
||||
assert revoked_invite_link.invite_link == invite_link.invite_link
|
||||
assert revoked_invite_link.is_revoked is True
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
def test_advanced_chat_invite_links_default_tzinfo(self, tz_bot, channel_id):
|
||||
# we are testing this all in one function in order to save api calls
|
||||
add_seconds = dtm.timedelta(0, 70)
|
||||
aware_expire_date = dtm.datetime.now(tz=tz_bot.defaults.tzinfo) + add_seconds
|
||||
time_in_future = aware_expire_date.replace(tzinfo=None)
|
||||
|
||||
invite_link = tz_bot.create_chat_invite_link(
|
||||
channel_id, expire_date=time_in_future, member_limit=10
|
||||
)
|
||||
assert invite_link.invite_link != ''
|
||||
assert not invite_link.invite_link.endswith('...')
|
||||
assert pytest.approx(invite_link.expire_date == aware_expire_date)
|
||||
assert invite_link.member_limit == 10
|
||||
|
||||
add_seconds = dtm.timedelta(0, 80)
|
||||
aware_expire_date += add_seconds
|
||||
time_in_future = aware_expire_date.replace(tzinfo=None)
|
||||
|
||||
edited_invite_link = tz_bot.edit_chat_invite_link(
|
||||
channel_id, invite_link.invite_link, expire_date=time_in_future, member_limit=20
|
||||
)
|
||||
assert edited_invite_link.invite_link == invite_link.invite_link
|
||||
assert pytest.approx(edited_invite_link.expire_date == aware_expire_date)
|
||||
assert edited_invite_link.member_limit == 20
|
||||
|
||||
revoked_invite_link = tz_bot.revoke_chat_invite_link(channel_id, invite_link.invite_link)
|
||||
assert revoked_invite_link.invite_link == invite_link.invite_link
|
||||
assert revoked_invite_link.is_revoked is True
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
def test_set_chat_photo(self, bot, channel_id):
|
||||
@@ -1565,7 +1672,7 @@ class TestBot:
|
||||
|
||||
# TODO: Add bot to group to test there too
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
@pytest.mark.timeout(20)
|
||||
def test_pin_and_unpin_message(self, bot, super_group_id):
|
||||
message1 = bot.send_message(super_group_id, text="test_pin_message_1")
|
||||
message2 = bot.send_message(super_group_id, text="test_pin_message_2")
|
||||
@@ -1574,13 +1681,16 @@ class TestBot:
|
||||
assert bot.pin_chat_message(
|
||||
chat_id=super_group_id, message_id=message1.message_id, disable_notification=True
|
||||
)
|
||||
time.sleep(1)
|
||||
|
||||
bot.pin_chat_message(
|
||||
chat_id=super_group_id, message_id=message2.message_id, disable_notification=True
|
||||
)
|
||||
time.sleep(1)
|
||||
bot.pin_chat_message(
|
||||
chat_id=super_group_id, message_id=message3.message_id, disable_notification=True
|
||||
)
|
||||
time.sleep(1)
|
||||
|
||||
chat = bot.get_chat(super_group_id)
|
||||
assert chat.pinned_message == message3
|
||||
@@ -1834,7 +1944,7 @@ class TestBot:
|
||||
reply_to_message = default_bot.send_message(chat_id, 'test')
|
||||
reply_to_message.delete()
|
||||
if not default_bot.defaults.allow_sending_without_reply:
|
||||
with pytest.raises(BadRequest, match='Reply message not found'):
|
||||
with pytest.raises(BadRequest, match='not found'):
|
||||
default_bot.copy_message(
|
||||
chat_id,
|
||||
from_chat_id=chat_id,
|
||||
|
||||
@@ -37,6 +37,7 @@ def chat(bot):
|
||||
can_set_sticker_set=TestChat.can_set_sticker_set,
|
||||
permissions=TestChat.permissions,
|
||||
slow_mode_delay=TestChat.slow_mode_delay,
|
||||
message_auto_delete_time=TestChat.message_auto_delete_time,
|
||||
bio=TestChat.bio,
|
||||
linked_chat_id=TestChat.linked_chat_id,
|
||||
location=TestChat.location,
|
||||
@@ -57,6 +58,7 @@ class TestChat:
|
||||
can_invite_users=True,
|
||||
)
|
||||
slow_mode_delay = 30
|
||||
message_auto_delete_time = 42
|
||||
bio = "I'm a Barbie Girl in a Barbie World"
|
||||
linked_chat_id = 11880
|
||||
location = ChatLocation(Location(123, 456), 'Barbie World')
|
||||
@@ -72,6 +74,7 @@ class TestChat:
|
||||
'can_set_sticker_set': self.can_set_sticker_set,
|
||||
'permissions': self.permissions.to_dict(),
|
||||
'slow_mode_delay': self.slow_mode_delay,
|
||||
'message_auto_delete_time': self.message_auto_delete_time,
|
||||
'bio': self.bio,
|
||||
'linked_chat_id': self.linked_chat_id,
|
||||
'location': self.location.to_dict(),
|
||||
@@ -87,6 +90,7 @@ class TestChat:
|
||||
assert chat.can_set_sticker_set == self.can_set_sticker_set
|
||||
assert chat.permissions == self.permissions
|
||||
assert chat.slow_mode_delay == self.slow_mode_delay
|
||||
assert chat.message_auto_delete_time == self.message_auto_delete_time
|
||||
assert chat.bio == self.bio
|
||||
assert chat.linked_chat_id == self.linked_chat_id
|
||||
assert chat.location.location == self.location.location
|
||||
@@ -103,6 +107,7 @@ class TestChat:
|
||||
assert chat_dict['all_members_are_administrators'] == chat.all_members_are_administrators
|
||||
assert chat_dict['permissions'] == chat.permissions.to_dict()
|
||||
assert chat_dict['slow_mode_delay'] == chat.slow_mode_delay
|
||||
assert chat_dict['message_auto_delete_time'] == chat.message_auto_delete_time
|
||||
assert chat_dict['bio'] == chat.bio
|
||||
assert chat_dict['linked_chat_id'] == chat.linked_chat_id
|
||||
assert chat_dict['location'] == chat.location.to_dict()
|
||||
@@ -556,6 +561,62 @@ class TestChat:
|
||||
monkeypatch.setattr(chat.bot, 'copy_message', make_assertion)
|
||||
assert chat.copy_message(chat_id='test_copy', message_id=42)
|
||||
|
||||
def test_export_invite_link(self, monkeypatch, chat):
|
||||
def make_assertion(*_, **kwargs):
|
||||
return kwargs['chat_id'] == chat.id
|
||||
|
||||
assert check_shortcut_signature(
|
||||
Chat.export_invite_link, Bot.export_chat_invite_link, ['chat_id'], []
|
||||
)
|
||||
assert check_shortcut_call(chat.export_invite_link, chat.bot, 'export_chat_invite_link')
|
||||
assert check_defaults_handling(chat.export_invite_link, chat.bot)
|
||||
|
||||
monkeypatch.setattr(chat.bot, 'export_chat_invite_link', make_assertion)
|
||||
assert chat.export_invite_link()
|
||||
|
||||
def test_create_invite_link(self, monkeypatch, chat):
|
||||
def make_assertion(*_, **kwargs):
|
||||
return kwargs['chat_id'] == chat.id
|
||||
|
||||
assert check_shortcut_signature(
|
||||
Chat.create_invite_link, Bot.create_chat_invite_link, ['chat_id'], []
|
||||
)
|
||||
assert check_shortcut_call(chat.create_invite_link, chat.bot, 'create_chat_invite_link')
|
||||
assert check_defaults_handling(chat.create_invite_link, chat.bot)
|
||||
|
||||
monkeypatch.setattr(chat.bot, 'create_chat_invite_link', make_assertion)
|
||||
assert chat.create_invite_link()
|
||||
|
||||
def test_edit_invite_link(self, monkeypatch, chat):
|
||||
link = "ThisIsALink"
|
||||
|
||||
def make_assertion(*_, **kwargs):
|
||||
return kwargs['chat_id'] == chat.id and kwargs['invite_link'] == link
|
||||
|
||||
assert check_shortcut_signature(
|
||||
Chat.edit_invite_link, Bot.edit_chat_invite_link, ['chat_id'], []
|
||||
)
|
||||
assert check_shortcut_call(chat.edit_invite_link, chat.bot, 'edit_chat_invite_link')
|
||||
assert check_defaults_handling(chat.edit_invite_link, chat.bot)
|
||||
|
||||
monkeypatch.setattr(chat.bot, 'edit_chat_invite_link', make_assertion)
|
||||
assert chat.edit_invite_link(invite_link=link)
|
||||
|
||||
def test_revoke_invite_link(self, monkeypatch, chat):
|
||||
link = "ThisIsALink"
|
||||
|
||||
def make_assertion(*_, **kwargs):
|
||||
return kwargs['chat_id'] == chat.id and kwargs['invite_link'] == link
|
||||
|
||||
assert check_shortcut_signature(
|
||||
Chat.revoke_invite_link, Bot.revoke_chat_invite_link, ['chat_id'], []
|
||||
)
|
||||
assert check_shortcut_call(chat.revoke_invite_link, chat.bot, 'revoke_chat_invite_link')
|
||||
assert check_defaults_handling(chat.revoke_invite_link, chat.bot)
|
||||
|
||||
monkeypatch.setattr(chat.bot, 'revoke_chat_invite_link', make_assertion)
|
||||
assert chat.revoke_invite_link(invite_link=link)
|
||||
|
||||
def test_equality(self):
|
||||
a = Chat(self.id_, self.title, self.type_)
|
||||
b = Chat(self.id_, self.title, self.type_)
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2021
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
import datetime
|
||||
|
||||
import pytest
|
||||
|
||||
from telegram import User, ChatInviteLink
|
||||
from telegram.utils.helpers import to_timestamp
|
||||
|
||||
|
||||
@pytest.fixture(scope='class')
|
||||
def creator():
|
||||
return User(1, 'First name', False)
|
||||
|
||||
|
||||
@pytest.fixture(scope='class')
|
||||
def invite_link(creator):
|
||||
return ChatInviteLink(
|
||||
TestChatInviteLink.link,
|
||||
creator,
|
||||
TestChatInviteLink.primary,
|
||||
TestChatInviteLink.revoked,
|
||||
expire_date=TestChatInviteLink.expire_date,
|
||||
member_limit=TestChatInviteLink.member_limit,
|
||||
)
|
||||
|
||||
|
||||
class TestChatInviteLink:
|
||||
|
||||
link = "thisialink"
|
||||
primary = True
|
||||
revoked = False
|
||||
expire_date = datetime.datetime.utcnow()
|
||||
member_limit = 42
|
||||
|
||||
def test_de_json_required_args(self, bot, creator):
|
||||
json_dict = {
|
||||
'invite_link': self.link,
|
||||
'creator': creator.to_dict(),
|
||||
'is_primary': self.primary,
|
||||
'is_revoked': self.revoked,
|
||||
}
|
||||
|
||||
invite_link = ChatInviteLink.de_json(json_dict, bot)
|
||||
|
||||
assert invite_link.invite_link == self.link
|
||||
assert invite_link.creator == creator
|
||||
assert invite_link.is_primary == self.primary
|
||||
assert invite_link.is_revoked == self.revoked
|
||||
|
||||
def test_de_json_all_args(self, bot, creator):
|
||||
json_dict = {
|
||||
'invite_link': self.link,
|
||||
'creator': creator.to_dict(),
|
||||
'is_primary': self.primary,
|
||||
'is_revoked': self.revoked,
|
||||
'expire_date': to_timestamp(self.expire_date),
|
||||
'member_limit': self.member_limit,
|
||||
}
|
||||
|
||||
invite_link = ChatInviteLink.de_json(json_dict, bot)
|
||||
|
||||
assert invite_link.invite_link == self.link
|
||||
assert invite_link.creator == creator
|
||||
assert invite_link.is_primary == self.primary
|
||||
assert invite_link.is_revoked == self.revoked
|
||||
assert pytest.approx(invite_link.expire_date == self.expire_date)
|
||||
assert to_timestamp(invite_link.expire_date) == to_timestamp(self.expire_date)
|
||||
assert invite_link.member_limit == self.member_limit
|
||||
|
||||
def test_to_dict(self, invite_link):
|
||||
invite_link_dict = invite_link.to_dict()
|
||||
assert isinstance(invite_link_dict, dict)
|
||||
assert invite_link_dict['creator'] == invite_link.creator.to_dict()
|
||||
assert invite_link_dict['invite_link'] == invite_link.invite_link
|
||||
assert invite_link_dict['is_primary'] == self.primary
|
||||
assert invite_link_dict['is_revoked'] == self.revoked
|
||||
assert invite_link_dict['expire_date'] == to_timestamp(self.expire_date)
|
||||
assert invite_link_dict['member_limit'] == self.member_limit
|
||||
|
||||
def test_equality(self):
|
||||
a = ChatInviteLink("link", User(1, '', False), True, True)
|
||||
b = ChatInviteLink("link", User(1, '', False), True, True)
|
||||
d = ChatInviteLink("link", User(2, '', False), False, True)
|
||||
d2 = ChatInviteLink("notalink", User(1, '', False), False, True)
|
||||
d3 = ChatInviteLink("notalink", User(1, '', False), True, True)
|
||||
e = User(1, '', False)
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
assert a is not b
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
assert a != d2
|
||||
assert hash(a) != hash(d2)
|
||||
|
||||
assert d2 != d3
|
||||
assert hash(d2) != hash(d3)
|
||||
|
||||
assert a != e
|
||||
assert hash(a) != hash(e)
|
||||
@@ -69,6 +69,8 @@ class TestChatMember:
|
||||
'can_send_polls': False,
|
||||
'can_send_other_messages': True,
|
||||
'can_add_web_page_previews': False,
|
||||
'can_manage_chat': True,
|
||||
'can_manage_voice_chats': True,
|
||||
}
|
||||
|
||||
chat_member = ChatMember.de_json(json_dict, bot)
|
||||
@@ -91,6 +93,8 @@ class TestChatMember:
|
||||
assert chat_member.can_send_polls is False
|
||||
assert chat_member.can_send_other_messages is True
|
||||
assert chat_member.can_add_web_page_previews is False
|
||||
assert chat_member.can_manage_chat is True
|
||||
assert chat_member.can_manage_voice_chats is True
|
||||
|
||||
def test_to_dict(self, chat_member):
|
||||
chat_member_dict = chat_member.to_dict()
|
||||
|
||||
@@ -0,0 +1,221 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2021
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
import time
|
||||
from queue import Queue
|
||||
|
||||
import pytest
|
||||
|
||||
from telegram import (
|
||||
Update,
|
||||
Bot,
|
||||
Message,
|
||||
User,
|
||||
Chat,
|
||||
CallbackQuery,
|
||||
ChosenInlineResult,
|
||||
ShippingQuery,
|
||||
PreCheckoutQuery,
|
||||
ChatMemberUpdated,
|
||||
ChatMember,
|
||||
)
|
||||
from telegram.ext import CallbackContext, JobQueue, ChatMemberHandler
|
||||
from telegram.utils.helpers import from_timestamp
|
||||
|
||||
message = Message(1, None, Chat(1, ''), from_user=User(1, '', False), text='Text')
|
||||
|
||||
params = [
|
||||
{'message': message},
|
||||
{'edited_message': message},
|
||||
{'callback_query': CallbackQuery(1, User(1, '', False), 'chat', message=message)},
|
||||
{'channel_post': message},
|
||||
{'edited_channel_post': message},
|
||||
{'chosen_inline_result': ChosenInlineResult('id', User(1, '', False), '')},
|
||||
{'shipping_query': ShippingQuery('id', User(1, '', False), '', None)},
|
||||
{'pre_checkout_query': PreCheckoutQuery('id', User(1, '', False), '', 0, '')},
|
||||
{'callback_query': CallbackQuery(1, User(1, '', False), 'chat')},
|
||||
]
|
||||
|
||||
ids = (
|
||||
'message',
|
||||
'edited_message',
|
||||
'callback_query',
|
||||
'channel_post',
|
||||
'edited_channel_post',
|
||||
'chosen_inline_result',
|
||||
'shipping_query',
|
||||
'pre_checkout_query',
|
||||
'callback_query_without_message',
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope='class', params=params, ids=ids)
|
||||
def false_update(request):
|
||||
return Update(update_id=2, **request.param)
|
||||
|
||||
|
||||
@pytest.fixture(scope='class')
|
||||
def chat_member_updated():
|
||||
return ChatMemberUpdated(
|
||||
Chat(1, 'chat'),
|
||||
User(1, '', False),
|
||||
from_timestamp(int(time.time())),
|
||||
ChatMember(User(1, '', False), ChatMember.CREATOR),
|
||||
ChatMember(User(1, '', False), ChatMember.CREATOR),
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def chat_member(bot, chat_member_updated):
|
||||
return Update(0, my_chat_member=chat_member_updated)
|
||||
|
||||
|
||||
class TestChatMemberHandler:
|
||||
test_flag = False
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def reset(self):
|
||||
self.test_flag = False
|
||||
|
||||
def callback_basic(self, bot, update):
|
||||
test_bot = isinstance(bot, Bot)
|
||||
test_update = isinstance(update, Update)
|
||||
self.test_flag = test_bot and test_update
|
||||
|
||||
def callback_data_1(self, bot, update, user_data=None, chat_data=None):
|
||||
self.test_flag = (user_data is not None) or (chat_data is not None)
|
||||
|
||||
def callback_data_2(self, bot, update, user_data=None, chat_data=None):
|
||||
self.test_flag = (user_data is not None) and (chat_data is not None)
|
||||
|
||||
def callback_queue_1(self, bot, update, job_queue=None, update_queue=None):
|
||||
self.test_flag = (job_queue is not None) or (update_queue is not None)
|
||||
|
||||
def callback_queue_2(self, bot, update, job_queue=None, update_queue=None):
|
||||
self.test_flag = (job_queue is not None) and (update_queue is not None)
|
||||
|
||||
def callback_context(self, update, context):
|
||||
self.test_flag = (
|
||||
isinstance(context, CallbackContext)
|
||||
and isinstance(context.bot, Bot)
|
||||
and isinstance(update, Update)
|
||||
and isinstance(context.update_queue, Queue)
|
||||
and isinstance(context.job_queue, JobQueue)
|
||||
and isinstance(context.user_data, dict)
|
||||
and isinstance(context.chat_data, dict)
|
||||
and isinstance(context.bot_data, dict)
|
||||
and isinstance(update.chat_member or update.my_chat_member, ChatMemberUpdated)
|
||||
)
|
||||
|
||||
def test_basic(self, dp, chat_member):
|
||||
handler = ChatMemberHandler(self.callback_basic)
|
||||
dp.add_handler(handler)
|
||||
|
||||
assert handler.check_update(chat_member)
|
||||
|
||||
dp.process_update(chat_member)
|
||||
assert self.test_flag
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
argnames=['allowed_types', 'expected'],
|
||||
argvalues=[
|
||||
(ChatMemberHandler.MY_CHAT_MEMBER, (True, False)),
|
||||
(ChatMemberHandler.CHAT_MEMBER, (False, True)),
|
||||
(ChatMemberHandler.ANY_CHAT_MEMBER, (True, True)),
|
||||
],
|
||||
ids=['MY_CHAT_MEMBER', 'CHAT_MEMBER', 'ANY_CHAT_MEMBER'],
|
||||
)
|
||||
def test_chat_member_types(
|
||||
self, dp, chat_member_updated, chat_member, expected, allowed_types
|
||||
):
|
||||
result_1, result_2 = expected
|
||||
|
||||
handler = ChatMemberHandler(self.callback_basic, chat_member_types=allowed_types)
|
||||
dp.add_handler(handler)
|
||||
|
||||
assert handler.check_update(chat_member) == result_1
|
||||
dp.process_update(chat_member)
|
||||
assert self.test_flag == result_1
|
||||
|
||||
self.test_flag = False
|
||||
chat_member.my_chat_member = None
|
||||
chat_member.chat_member = chat_member_updated
|
||||
|
||||
assert handler.check_update(chat_member) == result_2
|
||||
dp.process_update(chat_member)
|
||||
assert self.test_flag == result_2
|
||||
|
||||
def test_pass_user_or_chat_data(self, dp, chat_member):
|
||||
handler = ChatMemberHandler(self.callback_data_1, pass_user_data=True)
|
||||
dp.add_handler(handler)
|
||||
|
||||
dp.process_update(chat_member)
|
||||
assert self.test_flag
|
||||
|
||||
dp.remove_handler(handler)
|
||||
handler = ChatMemberHandler(self.callback_data_1, pass_chat_data=True)
|
||||
dp.add_handler(handler)
|
||||
|
||||
self.test_flag = False
|
||||
dp.process_update(chat_member)
|
||||
assert self.test_flag
|
||||
|
||||
dp.remove_handler(handler)
|
||||
handler = ChatMemberHandler(self.callback_data_2, pass_chat_data=True, pass_user_data=True)
|
||||
dp.add_handler(handler)
|
||||
|
||||
self.test_flag = False
|
||||
dp.process_update(chat_member)
|
||||
assert self.test_flag
|
||||
|
||||
def test_pass_job_or_update_queue(self, dp, chat_member):
|
||||
handler = ChatMemberHandler(self.callback_queue_1, pass_job_queue=True)
|
||||
dp.add_handler(handler)
|
||||
|
||||
dp.process_update(chat_member)
|
||||
assert self.test_flag
|
||||
|
||||
dp.remove_handler(handler)
|
||||
handler = ChatMemberHandler(self.callback_queue_1, pass_update_queue=True)
|
||||
dp.add_handler(handler)
|
||||
|
||||
self.test_flag = False
|
||||
dp.process_update(chat_member)
|
||||
assert self.test_flag
|
||||
|
||||
dp.remove_handler(handler)
|
||||
handler = ChatMemberHandler(
|
||||
self.callback_queue_2, pass_job_queue=True, pass_update_queue=True
|
||||
)
|
||||
dp.add_handler(handler)
|
||||
|
||||
self.test_flag = False
|
||||
dp.process_update(chat_member)
|
||||
assert self.test_flag
|
||||
|
||||
def test_other_update_types(self, false_update):
|
||||
handler = ChatMemberHandler(self.callback_basic)
|
||||
assert not handler.check_update(false_update)
|
||||
assert not handler.check_update(True)
|
||||
|
||||
def test_context(self, cdp, chat_member):
|
||||
handler = ChatMemberHandler(self.callback_context)
|
||||
cdp.add_handler(handler)
|
||||
|
||||
cdp.process_update(chat_member)
|
||||
assert self.test_flag
|
||||
@@ -0,0 +1,177 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2021
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
import datetime
|
||||
|
||||
import pytest
|
||||
import pytz
|
||||
|
||||
from telegram import User, ChatMember, Chat, ChatMemberUpdated, ChatInviteLink
|
||||
from telegram.utils.helpers import to_timestamp
|
||||
|
||||
|
||||
@pytest.fixture(scope='class')
|
||||
def user():
|
||||
return User(1, 'First name', False)
|
||||
|
||||
|
||||
@pytest.fixture(scope='class')
|
||||
def chat():
|
||||
return Chat(1, Chat.SUPERGROUP, 'Chat')
|
||||
|
||||
|
||||
@pytest.fixture(scope='class')
|
||||
def old_chat_member(user):
|
||||
return ChatMember(user, TestChatMemberUpdated.old_status)
|
||||
|
||||
|
||||
@pytest.fixture(scope='class')
|
||||
def new_chat_member(user):
|
||||
return ChatMember(user, TestChatMemberUpdated.new_status)
|
||||
|
||||
|
||||
@pytest.fixture(scope='class')
|
||||
def time():
|
||||
return datetime.datetime.now(tz=pytz.utc)
|
||||
|
||||
|
||||
@pytest.fixture(scope='class')
|
||||
def invite_link(user):
|
||||
return ChatInviteLink('link', user, True, True)
|
||||
|
||||
|
||||
@pytest.fixture(scope='class')
|
||||
def chat_member_updated(user, chat, old_chat_member, new_chat_member, invite_link, time):
|
||||
return ChatMemberUpdated(chat, user, time, old_chat_member, new_chat_member, invite_link)
|
||||
|
||||
|
||||
class TestChatMemberUpdated:
|
||||
old_status = ChatMember.MEMBER
|
||||
new_status = ChatMember.ADMINISTRATOR
|
||||
|
||||
def test_de_json_required_args(self, bot, user, chat, old_chat_member, new_chat_member, time):
|
||||
json_dict = {
|
||||
'chat': chat.to_dict(),
|
||||
'from': user.to_dict(),
|
||||
'date': to_timestamp(time),
|
||||
'old_chat_member': old_chat_member.to_dict(),
|
||||
'new_chat_member': new_chat_member.to_dict(),
|
||||
}
|
||||
|
||||
chat_member_updated = ChatMemberUpdated.de_json(json_dict, bot)
|
||||
|
||||
assert chat_member_updated.chat == chat
|
||||
assert chat_member_updated.from_user == user
|
||||
assert pytest.approx(chat_member_updated.date == time)
|
||||
assert to_timestamp(chat_member_updated.date) == to_timestamp(time)
|
||||
assert chat_member_updated.old_chat_member == old_chat_member
|
||||
assert chat_member_updated.new_chat_member == new_chat_member
|
||||
assert chat_member_updated.invite_link is None
|
||||
|
||||
def test_de_json_all_args(
|
||||
self, bot, user, time, invite_link, chat, old_chat_member, new_chat_member
|
||||
):
|
||||
json_dict = {
|
||||
'chat': chat.to_dict(),
|
||||
'from': user.to_dict(),
|
||||
'date': to_timestamp(time),
|
||||
'old_chat_member': old_chat_member.to_dict(),
|
||||
'new_chat_member': new_chat_member.to_dict(),
|
||||
'invite_link': invite_link.to_dict(),
|
||||
}
|
||||
|
||||
chat_member_updated = ChatMemberUpdated.de_json(json_dict, bot)
|
||||
|
||||
assert chat_member_updated.chat == chat
|
||||
assert chat_member_updated.from_user == user
|
||||
assert pytest.approx(chat_member_updated.date == time)
|
||||
assert to_timestamp(chat_member_updated.date) == to_timestamp(time)
|
||||
assert chat_member_updated.old_chat_member == old_chat_member
|
||||
assert chat_member_updated.new_chat_member == new_chat_member
|
||||
assert chat_member_updated.invite_link == invite_link
|
||||
|
||||
def test_to_dict(self, chat_member_updated):
|
||||
chat_member_updated_dict = chat_member_updated.to_dict()
|
||||
assert isinstance(chat_member_updated_dict, dict)
|
||||
assert chat_member_updated_dict['chat'] == chat_member_updated.chat.to_dict()
|
||||
assert chat_member_updated_dict['from'] == chat_member_updated.from_user.to_dict()
|
||||
assert chat_member_updated_dict['date'] == to_timestamp(chat_member_updated.date)
|
||||
assert (
|
||||
chat_member_updated_dict['old_chat_member']
|
||||
== chat_member_updated.old_chat_member.to_dict()
|
||||
)
|
||||
assert (
|
||||
chat_member_updated_dict['new_chat_member']
|
||||
== chat_member_updated.new_chat_member.to_dict()
|
||||
)
|
||||
assert chat_member_updated_dict['invite_link'] == chat_member_updated.invite_link.to_dict()
|
||||
|
||||
def test_equality(self, time, old_chat_member, new_chat_member, invite_link):
|
||||
a = ChatMemberUpdated(
|
||||
Chat(1, 'chat'),
|
||||
User(1, '', False),
|
||||
time,
|
||||
old_chat_member,
|
||||
new_chat_member,
|
||||
invite_link,
|
||||
)
|
||||
b = ChatMemberUpdated(
|
||||
Chat(1, 'chat'), User(1, '', False), time, old_chat_member, new_chat_member
|
||||
)
|
||||
# wrong date
|
||||
c = ChatMemberUpdated(
|
||||
Chat(1, 'chat'),
|
||||
User(1, '', False),
|
||||
time + datetime.timedelta(hours=1),
|
||||
old_chat_member,
|
||||
new_chat_member,
|
||||
)
|
||||
# wrong chat & form_user
|
||||
d = ChatMemberUpdated(
|
||||
Chat(42, 'wrong_chat'),
|
||||
User(42, 'wrong_user', False),
|
||||
time,
|
||||
old_chat_member,
|
||||
new_chat_member,
|
||||
)
|
||||
# wrong old_chat_member
|
||||
e = ChatMemberUpdated(
|
||||
Chat(1, 'chat'),
|
||||
User(1, '', False),
|
||||
time,
|
||||
ChatMember(User(1, '', False), ChatMember.CREATOR),
|
||||
new_chat_member,
|
||||
)
|
||||
# wrong new_chat_member
|
||||
f = ChatMemberUpdated(
|
||||
Chat(1, 'chat'),
|
||||
User(1, '', False),
|
||||
time,
|
||||
old_chat_member,
|
||||
ChatMember(User(1, '', False), ChatMember.CREATOR),
|
||||
)
|
||||
# wrong type
|
||||
g = ChatMember(User(1, '', False), ChatMember.CREATOR)
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
assert a is not b
|
||||
|
||||
for other in [c, d, e, f, g]:
|
||||
assert a != other
|
||||
assert hash(a) != hash(other)
|
||||
+28
-1
@@ -44,7 +44,7 @@ def update():
|
||||
|
||||
@pytest.fixture(scope='function', params=MessageEntity.ALL_TYPES)
|
||||
def message_entity(request):
|
||||
return MessageEntity(request.param, 0, 0, url='', user='')
|
||||
return MessageEntity(request.param, 0, 0, url='', user=User(1, 'first_name', False))
|
||||
|
||||
|
||||
@pytest.fixture(
|
||||
@@ -828,6 +828,11 @@ class TestFilters:
|
||||
assert Filters.status_update.chat_created(update)
|
||||
update.message.channel_chat_created = False
|
||||
|
||||
update.message.message_auto_delete_timer_changed = True
|
||||
assert Filters.status_update(update)
|
||||
assert Filters.status_update.message_auto_delete_timer_changed(update)
|
||||
update.message.message_auto_delete_timer_changed = False
|
||||
|
||||
update.message.migrate_to_chat_id = 100
|
||||
assert Filters.status_update(update)
|
||||
assert Filters.status_update.migrate(update)
|
||||
@@ -853,6 +858,21 @@ class TestFilters:
|
||||
assert Filters.status_update.proximity_alert_triggered(update)
|
||||
update.message.proximity_alert_triggered = None
|
||||
|
||||
update.message.voice_chat_started = 'hello'
|
||||
assert Filters.status_update(update)
|
||||
assert Filters.status_update.voice_chat_started(update)
|
||||
update.message.voice_chat_started = None
|
||||
|
||||
update.message.voice_chat_ended = 'bye'
|
||||
assert Filters.status_update(update)
|
||||
assert Filters.status_update.voice_chat_ended(update)
|
||||
update.message.voice_chat_ended = None
|
||||
|
||||
update.message.voice_chat_participants_invited = 'invited'
|
||||
assert Filters.status_update(update)
|
||||
assert Filters.status_update.voice_chat_participants_invited(update)
|
||||
update.message.voice_chat_participants_invited = None
|
||||
|
||||
def test_filters_forwarded(self, update):
|
||||
assert not Filters.forwarded(update)
|
||||
update.message.forward_date = datetime.datetime.utcnow()
|
||||
@@ -1453,6 +1473,13 @@ class TestFilters:
|
||||
assert not Filters.dice.darts(update)
|
||||
assert not Filters.dice.slot_machine([4])(update)
|
||||
|
||||
update.message.dice = Dice(5, '🎳')
|
||||
assert Filters.dice.bowling(update)
|
||||
assert Filters.dice.bowling([4, 5])(update)
|
||||
assert not Filters.dice.dice(update)
|
||||
assert not Filters.dice.darts(update)
|
||||
assert not Filters.dice.bowling([4])(update)
|
||||
|
||||
def test_language_filter_single(self, update):
|
||||
update.message.from_user.language_code = 'en_US'
|
||||
assert (Filters.language('en_US'))(update)
|
||||
|
||||
@@ -48,6 +48,10 @@ from telegram import (
|
||||
Dice,
|
||||
Bot,
|
||||
ChatAction,
|
||||
VoiceChatStarted,
|
||||
VoiceChatEnded,
|
||||
VoiceChatParticipantsInvited,
|
||||
MessageAutoDeleteTimerChanged,
|
||||
)
|
||||
from telegram.ext import Defaults
|
||||
from tests.conftest import check_shortcut_signature, check_shortcut_call, check_defaults_handling
|
||||
@@ -115,6 +119,7 @@ def message(bot):
|
||||
{'group_chat_created': True},
|
||||
{'supergroup_chat_created': True},
|
||||
{'channel_chat_created': True},
|
||||
{'message_auto_delete_timer_changed': MessageAutoDeleteTimerChanged(42)},
|
||||
{'migrate_to_chat_id': -12345},
|
||||
{'migrate_from_chat_id': -54321},
|
||||
{'pinned_message': Message(7, None, None, None)},
|
||||
@@ -166,6 +171,13 @@ def message(bot):
|
||||
User(1, 'John', False), User(2, 'Doe', False), 42
|
||||
)
|
||||
},
|
||||
{'voice_chat_started': VoiceChatStarted()},
|
||||
{'voice_chat_ended': VoiceChatEnded(100)},
|
||||
{
|
||||
'voice_chat_participants_invited': VoiceChatParticipantsInvited(
|
||||
[User(1, 'Rem', False), User(2, 'Emilia', False)]
|
||||
)
|
||||
},
|
||||
{'sender_chat': Chat(-123, 'discussion_channel')},
|
||||
],
|
||||
ids=[
|
||||
@@ -195,6 +207,7 @@ def message(bot):
|
||||
'group_created',
|
||||
'supergroup_created',
|
||||
'channel_created',
|
||||
'message_auto_delete_timer_changed',
|
||||
'migrated_to',
|
||||
'migrated_from',
|
||||
'pinned',
|
||||
@@ -211,6 +224,9 @@ def message(bot):
|
||||
'dice',
|
||||
'via_bot',
|
||||
'proximity_alert_triggered',
|
||||
'voice_chat_started',
|
||||
'voice_chat_ended',
|
||||
'voice_chat_participants_invited',
|
||||
'sender_chat',
|
||||
],
|
||||
)
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env python
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2021
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
from telegram import MessageAutoDeleteTimerChanged, VoiceChatEnded
|
||||
|
||||
|
||||
class TestMessageAutoDeleteTimerChanged:
|
||||
message_auto_delete_time = 100
|
||||
|
||||
def test_de_json(self):
|
||||
json_dict = {'message_auto_delete_time': self.message_auto_delete_time}
|
||||
madtc = MessageAutoDeleteTimerChanged.de_json(json_dict, None)
|
||||
|
||||
assert madtc.message_auto_delete_time == self.message_auto_delete_time
|
||||
|
||||
def test_to_dict(self):
|
||||
madtc = MessageAutoDeleteTimerChanged(self.message_auto_delete_time)
|
||||
madtc_dict = madtc.to_dict()
|
||||
|
||||
assert isinstance(madtc_dict, dict)
|
||||
assert madtc_dict["message_auto_delete_time"] == self.message_auto_delete_time
|
||||
|
||||
def test_equality(self):
|
||||
a = MessageAutoDeleteTimerChanged(100)
|
||||
b = MessageAutoDeleteTimerChanged(100)
|
||||
c = MessageAutoDeleteTimerChanged(50)
|
||||
d = VoiceChatEnded(25)
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
|
||||
assert a != c
|
||||
assert hash(a) != hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
+7
-12
@@ -63,7 +63,7 @@ class TestPhoto:
|
||||
width = 800
|
||||
height = 800
|
||||
caption = '<b>PhotoTest</b> - *Caption*'
|
||||
photo_file_url = 'https://python-telegram-bot.org/static/testfiles/telegram_new.jpg'
|
||||
photo_file_url = 'https://python-telegram-bot.org/static/testfiles/telegram.jpg'
|
||||
file_size = 29176
|
||||
|
||||
def test_creation(self, thumb, photo):
|
||||
@@ -81,12 +81,12 @@ class TestPhoto:
|
||||
assert thumb.file_unique_id != ''
|
||||
|
||||
def test_expected_values(self, photo, thumb):
|
||||
# We used to test for file_size as well, but TG apparently at some point apparently changed
|
||||
# the compression method and it's not really our job anyway ...
|
||||
assert photo.width == self.width
|
||||
assert photo.height == self.height
|
||||
assert photo.file_size == self.file_size
|
||||
assert thumb.width == 320
|
||||
assert thumb.height == 320
|
||||
assert thumb.file_size == 9331
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
@@ -191,9 +191,6 @@ class TestPhoto:
|
||||
message = bot.send_photo(
|
||||
chat_id, photo_file, caption=test_string, caption_entities=entities
|
||||
)
|
||||
# message = bot.send_photo(
|
||||
# chat_id, photo_file, caption=test_string, caption_entities=entities
|
||||
# )
|
||||
|
||||
assert message.caption == test_string
|
||||
assert message.caption_entities == entities
|
||||
@@ -305,18 +302,16 @@ class TestPhoto:
|
||||
assert isinstance(message.photo[0].file_unique_id, str)
|
||||
assert message.photo[0].file_id != ''
|
||||
assert message.photo[0].file_unique_id != ''
|
||||
assert message.photo[0].width == thumb.width
|
||||
assert message.photo[0].height == thumb.height
|
||||
assert message.photo[0].file_size == thumb.file_size
|
||||
# We used to test for width, height and file_size, but TG apparently started to treat
|
||||
# sending by URL and sending by upload differently and it's not really our job anyway ...
|
||||
|
||||
assert isinstance(message.photo[1], PhotoSize)
|
||||
assert isinstance(message.photo[1].file_id, str)
|
||||
assert isinstance(message.photo[1].file_unique_id, str)
|
||||
assert message.photo[1].file_id != ''
|
||||
assert message.photo[1].file_unique_id != ''
|
||||
assert message.photo[1].width == photo.width
|
||||
assert message.photo[1].height == photo.height
|
||||
assert message.photo[1].file_size == photo.file_size
|
||||
# We used to test for width, height and file_size, but TG apparently started to treat
|
||||
# sending by URL and sending by upload differently and it's not really our job anyway ...
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
import time
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -31,10 +32,20 @@ from telegram import (
|
||||
PreCheckoutQuery,
|
||||
Poll,
|
||||
PollOption,
|
||||
ChatMemberUpdated,
|
||||
ChatMember,
|
||||
)
|
||||
from telegram.poll import PollAnswer
|
||||
from telegram.utils.helpers import from_timestamp
|
||||
|
||||
message = Message(1, None, Chat(1, ''), from_user=User(1, '', False), text='Text')
|
||||
chat_member_updated = ChatMemberUpdated(
|
||||
Chat(1, 'chat'),
|
||||
User(1, '', False),
|
||||
from_timestamp(int(time.time())),
|
||||
ChatMember(User(1, '', False), ChatMember.CREATOR),
|
||||
ChatMember(User(1, '', False), ChatMember.CREATOR),
|
||||
)
|
||||
|
||||
params = [
|
||||
{'message': message},
|
||||
@@ -49,6 +60,8 @@ params = [
|
||||
{'callback_query': CallbackQuery(1, User(1, '', False), 'chat')},
|
||||
{'poll': Poll('id', '?', [PollOption('.', 1)], False, False, False, Poll.REGULAR, True)},
|
||||
{'poll_answer': PollAnswer("id", User(1, '', False), [1])},
|
||||
{'my_chat_member': chat_member_updated},
|
||||
{'chat_member': chat_member_updated},
|
||||
]
|
||||
|
||||
all_types = (
|
||||
@@ -63,6 +76,8 @@ all_types = (
|
||||
'pre_checkout_query',
|
||||
'poll',
|
||||
'poll_answer',
|
||||
'my_chat_member',
|
||||
'chat_member',
|
||||
)
|
||||
|
||||
ids = all_types + ('callback_query_without_message',)
|
||||
@@ -146,6 +161,8 @@ class TestUpdate:
|
||||
or update.pre_checkout_query is not None
|
||||
or update.poll is not None
|
||||
or update.poll_answer is not None
|
||||
or update.my_chat_member is not None
|
||||
or update.chat_member is not None
|
||||
):
|
||||
assert eff_message.message_id == message.message_id
|
||||
else:
|
||||
|
||||
+95
-35
@@ -72,6 +72,7 @@ class TestUpdater:
|
||||
err_handler_called = Event()
|
||||
cb_handler_called = Event()
|
||||
offset = 0
|
||||
test_flag = False
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def reset(self):
|
||||
@@ -80,6 +81,7 @@ class TestUpdater:
|
||||
self.attempts = 0
|
||||
self.err_handler_called.clear()
|
||||
self.cb_handler_called.clear()
|
||||
self.test_flag = False
|
||||
|
||||
def error_handler(self, bot, update, error):
|
||||
self.received = error.message
|
||||
@@ -247,7 +249,7 @@ class TestUpdater:
|
||||
cert=None,
|
||||
key=None,
|
||||
bootstrap_retries=0,
|
||||
clean=False,
|
||||
drop_pending_updates=False,
|
||||
webhook_url=None,
|
||||
allowed_updates=None,
|
||||
)
|
||||
@@ -274,7 +276,7 @@ class TestUpdater:
|
||||
cert=None,
|
||||
key=None,
|
||||
bootstrap_retries=0,
|
||||
clean=False,
|
||||
drop_pending_updates=False,
|
||||
webhook_url=None,
|
||||
allowed_updates=None,
|
||||
)
|
||||
@@ -307,7 +309,7 @@ class TestUpdater:
|
||||
cert=None,
|
||||
key=None,
|
||||
bootstrap_retries=0,
|
||||
clean=False,
|
||||
drop_pending_updates=False,
|
||||
webhook_url=None,
|
||||
allowed_updates=None,
|
||||
force_event_loop=True,
|
||||
@@ -328,7 +330,7 @@ class TestUpdater:
|
||||
cert='./tests/test_updater.py',
|
||||
key='./tests/test_updater.py',
|
||||
bootstrap_retries=0,
|
||||
clean=False,
|
||||
drop_pending_updates=False,
|
||||
webhook_url=None,
|
||||
allowed_updates=None,
|
||||
)
|
||||
@@ -357,6 +359,42 @@ class TestUpdater:
|
||||
assert q.get(False) == update
|
||||
updater.stop()
|
||||
|
||||
def test_webhook_ssl_just_for_telegram(self, monkeypatch, updater):
|
||||
q = Queue()
|
||||
|
||||
def set_webhook(**kwargs):
|
||||
self.test_flag.append(bool(kwargs.get('certificate')))
|
||||
return True
|
||||
|
||||
orig_wh_server_init = WebhookServer.__init__
|
||||
|
||||
def webhook_server_init(*args):
|
||||
self.test_flag = [args[-1] is None]
|
||||
orig_wh_server_init(*args)
|
||||
|
||||
monkeypatch.setattr(updater.bot, 'set_webhook', set_webhook)
|
||||
monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True)
|
||||
monkeypatch.setattr('telegram.ext.Dispatcher.process_update', lambda _, u: q.put(u))
|
||||
monkeypatch.setattr(
|
||||
'telegram.ext.utils.webhookhandler.WebhookServer.__init__', webhook_server_init
|
||||
)
|
||||
|
||||
ip = '127.0.0.1'
|
||||
port = randrange(1024, 49152) # Select random port
|
||||
updater.start_webhook(ip, port, webhook_url=None, cert='./tests/test_updater.py')
|
||||
sleep(0.2)
|
||||
|
||||
# Now, we send an update to the server via urlopen
|
||||
update = Update(
|
||||
1,
|
||||
message=Message(1, None, Chat(1, ''), from_user=User(1, '', False), text='Webhook 2'),
|
||||
)
|
||||
self._send_webhook_msg(ip, port, update.to_json())
|
||||
sleep(0.2)
|
||||
assert q.get(False) == update
|
||||
updater.stop()
|
||||
assert self.test_flag == [True, True]
|
||||
|
||||
@pytest.mark.parametrize(('error',), argvalues=[(TelegramError(''),)], ids=('TelegramError',))
|
||||
def test_bootstrap_retries_success(self, monkeypatch, updater, error):
|
||||
retries = 2
|
||||
@@ -391,41 +429,63 @@ class TestUpdater:
|
||||
updater._bootstrap(retries, False, 'path', None, bootstrap_interval=0)
|
||||
assert self.attempts == attempts
|
||||
|
||||
def test_bootstrap_clean_updates(self, monkeypatch, updater):
|
||||
clean = True
|
||||
expected_id = 4
|
||||
self.offset = 0
|
||||
@pytest.mark.parametrize('drop_pending_updates', (True, False))
|
||||
def test_bootstrap_clean_updates(self, monkeypatch, updater, drop_pending_updates):
|
||||
# As dropping pending updates is done by passing `drop_pending_updates` to
|
||||
# set_webhook, we just check that we pass the correct value
|
||||
self.test_flag = False
|
||||
|
||||
def get_updates(*args, **kwargs):
|
||||
# we're hitting this func twice
|
||||
# 1. no args, return list of updates
|
||||
# 2. with 1 arg, int => if int == expected_id => test successful
|
||||
def delete_webhook(**kwargs):
|
||||
self.test_flag = kwargs.get('drop_pending_updates') == drop_pending_updates
|
||||
|
||||
# case 2
|
||||
# 2nd call from bootstrap____clean
|
||||
# we should be called with offset = 4
|
||||
# save value passed in self.offset for assert down below
|
||||
if len(args) > 0:
|
||||
self.offset = int(args[0])
|
||||
return []
|
||||
|
||||
class FakeUpdate:
|
||||
def __init__(self, update_id):
|
||||
self.update_id = update_id
|
||||
|
||||
# case 1
|
||||
# return list of obj's
|
||||
|
||||
# build list of fake updates
|
||||
# returns list of 4 objects with
|
||||
# update_id's 0, 1, 2 and 3
|
||||
return [FakeUpdate(i) for i in range(0, expected_id)]
|
||||
|
||||
monkeypatch.setattr(updater.bot, 'get_updates', get_updates)
|
||||
monkeypatch.setattr(updater.bot, 'delete_webhook', delete_webhook)
|
||||
|
||||
updater.running = True
|
||||
updater._bootstrap(1, clean, None, None, bootstrap_interval=0)
|
||||
assert self.offset == expected_id
|
||||
updater._bootstrap(
|
||||
1,
|
||||
drop_pending_updates=drop_pending_updates,
|
||||
webhook_url=None,
|
||||
allowed_updates=None,
|
||||
bootstrap_interval=0,
|
||||
)
|
||||
assert self.test_flag is True
|
||||
|
||||
def test_clean_deprecation_warning_webhook(self, recwarn, updater, monkeypatch):
|
||||
monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True)
|
||||
monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True)
|
||||
# prevent api calls from @info decorator when updater.bot.id is used in thread names
|
||||
monkeypatch.setattr(updater.bot, '_bot', User(id=123, first_name='bot', is_bot=True))
|
||||
monkeypatch.setattr(updater.bot, '_commands', [])
|
||||
|
||||
ip = '127.0.0.1'
|
||||
port = randrange(1024, 49152) # Select random port
|
||||
updater.start_webhook(ip, port, clean=True)
|
||||
updater.stop()
|
||||
assert len(recwarn) == 2
|
||||
assert str(recwarn[0].message).startswith('Old Handler API')
|
||||
assert str(recwarn[1].message).startswith('The argument `clean` of')
|
||||
|
||||
def test_clean_deprecation_warning_polling(self, recwarn, updater, monkeypatch):
|
||||
monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True)
|
||||
monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True)
|
||||
# prevent api calls from @info decorator when updater.bot.id is used in thread names
|
||||
monkeypatch.setattr(updater.bot, '_bot', User(id=123, first_name='bot', is_bot=True))
|
||||
monkeypatch.setattr(updater.bot, '_commands', [])
|
||||
|
||||
updater.start_polling(clean=True)
|
||||
updater.stop()
|
||||
assert len(recwarn) == 2
|
||||
for msg in recwarn:
|
||||
print(msg)
|
||||
assert str(recwarn[0].message).startswith('Old Handler API')
|
||||
assert str(recwarn[1].message).startswith('The argument `clean` of')
|
||||
|
||||
def test_clean_drop_pending_mutually_exclusive(self, updater):
|
||||
with pytest.raises(TypeError, match='`clean` and `drop_pending_updates` are mutually'):
|
||||
updater.start_polling(clean=True, drop_pending_updates=False)
|
||||
|
||||
with pytest.raises(TypeError, match='`clean` and `drop_pending_updates` are mutually'):
|
||||
updater.start_webhook(clean=True, drop_pending_updates=False)
|
||||
|
||||
@flaky(3, 1)
|
||||
def test_webhook_invalid_posts(self, updater):
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2021
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
import pytest
|
||||
|
||||
from telegram import VoiceChatStarted, VoiceChatEnded, VoiceChatParticipantsInvited, User
|
||||
|
||||
|
||||
@pytest.fixture(scope='class')
|
||||
def user1():
|
||||
return User(first_name='Misses Test', id=123, is_bot=False)
|
||||
|
||||
|
||||
@pytest.fixture(scope='class')
|
||||
def user2():
|
||||
return User(first_name='Mister Test', id=124, is_bot=False)
|
||||
|
||||
|
||||
class TestVoiceChatStarted:
|
||||
def test_de_json(self):
|
||||
voice_chat_started = VoiceChatStarted.de_json({}, None)
|
||||
assert isinstance(voice_chat_started, VoiceChatStarted)
|
||||
|
||||
def test_to_dict(self):
|
||||
voice_chat_started = VoiceChatStarted()
|
||||
voice_chat_dict = voice_chat_started.to_dict()
|
||||
assert voice_chat_dict == {}
|
||||
|
||||
|
||||
class TestVoiceChatEnded:
|
||||
duration = 100
|
||||
|
||||
def test_de_json(self):
|
||||
json_dict = {'duration': self.duration}
|
||||
voice_chat_ended = VoiceChatEnded.de_json(json_dict, None)
|
||||
|
||||
assert voice_chat_ended.duration == self.duration
|
||||
|
||||
def test_to_dict(self):
|
||||
voice_chat_ended = VoiceChatEnded(self.duration)
|
||||
voice_chat_dict = voice_chat_ended.to_dict()
|
||||
|
||||
assert isinstance(voice_chat_dict, dict)
|
||||
assert voice_chat_dict["duration"] == self.duration
|
||||
|
||||
def test_equality(self):
|
||||
a = VoiceChatEnded(100)
|
||||
b = VoiceChatEnded(100)
|
||||
c = VoiceChatEnded(50)
|
||||
d = VoiceChatStarted()
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
|
||||
assert a != c
|
||||
assert hash(a) != hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
|
||||
class TestVoiceChatParticipantsInvited:
|
||||
def test_de_json(self, user1, user2, bot):
|
||||
json_data = {"users": [user1.to_dict(), user2.to_dict()]}
|
||||
voice_chat_participants = VoiceChatParticipantsInvited.de_json(json_data, bot)
|
||||
|
||||
assert isinstance(voice_chat_participants.users, list)
|
||||
assert voice_chat_participants.users[0] == user1
|
||||
assert voice_chat_participants.users[1] == user2
|
||||
assert voice_chat_participants.users[0].id == user1.id
|
||||
assert voice_chat_participants.users[1].id == user2.id
|
||||
|
||||
def test_to_dict(self, user1, user2):
|
||||
voice_chat_participants = VoiceChatParticipantsInvited([user1, user2])
|
||||
voice_chat_dict = voice_chat_participants.to_dict()
|
||||
|
||||
assert isinstance(voice_chat_dict, dict)
|
||||
assert voice_chat_dict["users"] == [user1.to_dict(), user2.to_dict()]
|
||||
assert voice_chat_dict["users"][0]["id"] == user1.id
|
||||
assert voice_chat_dict["users"][1]["id"] == user2.id
|
||||
|
||||
def test_equality(self, user1, user2):
|
||||
a = VoiceChatParticipantsInvited([user1])
|
||||
b = VoiceChatParticipantsInvited([user1])
|
||||
c = VoiceChatParticipantsInvited([user1, user2])
|
||||
d = VoiceChatParticipantsInvited([user2])
|
||||
e = VoiceChatStarted()
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
|
||||
assert a != c
|
||||
assert hash(a) != hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
assert a != e
|
||||
assert hash(a) != hash(e)
|
||||
Reference in New Issue
Block a user