mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2026-06-19 15:45:13 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 132a3b203d | |||
| debe86aea2 | |||
| 7a7465e8e2 | |||
| 977c54e693 | |||
| a769861b32 | |||
| 0a00a821cb | |||
| ef2a0527fe |
@@ -9,9 +9,9 @@ body:
|
||||
value: |
|
||||
Hey there, you have a question? We are happy to answer. Please make sure no similar question was opened already.
|
||||
|
||||
To make it easier for us to help you, please read this [article](https://git.io/JURJO).
|
||||
To make it easier for us to help you, please read this [article](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Ask-Right).
|
||||
|
||||
Please mind that there is also a users' [Telegram group](https://t.me/pythontelegrambotgroup) for questions about the library. Questions asked there might be answered quicker than here. Moreover, [GitHub Discussions](https://git.io/JG3rk) offer a slightly better format to discuss usage questions.
|
||||
Please mind that there is also a users' [Telegram group](https://t.me/pythontelegrambotgroup) for questions about the library. Questions asked there might be answered quicker than here. Moreover, [GitHub Discussions](https://github.com/python-telegram-bot/python-telegram-bot/discussions) offer a slightly better format to discuss usage questions.
|
||||
|
||||
- type: textarea
|
||||
id: issue-faced
|
||||
|
||||
@@ -3,9 +3,11 @@ on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- v13.x
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- v13.x
|
||||
|
||||
jobs:
|
||||
pytest:
|
||||
@@ -13,7 +15,7 @@ jobs:
|
||||
runs-on: ${{matrix.os}}
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.6, 3.7, 3.8, 3.9]
|
||||
python-version: [3.7, 3.8, 3.9]
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
fail-fast: False
|
||||
steps:
|
||||
@@ -94,27 +96,3 @@ jobs:
|
||||
env:
|
||||
TEST_OFFICIAL: "true"
|
||||
shell: bash --noprofile --norc {0}
|
||||
test_pre_commit:
|
||||
name: test-pre-commit
|
||||
runs-on: ${{matrix.os}}
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.7]
|
||||
os: [ubuntu-latest]
|
||||
fail-fast: False
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Initialize vendored libs
|
||||
run:
|
||||
git submodule update --init --recursive
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -W ignore -m pip install --upgrade pip
|
||||
python -W ignore -m pip install -r requirements.txt
|
||||
python -W ignore -m pip install -r requirements-dev.txt
|
||||
- name: Run pre-commit tests
|
||||
run: pre-commit run --all-files
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
# Make sure that
|
||||
# * the revs specified here match requirements-dev.txt
|
||||
# * the additional_dependencies here match requirements.txt
|
||||
|
||||
ci:
|
||||
# We currently only need this behavior on the v13.x branch were we have the vendored urllib
|
||||
submodules: true
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 20.8b1
|
||||
rev: 22.3.0
|
||||
hooks:
|
||||
- id: black
|
||||
args:
|
||||
|
||||
+35
-2
@@ -2,6 +2,39 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
Version 13.13
|
||||
=============
|
||||
*Released 2022-06-28*
|
||||
|
||||
This is the technical changelog for version 13.13. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel <https://t.me/pythontelegrambotchannel>`_.
|
||||
|
||||
**Major Changes:**
|
||||
|
||||
- Full Support for API 6.1 (`#3117`_)
|
||||
|
||||
.. _`#3117`: https://github.com/python-telegram-bot/python-telegram-bot/pull/3117
|
||||
|
||||
Version 13.12
|
||||
=============
|
||||
*Released 2022-05-26*
|
||||
|
||||
This is the technical changelog for version 13.12. More elaborate release notes can be found in the news channel `@pythontelegrambotchannel <https://t.me/pythontelegrambotchannel>`_.
|
||||
|
||||
**Breaking changes:**
|
||||
|
||||
- Drop support for python 3.6
|
||||
|
||||
**Major Changes:**
|
||||
|
||||
- Full Support for API 6.0 (`#3027`_)
|
||||
|
||||
**Minor Changes:**
|
||||
|
||||
- Documentation Improvements (`#3029`_)
|
||||
|
||||
.. _`#3027`: https://github.com/python-telegram-bot/python-telegram-bot/pull/3027
|
||||
.. _`#3029`: https://github.com/python-telegram-bot/python-telegram-bot/pull/3029
|
||||
|
||||
Version 13.11
|
||||
=============
|
||||
*Released 2022-02-02*
|
||||
@@ -895,7 +928,7 @@ Expect minor releases soon (mainly complete Bot API 4.4 support)
|
||||
- Error Handler now handles all types of exceptions (`#1485`_)
|
||||
- Return UTC from from_timestamp() (`#1485`_)
|
||||
|
||||
**See the wiki page at https://git.io/fxJuV for a detailed guide on how to migrate from version 11 to version 12.**
|
||||
**See the wiki page at https://github.com/python-telegram-bot/python-telegram-bot/wiki/Transition-guide-to-Version-12.0 for a detailed guide on how to migrate from version 11 to version 12.**
|
||||
|
||||
Context based callbacks (`#1100`_)
|
||||
----------------------------------
|
||||
@@ -1100,7 +1133,7 @@ Non Bot API 4.0 changes:
|
||||
.. _`#1172`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1172
|
||||
.. _`#1179`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1179
|
||||
.. _`#1184`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1184
|
||||
.. _`our telegram passport wiki page`: https://git.io/fAvYd
|
||||
.. _`our telegram passport wiki page`: https://github.com/python-telegram-bot/python-telegram-bot/wiki/Telegram-Passport
|
||||
|
||||
Version 10.1.0
|
||||
==============
|
||||
|
||||
+7
-6
@@ -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.7-blue?logo=telegram
|
||||
.. image:: https://img.shields.io/badge/Bot%20API-6.1-blue?logo=telegram
|
||||
:target: https://core.telegram.org/bots/api-changelog
|
||||
:alt: Supported Bot API versions
|
||||
|
||||
@@ -87,19 +87,20 @@ Table of contents
|
||||
|
||||
- `License`_
|
||||
|
||||
|
||||
============
|
||||
Introduction
|
||||
============
|
||||
|
||||
This library provides a pure Python interface for the
|
||||
`Telegram Bot API <https://core.telegram.org/bots/api>`_.
|
||||
It's compatible with Python versions 3.6.8+. PTB might also work on `PyPy <http://pypy.org/>`_, though there have been a lot of issues before. Hence, PyPy is not officially supported.
|
||||
It's compatible with Python versions 3.7+. PTB might also work on `PyPy <http://pypy.org/>`_, though there have been a lot of issues before. Hence, PyPy is not officially supported.
|
||||
|
||||
In addition to the pure API implementation, this library features a number of high-level classes to
|
||||
make the development of bots easy and straightforward. These classes are contained in the
|
||||
``telegram.ext`` submodule.
|
||||
|
||||
A pure API implementation *without* ``telegram.ext`` is available as the standalone package ``python-telegram-bot-raw``. `See here for details. <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/README_RAW.rst>`_
|
||||
A pure API implementation *without* ``telegram.ext`` is available as the standalone package ``python-telegram-bot-raw``. `See here for details. <https://github.com/python-telegram-bot/python-telegram-bot/blob/v13.x/README_RAW.rst>`_
|
||||
|
||||
----
|
||||
Note
|
||||
@@ -111,7 +112,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.7** are supported.
|
||||
All types and methods of the Telegram Bot API **6.1** are supported.
|
||||
|
||||
==========
|
||||
Installing
|
||||
@@ -171,7 +172,7 @@ look at ``echobot.py``, it is the de facto base for most of the bots out there.
|
||||
the code for these examples are released to the public domain, so you can start by grabbing the
|
||||
code and building on top of it.
|
||||
|
||||
Visit `this page <https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/README.md>`_ to discover the official examples or look at the examples on the `wiki <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Examples>`_ to see other bots the community has built.
|
||||
Visit `this page <https://github.com/python-telegram-bot/python-telegram-bot/blob/v13.x/examples/README.md>`_ to discover the official examples or look at the examples on the `wiki <https://github.com/python-telegram-bot/python-telegram-bot/wiki/Examples>`_ to see other bots the community has built.
|
||||
|
||||
-------
|
||||
Logging
|
||||
@@ -226,7 +227,7 @@ You can get help in several ways:
|
||||
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>`_.
|
||||
Contributions of all sizes are welcome. Please review our `contribution guidelines <https://github.com/python-telegram-bot/python-telegram-bot/blob/v13.x/.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
|
||||
|
||||
+4
-4
@@ -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.7-blue?logo=telegram
|
||||
.. image:: https://img.shields.io/badge/Bot%20API-6.1-blue?logo=telegram
|
||||
:target: https://core.telegram.org/bots/api-changelog
|
||||
:alt: Supported Bot API versions
|
||||
|
||||
@@ -91,7 +91,7 @@ Introduction
|
||||
|
||||
This library provides a pure Python, lightweight interface for the
|
||||
`Telegram Bot API <https://core.telegram.org/bots/api>`_.
|
||||
It's compatible with Python versions 3.6.8+. PTB-Raw might also work on `PyPy <http://pypy.org/>`_, though there have been a lot of issues before. Hence, PyPy is not officially supported.
|
||||
It's compatible with Python versions 3.7+. PTB-Raw might also work on `PyPy <http://pypy.org/>`_, though there have been a lot of issues before. Hence, PyPy is not officially supported.
|
||||
|
||||
``python-telegram-bot-raw`` is part of the `python-telegram-bot <https://python-telegram-bot.org>`_ ecosystem and provides the pure API functionality extracted from PTB. It therefore does *not* have independent release schedules, changelogs or documentation. Please consult the PTB resources.
|
||||
|
||||
@@ -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.7** are supported.
|
||||
All types and methods of the Telegram Bot API **6.1** are supported.
|
||||
|
||||
==========
|
||||
Installing
|
||||
@@ -208,7 +208,7 @@ You can get help in several ways:
|
||||
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>`_.
|
||||
Contributions of all sizes are welcome. Please review our `contribution guidelines <https://github.com/python-telegram-bot/python-telegram-bot/blob/v13.x/.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
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
/*
|
||||
Generated by https://darkreader.org
|
||||
Instructions: Install the extension on a Chromium-based browser
|
||||
Then do this to export the CSS: https://git.io/JOM6t and drop it here
|
||||
Then do this to export the CSS: https://github.com/darkreader/darkreader/issues/604#issuecomment-661107893 and drop it here
|
||||
Some color values where manually changed - just search for "/*" in this file and insert them in the new css
|
||||
*/
|
||||
/* User-Agent Style */
|
||||
|
||||
+2
-2
@@ -60,9 +60,9 @@ author = u'Leandro Toledo'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '13.11' # telegram.__version__[:3]
|
||||
version = '13.13' # telegram.__version__[:3]
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '13.11' # telegram.__version__
|
||||
release = '13.13' # telegram.__version__
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
:github_url: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/telegram/chatadministratorrights.py
|
||||
|
||||
telegram.ChatAdministratorRights
|
||||
================================
|
||||
|
||||
.. autoclass:: telegram.ChatAdministratorRights
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,8 @@
|
||||
:github_url: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/telegram/menubutton.py
|
||||
|
||||
telegram.MenuButton
|
||||
===================
|
||||
|
||||
.. autoclass:: telegram.MenuButton
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,8 @@
|
||||
:github_url: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/telegram/menubutton.py
|
||||
|
||||
telegram.MenuButtonCommands
|
||||
===========================
|
||||
|
||||
.. autoclass:: telegram.MenuButtonCommands
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,8 @@
|
||||
:github_url: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/telegram/menubutton.py
|
||||
|
||||
telegram.MenuButtonDefault
|
||||
==========================
|
||||
|
||||
.. autoclass:: telegram.MenuButtonDefault
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,8 @@
|
||||
:github_url: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/telegram/menubutton.py
|
||||
|
||||
telegram.MenuButtonWebApp
|
||||
=========================
|
||||
|
||||
.. autoclass:: telegram.MenuButtonWebApp
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -17,6 +17,7 @@ telegram package
|
||||
telegram.botcommandscopechatmember
|
||||
telegram.callbackquery
|
||||
telegram.chat
|
||||
telegram.chatadministratorrights
|
||||
telegram.chataction
|
||||
telegram.chatinvitelink
|
||||
telegram.chatjoinrequest
|
||||
@@ -51,6 +52,10 @@ telegram package
|
||||
telegram.keyboardbuttonpolltype
|
||||
telegram.location
|
||||
telegram.loginurl
|
||||
telegram.menubutton
|
||||
telegram.menubuttoncommands
|
||||
telegram.menubuttondefault
|
||||
telegram.menubuttonwebapp
|
||||
telegram.message
|
||||
telegram.messageautodeletetimerchanged
|
||||
telegram.messageid
|
||||
@@ -64,18 +69,25 @@ telegram package
|
||||
telegram.replykeyboardremove
|
||||
telegram.replykeyboardmarkup
|
||||
telegram.replymarkup
|
||||
telegram.sentwebappmessage
|
||||
telegram.telegramobject
|
||||
telegram.update
|
||||
telegram.user
|
||||
telegram.userprofilephotos
|
||||
telegram.venue
|
||||
telegram.video
|
||||
telegram.videochatended
|
||||
telegram.videochatparticipantsinvited
|
||||
telegram.videochatscheduled
|
||||
telegram.videochatstarted
|
||||
telegram.videonote
|
||||
telegram.voice
|
||||
telegram.voicechatstarted
|
||||
telegram.voicechatended
|
||||
telegram.voicechatscheduled
|
||||
telegram.voicechatparticipantsinvited
|
||||
telegram.webappdata
|
||||
telegram.webappinfo
|
||||
telegram.webhookinfo
|
||||
|
||||
Stickers
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
:github_url: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/telegram/sentwebappmessage.py
|
||||
|
||||
telegram.SentWebAppMessage
|
||||
==========================
|
||||
|
||||
.. autoclass:: telegram.SentWebAppMessage
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,9 @@
|
||||
:github_url: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/telegram/videochatended.py
|
||||
|
||||
telegram.VideoChatEnded
|
||||
=======================
|
||||
|
||||
.. autoclass:: telegram.VideoChatEnded
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
:github_url: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/telegram/videochatparticipantsinvited.py
|
||||
|
||||
telegram.VideoChatParticipantsInvited
|
||||
=====================================
|
||||
|
||||
.. autoclass:: telegram.VideoChatParticipantsInvited
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,8 @@
|
||||
:github_url: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/telegram/videochatscheduled.py
|
||||
|
||||
telegram.VideoChatScheduled
|
||||
===========================
|
||||
|
||||
.. autoclass:: telegram.VideoChatScheduled
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,8 @@
|
||||
:github_url: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/telegram/videochatstarted.py
|
||||
|
||||
telegram.VideoChatStarted
|
||||
=========================
|
||||
|
||||
.. autoclass:: telegram.VideoChatStarted
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -7,3 +7,5 @@ telegram.VoiceChatEnded
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
.. versionchanged:: v13.12
|
||||
Since Bot API 6.0, voice chat was renamed to video chat.
|
||||
|
||||
@@ -7,3 +7,5 @@ telegram.VoiceChatParticipantsInvited
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
.. versionchanged:: v13.12
|
||||
Since Bot API 6.0, voice chat was renamed to video chat.
|
||||
|
||||
@@ -7,3 +7,5 @@ telegram.VoiceChatScheduled
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
.. versionchanged:: v13.12
|
||||
Since Bot API 6.0, voice chat was renamed to video chat.
|
||||
|
||||
@@ -7,3 +7,5 @@ telegram.VoiceChatStarted
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
.. versionchanged:: v13.12
|
||||
Since Bot API 6.0, voice chat was renamed to video chat.
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
:github_url: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/telegram/webappdata.py
|
||||
|
||||
telegram.WebAppData
|
||||
===================
|
||||
|
||||
.. autoclass:: telegram.WebAppData
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,8 @@
|
||||
:github_url: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/telegram/webappinfo.py
|
||||
|
||||
telegram.WebAppInfo
|
||||
===================
|
||||
|
||||
.. autoclass:: telegram.WebAppInfo
|
||||
:members:
|
||||
:show-inheritance:
|
||||
+24
-24
@@ -2,60 +2,60 @@
|
||||
|
||||
In this folder are small examples to show what a bot written with `python-telegram-bot` looks like. Some bots focus on one specific aspect of the Telegram Bot API while others focus on one of the mechanics of this library. Except for the [`rawapibot.py`](#pure-api) example, they all use the high-level framework this library provides with the [`telegram.ext`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.html) submodule.
|
||||
|
||||
All examples are licensed under the [CC0 License](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/LICENSE.txt) and are therefore fully dedicated to the public domain. You can use them as the base for your own bots without worrying about copyrights.
|
||||
All examples are licensed under the [CC0 License](https://github.com/python-telegram-bot/python-telegram-bot/blob/v13.x/examples/LICENSE.txt) and are therefore fully dedicated to the public domain. You can use them as the base for your own bots without worrying about copyrights.
|
||||
|
||||
Do note that we ignore one pythonic convention. Best practice would dictate, in many handler callbacks function signatures, to replace the argument `context` with an underscore, since `context` is an unused local variable in those callbacks. However, since these are examples and not having a name for that argument confuses beginners, we decided to have it present.
|
||||
|
||||
### [`echobot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/echobot.py)
|
||||
### [`echobot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/v13.x/examples/echobot.py)
|
||||
This is probably the base for most of the bots made with `python-telegram-bot`. It simply replies to each text message with a message that contains the same text.
|
||||
|
||||
### [`timerbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/timerbot.py)
|
||||
### [`timerbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/v13.x/examples/timerbot.py)
|
||||
This bot uses the [`JobQueue`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.jobqueue.html) class to send timed messages. The user sets a timer by using `/set` command with a specific time, for example `/set 30`. The bot then sets up a job to send a message to that user after 30 seconds. The user can also cancel the timer by sending `/unset`. To learn more about the `JobQueue`, read [this wiki article](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Extensions-%E2%80%93-JobQueue).
|
||||
|
||||
### [`conversationbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/conversationbot.py)
|
||||
A common task for a bot is to ask information from the user. In v5.0 of this library, we introduced the [`ConversationHandler`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.conversationhandler.html) for that exact purpose. This example uses it to retrieve user-information in a conversation-like style. To get a better understanding, take a look at the [state diagram](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/conversationbot.png).
|
||||
### [`conversationbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/v13.x/examples/conversationbot.py)
|
||||
A common task for a bot is to ask information from the user. In v5.0 of this library, we introduced the [`ConversationHandler`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.conversationhandler.html) for that exact purpose. This example uses it to retrieve user-information in a conversation-like style. To get a better understanding, take a look at the [state diagram](https://github.com/python-telegram-bot/python-telegram-bot/blob/v13.x/examples/conversationbot.png).
|
||||
|
||||
### [`conversationbot2.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/conversationbot2.py)
|
||||
A more complex example of a bot that uses the `ConversationHandler`. It is also more confusing. Good thing there is a [fancy state diagram](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/conversationbot2.png) for this one, too!
|
||||
### [`conversationbot2.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/v13.x/examples/conversationbot2.py)
|
||||
A more complex example of a bot that uses the `ConversationHandler`. It is also more confusing. Good thing there is a [fancy state diagram](https://github.com/python-telegram-bot/python-telegram-bot/blob/v13.x/examples/conversationbot2.png) for this one, too!
|
||||
|
||||
### [`nestedconversationbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/nestedconversationbot.py)
|
||||
A even more complex example of a bot that uses the nested `ConversationHandler`s. While it's certainly not that complex that you couldn't built it without nested `ConversationHanldler`s, it gives a good impression on how to work with them. Of course, there is a [fancy state diagram](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/nestedconversationbot.png) for this example, too!
|
||||
### [`nestedconversationbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/v13.x/examples/nestedconversationbot.py)
|
||||
A even more complex example of a bot that uses the nested `ConversationHandler`s. While it's certainly not that complex that you couldn't built it without nested `ConversationHanldler`s, it gives a good impression on how to work with them. Of course, there is a [fancy state diagram](https://github.com/python-telegram-bot/python-telegram-bot/blob/v13.x/examples/nestedconversationbot.png) for this example, too!
|
||||
|
||||
### [`persistentconversationbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/persistentconversationbot.py)
|
||||
### [`persistentconversationbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/v13.x/examples/persistentconversationbot.py)
|
||||
A basic example of a bot store conversation state and user_data over multiple restarts.
|
||||
|
||||
### [`inlinekeyboard.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/inlinekeyboard.py)
|
||||
This example sheds some light on inline keyboards, callback queries and message editing. A wikipedia site explaining this examples lives at https://git.io/JOmFw.
|
||||
### [`inlinekeyboard.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/v13.x/examples/inlinekeyboard.py)
|
||||
This example sheds some light on inline keyboards, callback queries and message editing. A wikipedia site explaining this examples lives at https://github.com/python-telegram-bot/python-telegram-bot/wiki/InlineKeyboard-Example.
|
||||
|
||||
### [`inlinekeyboard2.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/inlinekeyboard2.py)
|
||||
### [`inlinekeyboard2.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/v13.x/examples/inlinekeyboard2.py)
|
||||
A more complex example about inline keyboards, callback queries and message editing. This example showcases how an interactive menu could be build using inline keyboards.
|
||||
|
||||
### [`deeplinking.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/deeplinking.py)
|
||||
### [`deeplinking.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/v13.x/examples/deeplinking.py)
|
||||
A basic example on how to use deeplinking with inline keyboards.
|
||||
|
||||
### [`inlinebot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/inlinebot.py)
|
||||
### [`inlinebot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/v13.x/examples/inlinebot.py)
|
||||
A basic example of an [inline bot](https://core.telegram.org/bots/inline). Don't forget to enable inline mode with [@BotFather](https://telegram.me/BotFather).
|
||||
|
||||
### [`pollbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/pollbot.py)
|
||||
### [`pollbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/v13.x/examples/pollbot.py)
|
||||
This example sheds some light on polls, poll answers and the corresponding handlers.
|
||||
|
||||
### [`passportbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/passportbot.py)
|
||||
A basic example of a bot that can accept passports. Use in combination with [`passportbot.html`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/passportbot.html). Don't forget to enable and configure payments with [@BotFather](https://telegram.me/BotFather). Check out this [guide](https://git.io/fAvYd) on Telegram passports in PTB.
|
||||
### [`passportbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/v13.x/examples/passportbot.py)
|
||||
A basic example of a bot that can accept passports. Use in combination with [`passportbot.html`](https://github.com/python-telegram-bot/python-telegram-bot/blob/v13.x/examples/passportbot.html). Don't forget to enable and configure payments with [@BotFather](https://telegram.me/BotFather). Check out this [guide](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Telegram-Passport) on Telegram passports in PTB.
|
||||
|
||||
### [`paymentbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/paymentbot.py)
|
||||
### [`paymentbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/v13.x/examples/paymentbot.py)
|
||||
A basic example of a bot that can accept payments. Don't forget to enable and configure payments with [@BotFather](https://telegram.me/BotFather).
|
||||
|
||||
### [`errorhandlerbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/errorhandlerbot.py)
|
||||
### [`errorhandlerbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/v13.x/examples/errorhandlerbot.py)
|
||||
A basic example on how to set up a custom error handler.
|
||||
|
||||
### [`chatmemberbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/chatmemberbot.py)
|
||||
### [`chatmemberbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/v13.x/examples/chatmemberbot.py)
|
||||
A basic example on how `(my_)chat_member` updates can be used.
|
||||
|
||||
### [`contexttypesbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/contexttypesbot.py)
|
||||
### [`contexttypesbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/v13.x/examples/contexttypesbot.py)
|
||||
This example showcases how `telegram.ext.ContextTypes` can be used to customize the `context` argument of handler and job callbacks.
|
||||
|
||||
### [`arbitrarycallbackdatabot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/arbitrarycallbackdatabot.py)
|
||||
### [`arbitrarycallbackdatabot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/v13.x/examples/arbitrarycallbackdatabot.py)
|
||||
This example showcases how PTBs "arbitrary callback data" feature can be used.
|
||||
|
||||
## Pure API
|
||||
The [`rawapibot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/rawapibot.py) example uses only the pure, "bare-metal" API wrapper.
|
||||
The [`rawapibot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/v13.x/examples/rawapibot.py) example uses only the pure, "bare-metal" API wrapper.
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
|
||||
"""This example showcases how PTBs "arbitrary callback data" feature can be used.
|
||||
|
||||
For detailed info on arbitrary callback data, see the wiki page at https://git.io/JGBDI
|
||||
For detailed info on arbitrary callback data, see the wiki page at
|
||||
https://github.com/python-telegram-bot/python-telegram-bot/wiki/Arbitrary-callback_data
|
||||
"""
|
||||
import logging
|
||||
from typing import List, Tuple, cast
|
||||
|
||||
+10
-18
@@ -44,24 +44,16 @@ def extract_status_change(
|
||||
return None
|
||||
|
||||
old_status, new_status = status_change
|
||||
was_member = (
|
||||
old_status
|
||||
in [
|
||||
ChatMember.MEMBER,
|
||||
ChatMember.CREATOR,
|
||||
ChatMember.ADMINISTRATOR,
|
||||
]
|
||||
or (old_status == ChatMember.RESTRICTED and old_is_member is True)
|
||||
)
|
||||
is_member = (
|
||||
new_status
|
||||
in [
|
||||
ChatMember.MEMBER,
|
||||
ChatMember.CREATOR,
|
||||
ChatMember.ADMINISTRATOR,
|
||||
]
|
||||
or (new_status == ChatMember.RESTRICTED and new_is_member is True)
|
||||
)
|
||||
was_member = old_status in [
|
||||
ChatMember.MEMBER,
|
||||
ChatMember.CREATOR,
|
||||
ChatMember.ADMINISTRATOR,
|
||||
] or (old_status == ChatMember.RESTRICTED and old_is_member is True)
|
||||
is_member = new_status in [
|
||||
ChatMember.MEMBER,
|
||||
ChatMember.CREATOR,
|
||||
ChatMember.ADMINISTRATOR,
|
||||
] or (new_status == ChatMember.RESTRICTED and new_is_member is True)
|
||||
|
||||
return was_member, is_member
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
"""
|
||||
Basic example for a bot that uses inline keyboards. For an in-depth explanation, check out
|
||||
https://git.io/JOmFw.
|
||||
https://github.com/python-telegram-bot/python-telegram-bot/wiki/InlineKeyboard-Example.
|
||||
"""
|
||||
import logging
|
||||
|
||||
|
||||
@@ -7,7 +7,8 @@ Simple Bot to print/download all incoming passport data
|
||||
|
||||
See https://telegram.org/blog/passport for info about what telegram passport is.
|
||||
|
||||
See https://git.io/fAvYd for how to use Telegram Passport properly with python-telegram-bot.
|
||||
See https://github.com/python-telegram-bot/python-telegram-bot/wiki/Telegram-Passport
|
||||
for how to use Telegram Passport properly with python-telegram-bot.
|
||||
|
||||
"""
|
||||
import logging
|
||||
|
||||
@@ -3,7 +3,7 @@ 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!
|
||||
black==20.8b1
|
||||
black==22.3.0
|
||||
flake8==3.9.2
|
||||
pylint==2.8.3
|
||||
mypy==0.812
|
||||
|
||||
@@ -30,6 +30,7 @@ filterwarnings =
|
||||
; Unfortunately due to https://github.com/pytest-dev/pytest/issues/8343 we can't have this here
|
||||
; and instead do a trick directly in tests/conftest.py
|
||||
; ignore::telegram.utils.deprecate.TelegramDeprecationWarning
|
||||
markers = dev: If you want to test a specific test, use this
|
||||
|
||||
[coverage:run]
|
||||
branch = True
|
||||
|
||||
@@ -65,7 +65,7 @@ def get_setup_kwargs(raw=False):
|
||||
author_email='devs@python-telegram-bot.org',
|
||||
license='LGPLv3',
|
||||
url='https://python-telegram-bot.org/',
|
||||
# Keywords supported by PyPI can be found at https://git.io/JtLIZ
|
||||
# Keywords supported by PyPI can be found at https://github.com/pypa/warehouse/blob/aafc5185e57e67d43487ce4faa95913dd4573e14/warehouse/templates/packaging/detail.html#L20-L58
|
||||
project_urls={
|
||||
"Documentation": "https://python-telegram-bot.readthedocs.io",
|
||||
"Bug Tracker": "https://github.com/python-telegram-bot/python-telegram-bot/issues",
|
||||
@@ -98,12 +98,11 @@ def get_setup_kwargs(raw=False):
|
||||
'Topic :: Internet',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Programming Language :: Python :: 3.8',
|
||||
'Programming Language :: Python :: 3.9',
|
||||
],
|
||||
python_requires='>=3.6'
|
||||
python_requires='>=3.7'
|
||||
)
|
||||
|
||||
return kwargs
|
||||
|
||||
+25
-2
@@ -20,9 +20,16 @@
|
||||
|
||||
from .base import TelegramObject
|
||||
from .botcommand import BotCommand
|
||||
from .webappdata import WebAppData
|
||||
from .webappinfo import WebAppInfo
|
||||
from .sentwebappmessage import SentWebAppMessage
|
||||
from .menubutton import MenuButton, MenuButtonCommands, MenuButtonDefault, MenuButtonWebApp
|
||||
from .loginurl import LoginUrl
|
||||
from .games.callbackgame import CallbackGame
|
||||
from .user import User
|
||||
from .files.chatphoto import ChatPhoto
|
||||
from .chat import Chat
|
||||
from .chatadministratorrights import ChatAdministratorRights
|
||||
from .chatlocation import ChatLocation
|
||||
from .chatinvitelink import ChatInviteLink
|
||||
from .chatjoinrequest import ChatJoinRequest
|
||||
@@ -71,9 +78,13 @@ from .voicechat import (
|
||||
VoiceChatParticipantsInvited,
|
||||
VoiceChatScheduled,
|
||||
)
|
||||
from .loginurl import LoginUrl
|
||||
from .videochat import (
|
||||
VideoChatStarted,
|
||||
VideoChatEnded,
|
||||
VideoChatParticipantsInvited,
|
||||
VideoChatScheduled,
|
||||
)
|
||||
from .proximityalerttriggered import ProximityAlertTriggered
|
||||
from .games.callbackgame import CallbackGame
|
||||
from .payment.shippingaddress import ShippingAddress
|
||||
from .payment.orderinfo import OrderInfo
|
||||
from .payment.successfulpayment import SuccessfulPayment
|
||||
@@ -193,6 +204,7 @@ __all__ = ( # Keep this alphabetically ordered
|
||||
'CallbackGame',
|
||||
'CallbackQuery',
|
||||
'Chat',
|
||||
'ChatAdministratorRights',
|
||||
'ChatAction',
|
||||
'ChatInviteLink',
|
||||
'ChatJoinRequest',
|
||||
@@ -272,6 +284,10 @@ __all__ = ( # Keep this alphabetically ordered
|
||||
'MAX_MESSAGES_PER_SECOND_PER_CHAT',
|
||||
'MAX_MESSAGE_LENGTH',
|
||||
'MaskPosition',
|
||||
'MenuButton',
|
||||
'MenuButtonCommands',
|
||||
'MenuButtonDefault',
|
||||
'MenuButtonWebApp',
|
||||
'Message',
|
||||
'MessageAutoDeleteTimerChanged',
|
||||
'MessageEntity',
|
||||
@@ -304,6 +320,7 @@ __all__ = ( # Keep this alphabetically ordered
|
||||
'SUPPORTED_WEBHOOK_PORTS',
|
||||
'SecureData',
|
||||
'SecureValue',
|
||||
'SentWebAppMessage',
|
||||
'ShippingAddress',
|
||||
'ShippingOption',
|
||||
'ShippingQuery',
|
||||
@@ -318,11 +335,17 @@ __all__ = ( # Keep this alphabetically ordered
|
||||
'UserProfilePhotos',
|
||||
'Venue',
|
||||
'Video',
|
||||
'VideoChatEnded',
|
||||
'VideoChatParticipantsInvited',
|
||||
'VideoChatScheduled',
|
||||
'VideoChatStarted',
|
||||
'VideoNote',
|
||||
'Voice',
|
||||
'VoiceChatStarted',
|
||||
'VoiceChatEnded',
|
||||
'VoiceChatScheduled',
|
||||
'VoiceChatParticipantsInvited',
|
||||
'WebAppData',
|
||||
'WebAppInfo',
|
||||
'WebhookInfo',
|
||||
)
|
||||
|
||||
@@ -41,7 +41,7 @@ def print_ver_info() -> None: # skipcq: PY-D0003
|
||||
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]
|
||||
print('certifi' + certifi.__version__)
|
||||
sys_version = sys.version.replace('\n', ' ')
|
||||
print(f'Python {sys_version}')
|
||||
|
||||
|
||||
+391
-4
@@ -88,6 +88,9 @@ from telegram import (
|
||||
WebhookInfo,
|
||||
InlineKeyboardMarkup,
|
||||
ChatInviteLink,
|
||||
SentWebAppMessage,
|
||||
ChatAdministratorRights,
|
||||
MenuButton,
|
||||
)
|
||||
from telegram.constants import MAX_INLINE_QUERY_RESULTS
|
||||
from telegram.error import InvalidToken, TelegramError
|
||||
@@ -3077,6 +3080,7 @@ class Bot(TelegramObject):
|
||||
api_kwargs: JSONDict = None,
|
||||
ip_address: str = None,
|
||||
drop_pending_updates: bool = None,
|
||||
secret_token: str = None,
|
||||
) -> bool:
|
||||
"""
|
||||
Use this method to specify a url and receive incoming updates via an outgoing webhook.
|
||||
@@ -3084,9 +3088,9 @@ class Bot(TelegramObject):
|
||||
specified url, containing a JSON-serialized Update. In case of an unsuccessful request,
|
||||
Telegram will give up after a reasonable amount of attempts.
|
||||
|
||||
If you'd like to make sure that the Webhook request comes from Telegram, Telegram
|
||||
recommends using a secret path in the URL, e.g. https://www.example.com/<token>. Since
|
||||
nobody else knows your bot's token, you can be pretty sure it's us.
|
||||
If you'd like to make sure that the Webhook was set by you, you can specify secret data in
|
||||
the parameter ``secret_token``. If specified, the request will contain a header
|
||||
``X-Telegram-Bot-Api-Secret-Token`` with the secret token as content.
|
||||
|
||||
Note:
|
||||
The certificate argument should be a file from disk ``open(filename, 'rb')``.
|
||||
@@ -3114,6 +3118,12 @@ class Bot(TelegramObject):
|
||||
a short period of time.
|
||||
drop_pending_updates (:obj:`bool`, optional): Pass :obj:`True` to drop all pending
|
||||
updates.
|
||||
secret_token (:obj:`str`, optional): A secret token to be sent in a header
|
||||
``X-Telegram-Bot-Api-Secret-Token`` in every webhook request, 1-256 characters.
|
||||
Only characters ``A-Z``, ``a-z``, ``0-9``, ``_`` and ``-`` are allowed.
|
||||
The header is useful to ensure that the request comes from a webhook set by you.
|
||||
|
||||
.. versionadded:: 13.13
|
||||
timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as
|
||||
the read timeout from the server (instead of the one specified during creation of
|
||||
the connection pool).
|
||||
@@ -3154,6 +3164,8 @@ class Bot(TelegramObject):
|
||||
data['ip_address'] = ip_address
|
||||
if drop_pending_updates:
|
||||
data['drop_pending_updates'] = drop_pending_updates
|
||||
if secret_token is not None:
|
||||
data["secret_token"] = secret_token
|
||||
|
||||
result = self._post('setWebhook', data, timeout=timeout, api_kwargs=api_kwargs)
|
||||
|
||||
@@ -3897,6 +3909,47 @@ class Bot(TelegramObject):
|
||||
|
||||
return result # type: ignore[return-value]
|
||||
|
||||
@log
|
||||
def answer_web_app_query(
|
||||
self,
|
||||
web_app_query_id: str,
|
||||
result: 'InlineQueryResult',
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
) -> SentWebAppMessage:
|
||||
"""Use this method to set the result of an interaction with a Web App and send a
|
||||
corresponding message on behalf of the user to the chat from which the query originated.
|
||||
|
||||
.. versionadded:: 13.12
|
||||
|
||||
Args:
|
||||
web_app_query_id (:obj:`str`): Unique identifier for the query to be answered.
|
||||
result (:class:`telegram.InlineQueryResult`): An object describing the message to be
|
||||
sent.
|
||||
timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as
|
||||
the read timeout from the server (instead of the one specified during creation of
|
||||
the connection pool).
|
||||
api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the
|
||||
Telegram API.
|
||||
|
||||
Returns:
|
||||
:class:`telegram.SentWebAppMessage`: On success, a sent
|
||||
:class:`telegram.SentWebAppMessage` is returned.
|
||||
|
||||
Raises:
|
||||
:class:`telegram.error.TelegramError`
|
||||
"""
|
||||
data: JSONDict = {'web_app_query_id': web_app_query_id, 'result': result}
|
||||
|
||||
api_result = self._post(
|
||||
'answerWebAppQuery',
|
||||
data,
|
||||
timeout=timeout,
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
|
||||
return SentWebAppMessage.de_json(api_result, self) # type: ignore[return-value, arg-type]
|
||||
|
||||
@log
|
||||
def restrict_chat_member(
|
||||
self,
|
||||
@@ -3976,6 +4029,7 @@ class Bot(TelegramObject):
|
||||
is_anonymous: bool = None,
|
||||
can_manage_chat: bool = None,
|
||||
can_manage_voice_chats: bool = None,
|
||||
can_manage_video_chats: bool = None,
|
||||
) -> bool:
|
||||
"""
|
||||
Use this method to promote or demote a user in a supergroup or a channel. The bot must be
|
||||
@@ -4000,6 +4054,14 @@ class Bot(TelegramObject):
|
||||
|
||||
.. versionadded:: 13.4
|
||||
|
||||
.. deprecated:: 13.12
|
||||
Since Bot API 6.0, voice chat was renamed to video chat.
|
||||
|
||||
can_manage_video_chats (:obj:`bool`, optional): Pass :obj:`True`, if the administrator
|
||||
can manage video chats.
|
||||
|
||||
.. versionadded:: 13.12
|
||||
|
||||
can_change_info (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can
|
||||
change chat title, photo and other settings.
|
||||
can_post_messages (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can
|
||||
@@ -4031,6 +4093,11 @@ class Bot(TelegramObject):
|
||||
:class:`telegram.error.TelegramError`
|
||||
|
||||
"""
|
||||
if can_manage_voice_chats is not None and can_manage_video_chats is not None:
|
||||
raise ValueError(
|
||||
"Only supply one of `can_manage_video_chats`/`can_manage_voice_chats`, not both."
|
||||
)
|
||||
|
||||
data: JSONDict = {'chat_id': chat_id, 'user_id': user_id}
|
||||
|
||||
if is_anonymous is not None:
|
||||
@@ -4054,7 +4121,9 @@ class Bot(TelegramObject):
|
||||
if can_manage_chat is not None:
|
||||
data['can_manage_chat'] = can_manage_chat
|
||||
if can_manage_voice_chats is not None:
|
||||
data['can_manage_voice_chats'] = can_manage_voice_chats
|
||||
data['can_manage_video_chats'] = can_manage_voice_chats
|
||||
if can_manage_video_chats is not None:
|
||||
data['can_manage_video_chats'] = can_manage_video_chats
|
||||
|
||||
result = self._post('promoteChatMember', data, timeout=timeout, api_kwargs=api_kwargs)
|
||||
|
||||
@@ -5381,6 +5450,101 @@ class Bot(TelegramObject):
|
||||
protect_content=protect_content,
|
||||
)
|
||||
|
||||
@log
|
||||
def get_my_default_administrator_rights(
|
||||
self,
|
||||
for_channels: bool = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
) -> ChatAdministratorRights:
|
||||
"""Use this method to get the current default administrator rights of the bot.
|
||||
|
||||
.. seealso:: :meth:`set_my_default_administrator_rights`
|
||||
|
||||
.. versionadded:: 13.12
|
||||
|
||||
Args:
|
||||
for_channels (:obj:`bool`, optional): Pass :obj:`True` to get default administrator
|
||||
rights of the bot in channels. Otherwise, default administrator rights of the bot
|
||||
for groups and supergroups will be returned.
|
||||
timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as
|
||||
the read timeout from the server (instead of the one specified during creation of
|
||||
the connection pool).
|
||||
api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the
|
||||
Telegram API.
|
||||
|
||||
Returns:
|
||||
:class:`telegram.ChatAdministratorRights`: On success.
|
||||
|
||||
Raises:
|
||||
:class:`telegram.error.TelegramError`
|
||||
"""
|
||||
data: JSONDict = {}
|
||||
|
||||
if for_channels is not None:
|
||||
data['for_channels'] = for_channels
|
||||
|
||||
result = self._post(
|
||||
'getMyDefaultAdministratorRights',
|
||||
data,
|
||||
timeout=timeout,
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
|
||||
return ChatAdministratorRights.de_json(result, self) # type: ignore[return-value,arg-type]
|
||||
|
||||
@log
|
||||
def set_my_default_administrator_rights(
|
||||
self,
|
||||
rights: ChatAdministratorRights = None,
|
||||
for_channels: bool = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
) -> bool:
|
||||
"""Use this method to change the default administrator rights requested by the bot when
|
||||
it's added as an administrator to groups or channels. These rights will be suggested to
|
||||
users, but they are are free to modify the list before adding the bot.
|
||||
|
||||
.. seealso:: :meth:`get_my_default_administrator_rights`
|
||||
|
||||
.. versionadded:: 13.12
|
||||
|
||||
Args:
|
||||
rights (:obj:`telegram.ChatAdministratorRights`, optional): A
|
||||
:obj:`telegram.ChatAdministratorRights` object describing new default administrator
|
||||
rights. If not specified, the default administrator rights will be cleared.
|
||||
for_channels (:obj:`bool`, optional): Pass :obj:`True` to change the default
|
||||
administrator rights of the bot in channels. Otherwise, the default administrator
|
||||
rights of the bot for groups and supergroups will be changed.
|
||||
timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as
|
||||
the read timeout from the server (instead of the one specified during creation of
|
||||
the connection pool).
|
||||
api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the
|
||||
Telegram API.
|
||||
|
||||
Returns:
|
||||
:obj:`bool`: Returns :obj:`True` on success.
|
||||
|
||||
Raises:
|
||||
:obj:`telegram.error.TelegramError`
|
||||
"""
|
||||
data: JSONDict = {}
|
||||
|
||||
if rights is not None:
|
||||
data['rights'] = rights.to_dict()
|
||||
|
||||
if for_channels is not None:
|
||||
data['for_channels'] = for_channels
|
||||
|
||||
result = self._post(
|
||||
'setMyDefaultAdministratorRights',
|
||||
data,
|
||||
timeout=timeout,
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
|
||||
return result # type: ignore[return-value]
|
||||
|
||||
@log
|
||||
def get_my_commands(
|
||||
self,
|
||||
@@ -5677,6 +5841,217 @@ class Bot(TelegramObject):
|
||||
result = self._post('copyMessage', data, timeout=timeout, api_kwargs=api_kwargs)
|
||||
return MessageId.de_json(result, self) # type: ignore[return-value, arg-type]
|
||||
|
||||
@log
|
||||
def set_chat_menu_button(
|
||||
self,
|
||||
chat_id: int = None,
|
||||
menu_button: MenuButton = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
) -> bool:
|
||||
"""Use this method to change the bot's menu button in a private chat, or the default menu
|
||||
button.
|
||||
|
||||
.. seealso:: :meth:`get_chat_menu_button`, :meth:`telegram.Chat.set_menu_button`,
|
||||
:meth:`telegram.User.set_menu_button`
|
||||
|
||||
.. versionadded:: 13.12
|
||||
|
||||
Args:
|
||||
chat_id (:obj:`int`, optional): Unique identifier for the target private chat. If not
|
||||
specified, default bot's menu button will be changed
|
||||
menu_button (:class:`telegram.MenuButton`, optional): An object for the new bot's menu
|
||||
button. Defaults to :class:`telegram.MenuButtonDefault`.
|
||||
timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as
|
||||
the read timeout from the server (instead of the one specified during creation of
|
||||
the connection pool).
|
||||
api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the
|
||||
Telegram API.
|
||||
|
||||
Returns:
|
||||
:obj:`bool`: On success, :obj:`True` is returned.
|
||||
"""
|
||||
data: JSONDict = {}
|
||||
if chat_id is not None:
|
||||
data['chat_id'] = chat_id
|
||||
if menu_button is not None:
|
||||
data['menu_button'] = menu_button.to_dict()
|
||||
|
||||
return self._post( # type: ignore[return-value]
|
||||
'setChatMenuButton',
|
||||
data,
|
||||
timeout=timeout,
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
|
||||
@log
|
||||
def get_chat_menu_button(
|
||||
self,
|
||||
chat_id: int = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
) -> MenuButton:
|
||||
"""Use this method to get the current value of the bot's menu button in a private chat, or
|
||||
the default menu button.
|
||||
|
||||
.. seealso:: :meth:`set_chat_menu_button`, :meth:`telegram.Chat.get_menu_button`,
|
||||
:meth:`telegram.User.get_menu_button`
|
||||
|
||||
.. versionadded:: 13.12
|
||||
|
||||
Args:
|
||||
chat_id (:obj:`int`, optional): Unique identifier for the target private chat. If not
|
||||
specified, default bot's menu button will be returned.
|
||||
timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as
|
||||
the read timeout from the server (instead of the one specified during creation of
|
||||
the connection pool).
|
||||
api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the
|
||||
Telegram API.
|
||||
|
||||
Returns:
|
||||
:class:`telegram.MenuButton`: On success, the current menu button is returned.
|
||||
"""
|
||||
data = {}
|
||||
if chat_id is not None:
|
||||
data['chat_id'] = chat_id
|
||||
|
||||
result = self._post(
|
||||
'getChatMenuButton',
|
||||
data,
|
||||
timeout=timeout,
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
return MenuButton.de_json(result, bot=self) # type: ignore[return-value, arg-type]
|
||||
|
||||
@log
|
||||
def create_invoice_link(
|
||||
self,
|
||||
title: str,
|
||||
description: str,
|
||||
payload: str,
|
||||
provider_token: str,
|
||||
currency: str,
|
||||
prices: List["LabeledPrice"],
|
||||
max_tip_amount: int = None,
|
||||
suggested_tip_amounts: List[int] = None,
|
||||
provider_data: Union[str, object] = None,
|
||||
photo_url: str = None,
|
||||
photo_size: int = None,
|
||||
photo_width: int = None,
|
||||
photo_height: int = None,
|
||||
need_name: bool = None,
|
||||
need_phone_number: bool = None,
|
||||
need_email: bool = None,
|
||||
need_shipping_address: bool = None,
|
||||
send_phone_number_to_provider: bool = None,
|
||||
send_email_to_provider: bool = None,
|
||||
is_flexible: bool = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
) -> str:
|
||||
"""Use this method to create a link for an invoice.
|
||||
|
||||
.. versionadded:: 13.13
|
||||
|
||||
Args:
|
||||
title (:obj:`str`): Product name. 1-32 characters.
|
||||
description (:obj:`str`): Product description. 1-255 characters.
|
||||
payload (:obj:`str`): Bot-defined invoice payload. 1-128 bytes. This will not be
|
||||
displayed to the user, use for your internal processes.
|
||||
provider_token (:obj:`str`): Payments provider token, obtained via
|
||||
`@BotFather <https://t.me/BotFather>`_.
|
||||
currency (:obj:`str`): Three-letter ISO 4217 currency code, see `more on currencies
|
||||
<https://core.telegram.org/bots/payments#supported-currencies>`_.
|
||||
prices (List[:class:`telegram.LabeledPrice`)]: Price breakdown, a list
|
||||
of components (e.g. product price, tax, discount, delivery cost, delivery tax,
|
||||
bonus, etc.).
|
||||
max_tip_amount (:obj:`int`, optional): The maximum accepted amount for tips in the
|
||||
*smallest* units of the currency (integer, **not** float/double). For example, for
|
||||
a maximum tip of US$ 1.45 pass ``max_tip_amount = 145``. See the exp parameter in
|
||||
`currencies.json <https://core.telegram.org/bots/payments/currencies.json>`_, it
|
||||
shows the number of digits past the decimal point for each currency (2 for the
|
||||
majority of currencies). Defaults to ``0``.
|
||||
suggested_tip_amounts (List[:obj:`int`], optional): An array of
|
||||
suggested amounts of tips in the *smallest* units of the currency (integer, **not**
|
||||
float/double). At most 4 suggested tip amounts can be specified. The suggested tip
|
||||
amounts must be positive, passed in a strictly increased order and must not exceed
|
||||
``max_tip_amount``.
|
||||
provider_data (:obj:`str` | :obj:`object`, optional): Data about the
|
||||
invoice, which will be shared with the payment provider. A detailed description of
|
||||
required fields should be provided by the payment provider. When an object is
|
||||
passed, it will be encoded as JSON.
|
||||
photo_url (:obj:`str`, optional): URL of the product photo for the invoice. Can be a
|
||||
photo of the goods or a marketing image for a service.
|
||||
photo_size (:obj:`int`, optional): Photo size in bytes.
|
||||
photo_width (:obj:`int`, optional): Photo width.
|
||||
photo_height (:obj:`int`, optional): Photo height.
|
||||
need_name (:obj:`bool`, optional): Pass :obj:`True`, if you require the user's full
|
||||
name to complete the order.
|
||||
need_phone_number (:obj:`bool`, optional): Pass :obj:`True`, if you require the user's
|
||||
phone number to complete the order.
|
||||
need_email (:obj:`bool`, optional): Pass :obj:`True`, if you require the user's email
|
||||
address to complete the order.
|
||||
need_shipping_address (:obj:`bool`, optional): Pass :obj:`True`, if you require the
|
||||
user's shipping address to complete the order.
|
||||
send_phone_number_to_provider (:obj:`bool`, optional): Pass :obj:`True`, if user's
|
||||
phone number should be sent to provider.
|
||||
send_email_to_provider (:obj:`bool`, optional): Pass :obj:`True`, if user's email
|
||||
address should be sent to provider.
|
||||
is_flexible (:obj:`bool`, optional): Pass :obj:`True`, if the final price depends on
|
||||
the shipping method.
|
||||
timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as
|
||||
the read timeout from the server (instead of the one specified during creation of
|
||||
the connection pool).
|
||||
api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the
|
||||
Telegram API.
|
||||
|
||||
Returns:
|
||||
:class:`str`: On success, the created invoice link is returned.
|
||||
"""
|
||||
data: JSONDict = {
|
||||
"title": title,
|
||||
"description": description,
|
||||
"payload": payload,
|
||||
"provider_token": provider_token,
|
||||
"currency": currency,
|
||||
"prices": [p.to_dict() for p in prices],
|
||||
}
|
||||
if max_tip_amount is not None:
|
||||
data["max_tip_amount"] = max_tip_amount
|
||||
if suggested_tip_amounts is not None:
|
||||
data["suggested_tip_amounts"] = suggested_tip_amounts
|
||||
if provider_data is not None:
|
||||
data["provider_data"] = provider_data
|
||||
if photo_url is not None:
|
||||
data["photo_url"] = photo_url
|
||||
if photo_size is not None:
|
||||
data["photo_size"] = photo_size
|
||||
if photo_width is not None:
|
||||
data["photo_width"] = photo_width
|
||||
if photo_height is not None:
|
||||
data["photo_height"] = photo_height
|
||||
if need_name is not None:
|
||||
data["need_name"] = need_name
|
||||
if need_phone_number is not None:
|
||||
data["need_phone_number"] = need_phone_number
|
||||
if need_email is not None:
|
||||
data["need_email"] = need_email
|
||||
if need_shipping_address is not None:
|
||||
data["need_shipping_address"] = need_shipping_address
|
||||
if is_flexible is not None:
|
||||
data["is_flexible"] = is_flexible
|
||||
if send_phone_number_to_provider is not None:
|
||||
data["send_phone_number_to_provider"] = send_phone_number_to_provider
|
||||
if send_email_to_provider is not None:
|
||||
data["send_email_to_provider"] = send_email_to_provider
|
||||
|
||||
return self._post( # type: ignore[return-value]
|
||||
"createInvoiceLink",
|
||||
data,
|
||||
timeout=timeout,
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data: JSONDict = {'id': self.id, 'username': self.username, 'first_name': self.first_name}
|
||||
@@ -5793,6 +6168,8 @@ class Bot(TelegramObject):
|
||||
"""Alias for :meth:`answer_shipping_query`"""
|
||||
answerPreCheckoutQuery = answer_pre_checkout_query
|
||||
"""Alias for :meth:`answer_pre_checkout_query`"""
|
||||
answerWebAppQuery = answer_web_app_query
|
||||
"""Alias for :meth:`answer_web_app_query`"""
|
||||
restrictChatMember = restrict_chat_member
|
||||
"""Alias for :meth:`restrict_chat_member`"""
|
||||
promoteChatMember = promote_chat_member
|
||||
@@ -5859,3 +6236,13 @@ class Bot(TelegramObject):
|
||||
"""Alias for :meth:`log_out`"""
|
||||
copyMessage = copy_message
|
||||
"""Alias for :meth:`copy_message`"""
|
||||
getChatMenuButton = get_chat_menu_button
|
||||
"""Alias for :meth:`get_chat_menu_button`"""
|
||||
setChatMenuButton = set_chat_menu_button
|
||||
"""Alias for :meth:`set_chat_menu_button`"""
|
||||
getMyDefaultAdministratorRights = get_my_default_administrator_rights
|
||||
"""Alias for :meth:`get_my_default_administrator_rights`"""
|
||||
setMyDefaultAdministratorRights = set_my_default_administrator_rights
|
||||
"""Alias for :meth:`set_my_default_administrator_rights`"""
|
||||
createInvoiceLink = create_invoice_link
|
||||
"""Alias for :meth:`create_invoice_link`"""
|
||||
|
||||
+89
-1
@@ -22,7 +22,7 @@ import warnings
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING, List, Optional, ClassVar, Union, Tuple, Any
|
||||
|
||||
from telegram import ChatPhoto, TelegramObject, constants
|
||||
from telegram import ChatPhoto, TelegramObject, constants, MenuButton
|
||||
from telegram.utils.types import JSONDict, FileInput, ODVInput, DVInput
|
||||
from telegram.utils.deprecate import TelegramDeprecationWarning
|
||||
|
||||
@@ -116,6 +116,16 @@ class Chat(TelegramObject):
|
||||
chats. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
location (:class:`telegram.ChatLocation`, optional): For supergroups, the location to which
|
||||
the supergroup is connected. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
join_to_send_messages (:obj:`bool`, optional): :obj:`True`, if users need to join the
|
||||
supergroup before they can send messages. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 13.13
|
||||
join_by_request (:obj:`bool`, optional): :obj:`True`, if all users directly joining the
|
||||
supergroup need to be approved by supergroup administrators. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 13.13
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
Attributes:
|
||||
@@ -160,7 +170,16 @@ class Chat(TelegramObject):
|
||||
chats. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
location (:class:`telegram.ChatLocation`): Optional. For supergroups, the location to which
|
||||
the supergroup is connected. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
join_to_send_messages (:obj:`bool`): Optional. :obj:`True`, if users need to join the
|
||||
supergroup before they can send messages. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 13.13
|
||||
join_by_request (:obj:`bool`): Optional. :obj:`True`, if all users directly joining the
|
||||
supergroup need to be approved by supergroup administrators. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 13.13
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
@@ -186,6 +205,8 @@ class Chat(TelegramObject):
|
||||
'message_auto_delete_time',
|
||||
'has_protected_content',
|
||||
'has_private_forwards',
|
||||
'join_to_send_messages',
|
||||
'join_by_request',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
@@ -226,6 +247,8 @@ class Chat(TelegramObject):
|
||||
message_auto_delete_time: int = None,
|
||||
has_private_forwards: bool = None,
|
||||
has_protected_content: bool = None,
|
||||
join_to_send_messages: bool = None,
|
||||
join_by_request: bool = None,
|
||||
**_kwargs: Any,
|
||||
):
|
||||
# Required
|
||||
@@ -254,6 +277,8 @@ class Chat(TelegramObject):
|
||||
self.can_set_sticker_set = can_set_sticker_set
|
||||
self.linked_chat_id = linked_chat_id
|
||||
self.location = location
|
||||
self.join_to_send_messages = join_to_send_messages
|
||||
self.join_by_request = join_by_request
|
||||
|
||||
self.bot = bot
|
||||
self._id_attrs = (self.id,)
|
||||
@@ -590,6 +615,7 @@ class Chat(TelegramObject):
|
||||
is_anonymous: bool = None,
|
||||
can_manage_chat: bool = None,
|
||||
can_manage_voice_chats: bool = None,
|
||||
can_manage_video_chats: bool = None,
|
||||
) -> bool:
|
||||
"""Shortcut for::
|
||||
|
||||
@@ -600,6 +626,9 @@ class Chat(TelegramObject):
|
||||
|
||||
.. versionadded:: 13.2
|
||||
|
||||
..versionchanged:: 13.12
|
||||
Since Bot API 6.0, voice chat was renamed to video chat.
|
||||
|
||||
Returns:
|
||||
:obj:`bool`: On success, :obj:`True` is returned.
|
||||
|
||||
@@ -620,6 +649,7 @@ class Chat(TelegramObject):
|
||||
is_anonymous=is_anonymous,
|
||||
can_manage_chat=can_manage_chat,
|
||||
can_manage_voice_chats=can_manage_voice_chats,
|
||||
can_manage_video_chats=can_manage_video_chats,
|
||||
)
|
||||
|
||||
def restrict_member(
|
||||
@@ -1813,3 +1843,61 @@ class Chat(TelegramObject):
|
||||
return self.bot.decline_chat_join_request(
|
||||
chat_id=self.id, user_id=user_id, timeout=timeout, api_kwargs=api_kwargs
|
||||
)
|
||||
|
||||
def set_menu_button(
|
||||
self,
|
||||
menu_button: MenuButton = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
) -> bool:
|
||||
"""Shortcut for::
|
||||
|
||||
bot.set_chat_menu_button(chat_id=update.effective_chat.id, *args, **kwargs)
|
||||
|
||||
For the documentation of the arguments, please see
|
||||
:meth:`telegram.Bot.set_chat_menu_button`.
|
||||
|
||||
Caution:
|
||||
Can only work, if the chat is a private chat.
|
||||
|
||||
..seealso:: :meth:`get_menu_button`
|
||||
|
||||
.. versionadded:: 13.12
|
||||
|
||||
Returns:
|
||||
:obj:`bool`: On success, :obj:`True` is returned.
|
||||
"""
|
||||
return self.bot.set_chat_menu_button(
|
||||
chat_id=self.id,
|
||||
menu_button=menu_button,
|
||||
timeout=timeout,
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
|
||||
def get_menu_button(
|
||||
self,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
) -> MenuButton:
|
||||
"""Shortcut for::
|
||||
|
||||
bot.get_chat_menu_button(chat_id=update.effective_chat.id, *args, **kwargs)
|
||||
|
||||
For the documentation of the arguments, please see
|
||||
:meth:`telegram.Bot.set_chat_menu_button`.
|
||||
|
||||
Caution:
|
||||
Can only work, if the chat is a private chat.
|
||||
|
||||
..seealso:: :meth:`set_menu_button`
|
||||
|
||||
.. versionadded:: 13.12
|
||||
|
||||
Returns:
|
||||
:class:`telegram.MenuButton`: On success, the current menu button is returned.
|
||||
"""
|
||||
return self.bot.get_chat_menu_button(
|
||||
chat_id=self.id,
|
||||
timeout=timeout,
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2022
|
||||
# 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 class which represents a Telegram ChatAdministratorRights."""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from telegram import TelegramObject
|
||||
|
||||
|
||||
class ChatAdministratorRights(TelegramObject):
|
||||
"""Represents the rights of an administrator in a chat.
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`is_anonymous`, :attr:`can_manage_chat`,
|
||||
:attr:`can_delete_messages`, :attr:`can_manage_video_chats`, :attr:`can_restrict_members`,
|
||||
:attr:`can_promote_members`, :attr:`can_change_info`, :attr:`can_invite_users`,
|
||||
:attr:`can_post_messages`, :attr:`can_edit_messages`, :attr:`can_pin_messages` are equal.
|
||||
|
||||
.. seealso: :meth:`Bot.set_my_default_administrator_rights`,
|
||||
:meth:`Bot.get_my_default_administrator_rights`
|
||||
|
||||
.. versionadded:: 13.12
|
||||
|
||||
Args:
|
||||
is_anonymous (:obj:`bool`): :obj:`True`, if the user's presence in the chat is hidden.
|
||||
can_manage_chat (:obj:`bool`): :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.
|
||||
can_delete_messages (:obj:`bool`): :obj:`True`, if the administrator can delete messages of
|
||||
other users.
|
||||
can_manage_video_chats (:obj:`bool`): :obj:`True`, if the administrator can manage video
|
||||
chats.
|
||||
can_restrict_members (:obj:`bool`): :obj:`True`, if the administrator can restrict, ban or
|
||||
unban chat members.
|
||||
can_promote_members (:obj:`bool`): :obj:`True`, if the administrator can add new
|
||||
administrators with a subset of their own privileges or demote administrators that he
|
||||
has promoted, directly or indirectly (promoted by administrators that were appointed by
|
||||
the user.)
|
||||
can_change_info (:obj:`bool`): :obj:`True`, if the user is allowed to change the chat title
|
||||
, photo and other settings.
|
||||
can_invite_users (:obj:`bool`): :obj:`True`, if the user is allowed to invite new users to
|
||||
the chat.
|
||||
can_post_messages (:obj:`bool`, optional): :obj:`True`, if the administrator can post
|
||||
messages in the channel; channels only.
|
||||
can_edit_messages (:obj:`bool`, optional): :obj:`True`, if the administrator can edit
|
||||
messages of other users.
|
||||
can_pin_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed to pin
|
||||
messages; groups and supergroups only.
|
||||
|
||||
Attributes:
|
||||
is_anonymous (:obj:`bool`): :obj:`True`, if the user's presence in the chat is hidden.
|
||||
can_manage_chat (:obj:`bool`): :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.
|
||||
can_delete_messages (:obj:`bool`): :obj:`True`, if the administrator can delete messages of
|
||||
other users.
|
||||
can_manage_video_chats (:obj:`bool`): :obj:`True`, if the administrator can manage video
|
||||
chats.
|
||||
can_restrict_members (:obj:`bool`): :obj:`True`, if the administrator can restrict, ban or
|
||||
unban chat members.
|
||||
can_promote_members (:obj:`bool`): :obj:`True`, if the administrator can add new
|
||||
administrators with a subset of their own privileges or demote administrators that he
|
||||
has promoted, directly or indirectly (promoted by administrators that were appointed by
|
||||
the user.)
|
||||
can_change_info (:obj:`bool`): :obj:`True`, if the user is allowed to change the chat title
|
||||
,photo and other settings.
|
||||
can_invite_users (:obj:`bool`): :obj:`True`, if the user is allowed to invite new users to
|
||||
the chat.
|
||||
can_post_messages (:obj:`bool`): Optional. :obj:`True`, if the administrator can post
|
||||
messages in the channel; channels only.
|
||||
can_edit_messages (:obj:`bool`): Optional. :obj:`True`, if the administrator can edit
|
||||
messages of other users.
|
||||
can_pin_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to pin
|
||||
messages; groups and supergroups only.
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
'is_anonymous',
|
||||
'can_manage_chat',
|
||||
'can_delete_messages',
|
||||
'can_manage_video_chats',
|
||||
'can_restrict_members',
|
||||
'can_promote_members',
|
||||
'can_change_info',
|
||||
'can_invite_users',
|
||||
'can_post_messages',
|
||||
'can_edit_messages',
|
||||
'can_pin_messages',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
is_anonymous: bool,
|
||||
can_manage_chat: bool,
|
||||
can_delete_messages: bool,
|
||||
can_manage_video_chats: bool,
|
||||
can_restrict_members: bool,
|
||||
can_promote_members: bool,
|
||||
can_change_info: bool,
|
||||
can_invite_users: bool,
|
||||
can_post_messages: bool = None,
|
||||
can_edit_messages: bool = None,
|
||||
can_pin_messages: bool = None,
|
||||
**_kwargs: Any,
|
||||
) -> None:
|
||||
# Required
|
||||
self.is_anonymous = is_anonymous
|
||||
self.can_manage_chat = can_manage_chat
|
||||
self.can_delete_messages = can_delete_messages
|
||||
self.can_manage_video_chats = can_manage_video_chats
|
||||
self.can_restrict_members = can_restrict_members
|
||||
self.can_promote_members = can_promote_members
|
||||
self.can_change_info = can_change_info
|
||||
self.can_invite_users = can_invite_users
|
||||
# Optionals
|
||||
self.can_post_messages = can_post_messages
|
||||
self.can_edit_messages = can_edit_messages
|
||||
self.can_pin_messages = can_pin_messages
|
||||
|
||||
self._id_attrs = (
|
||||
self.is_anonymous,
|
||||
self.can_manage_chat,
|
||||
self.can_delete_messages,
|
||||
self.can_manage_video_chats,
|
||||
self.can_restrict_members,
|
||||
self.can_promote_members,
|
||||
self.can_change_info,
|
||||
self.can_invite_users,
|
||||
self.can_post_messages,
|
||||
self.can_edit_messages,
|
||||
self.can_pin_messages,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def all_rights(cls) -> 'ChatAdministratorRights':
|
||||
"""
|
||||
This method returns the :class:`ChatAdministratorRights` object with all attributes set to
|
||||
:obj:`True`. This is e.g. useful when changing the bot's default administrator rights with
|
||||
:meth:`telegram.Bot.set_my_default_administrator_rights`.
|
||||
"""
|
||||
return cls(True, True, True, True, True, True, True, True, True, True, True)
|
||||
|
||||
@classmethod
|
||||
def no_rights(cls) -> 'ChatAdministratorRights':
|
||||
"""
|
||||
This method returns the :class:`ChatAdministratorRights` object with all attributes set to
|
||||
:obj:`False`.
|
||||
"""
|
||||
return cls(False, False, False, False, False, False, False, False, False, False, False)
|
||||
+36
-1
@@ -286,6 +286,7 @@ class ChatMember(TelegramObject):
|
||||
'can_pin_messages',
|
||||
'can_manage_chat',
|
||||
'can_manage_voice_chats',
|
||||
'can_manage_video_chats',
|
||||
'until_date',
|
||||
'_id_attrs',
|
||||
)
|
||||
@@ -327,8 +328,18 @@ class ChatMember(TelegramObject):
|
||||
is_anonymous: bool = None,
|
||||
can_manage_chat: bool = None,
|
||||
can_manage_voice_chats: bool = None,
|
||||
can_manage_video_chats: bool = None,
|
||||
**_kwargs: Any,
|
||||
):
|
||||
# check before required to not waste resources if the error is raised
|
||||
if can_manage_voice_chats is not None and can_manage_video_chats is not None:
|
||||
# if they are the same it's fine...
|
||||
if can_manage_voice_chats != can_manage_video_chats:
|
||||
raise ValueError(
|
||||
"Only supply one of `can_manage_video_chats`/`can_manage_voice_chats`,"
|
||||
" not both."
|
||||
)
|
||||
|
||||
# Required
|
||||
self.user = user
|
||||
self.status = status
|
||||
@@ -353,7 +364,13 @@ class ChatMember(TelegramObject):
|
||||
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
|
||||
temp = (
|
||||
can_manage_video_chats
|
||||
if can_manage_video_chats is not None
|
||||
else can_manage_voice_chats
|
||||
)
|
||||
self.can_manage_voice_chats = temp
|
||||
self.can_manage_video_chats = temp
|
||||
|
||||
self._id_attrs = (self.user, self.status)
|
||||
|
||||
@@ -436,6 +453,9 @@ class ChatMemberAdministrator(ChatMember):
|
||||
|
||||
.. versionadded:: 13.7
|
||||
|
||||
.. versionchanged:: 13.12
|
||||
Since Bot API 6.0, voice chat was renamed to video chat.
|
||||
|
||||
Args:
|
||||
user (:class:`telegram.User`): Information about the user.
|
||||
can_be_edited (:obj:`bool`, optional): :obj:`True`, if the bot
|
||||
@@ -456,6 +476,12 @@ class ChatMemberAdministrator(ChatMember):
|
||||
administrator can delete messages of other users.
|
||||
can_manage_voice_chats (:obj:`bool`, optional): :obj:`True`, if the
|
||||
administrator can manage voice chats.
|
||||
|
||||
.. deprecated:: 13.12
|
||||
can_manage_video_chats (:obj:`bool`): :obj:`True`, if the
|
||||
administrator can manage video chats.
|
||||
|
||||
.. versionadded:: 13.12
|
||||
can_restrict_members (:obj:`bool`, optional): :obj:`True`, if the
|
||||
administrator can restrict, ban or unban chat members.
|
||||
can_promote_members (:obj:`bool`, optional): :obj:`True`, if the administrator
|
||||
@@ -491,6 +517,13 @@ class ChatMemberAdministrator(ChatMember):
|
||||
administrator can delete messages of other users.
|
||||
can_manage_voice_chats (:obj:`bool`): Optional. :obj:`True`, if the
|
||||
administrator can manage voice chats.
|
||||
|
||||
.. deprecated:: 13.12 contains the same value as :attr:`can_manage_video_chats`
|
||||
for backwards compatibility.
|
||||
can_manage_video_chats (:obj:`bool`): :obj:`True`, if the
|
||||
administrator can manage video chats.
|
||||
|
||||
.. versionadded:: 13.12
|
||||
can_restrict_members (:obj:`bool`): Optional. :obj:`True`, if the
|
||||
administrator can restrict, ban or unban chat members.
|
||||
can_promote_members (:obj:`bool`): Optional. :obj:`True`, if the administrator
|
||||
@@ -523,6 +556,7 @@ class ChatMemberAdministrator(ChatMember):
|
||||
can_change_info: bool = None,
|
||||
can_invite_users: bool = None,
|
||||
can_pin_messages: bool = None,
|
||||
can_manage_video_chats: bool = None,
|
||||
**_kwargs: Any,
|
||||
):
|
||||
super().__init__(
|
||||
@@ -541,6 +575,7 @@ class ChatMemberAdministrator(ChatMember):
|
||||
can_change_info=can_change_info,
|
||||
can_invite_users=can_invite_users,
|
||||
can_pin_messages=can_pin_messages,
|
||||
can_manage_video_chats=can_manage_video_chats,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ The following constants were extracted from the
|
||||
`Telegram Bots API <https://core.telegram.org/bots/api>`_.
|
||||
|
||||
Attributes:
|
||||
BOT_API_VERSION (:obj:`str`): `5.7`. Telegram Bot API version supported by this
|
||||
BOT_API_VERSION (:obj:`str`): `6.1`. Telegram Bot API version supported by this
|
||||
version of `python-telegram-bot`. Also available as ``telegram.bot_api_version``.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
@@ -247,7 +247,7 @@ Attributes:
|
||||
"""
|
||||
from typing import List
|
||||
|
||||
BOT_API_VERSION: str = '5.7'
|
||||
BOT_API_VERSION: str = '6.1'
|
||||
MAX_MESSAGE_LENGTH: int = 4096
|
||||
MAX_CAPTION_LENGTH: int = 1024
|
||||
ANONYMOUS_ADMIN_ID: int = 1087968824
|
||||
@@ -396,3 +396,7 @@ BOT_COMMAND_SCOPE_ALL_CHAT_ADMINISTRATORS = 'all_chat_administrators'
|
||||
BOT_COMMAND_SCOPE_CHAT = 'chat'
|
||||
BOT_COMMAND_SCOPE_CHAT_ADMINISTRATORS = 'chat_administrators'
|
||||
BOT_COMMAND_SCOPE_CHAT_MEMBER = 'chat_member'
|
||||
|
||||
MENU_BUTTON_COMMANDS = 'commands'
|
||||
MENU_BUTTON_WEB_APP = 'web_app'
|
||||
MENU_BUTTON_DEFAULT = 'default'
|
||||
|
||||
+3
-2
@@ -50,8 +50,9 @@ class Dice(TelegramObject):
|
||||
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.
|
||||
can be found at our `wiki <https://github.com/python-telegram-bot/python-telegram-bot/\
|
||||
wiki/Code-snippets#map-a-slot-machine-dice-value-to-the-corresponding-symbols>`_.
|
||||
However, this behaviour is undocumented and might be changed by Telegram.
|
||||
|
||||
Args:
|
||||
value (:obj:`int`): Value of the dice. 1-6 for dice, darts and bowling balls, 1-5 for
|
||||
|
||||
@@ -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=C0413
|
||||
"""Extensions over the Telegram Bot API to facilitate bot making"""
|
||||
|
||||
from .extbot import ExtBot
|
||||
@@ -28,17 +27,6 @@ from .callbackcontext import CallbackContext
|
||||
from .contexttypes import ContextTypes
|
||||
from .dispatcher import Dispatcher, DispatcherHandlerStop, run_async
|
||||
|
||||
# https://bugs.python.org/issue41451, fixed on 3.7+, doesn't actually remove slots
|
||||
# try-except is just here in case the __init__ is called twice (like in the tests)
|
||||
# this block is also the reason for the pylint-ignore at the top of the file
|
||||
try:
|
||||
del Dispatcher.__slots__
|
||||
except AttributeError as exc:
|
||||
if str(exc) == '__slots__':
|
||||
pass
|
||||
else:
|
||||
raise exc
|
||||
|
||||
from .jobqueue import JobQueue, Job
|
||||
from .updater import Updater
|
||||
from .callbackqueryhandler import CallbackQueryHandler
|
||||
|
||||
@@ -139,7 +139,9 @@ class CallbackContext(Generic[UD, CD, BD]):
|
||||
@bot_data.setter
|
||||
def bot_data(self, value: object) -> NoReturn:
|
||||
raise AttributeError(
|
||||
"You can not assign a new value to bot_data, see https://git.io/Jt6ic"
|
||||
"You can not assign a new value to bot_data, "
|
||||
"see https://github.com/python-telegram-bot/python-telegram-bot/wiki/Storing-bot%2C"
|
||||
"-user-and-chat-related-data "
|
||||
)
|
||||
|
||||
@property
|
||||
@@ -160,7 +162,9 @@ class CallbackContext(Generic[UD, CD, BD]):
|
||||
@chat_data.setter
|
||||
def chat_data(self, value: object) -> NoReturn:
|
||||
raise AttributeError(
|
||||
"You can not assign a new value to chat_data, see https://git.io/Jt6ic"
|
||||
"You can not assign a new value to chat_data, "
|
||||
"see https://github.com/python-telegram-bot/python-telegram-bot/wiki/Storing-bot%2C"
|
||||
"-user-and-chat-related-data "
|
||||
)
|
||||
|
||||
@property
|
||||
@@ -175,7 +179,9 @@ class CallbackContext(Generic[UD, CD, BD]):
|
||||
@user_data.setter
|
||||
def user_data(self, value: object) -> NoReturn:
|
||||
raise AttributeError(
|
||||
"You can not assign a new value to user_data, see https://git.io/Jt6ic"
|
||||
"You can not assign a new value to user_data, "
|
||||
"see https://github.com/python-telegram-bot/python-telegram-bot/wiki/Storing-bot%2C"
|
||||
"-user-and-chat-related-data "
|
||||
)
|
||||
|
||||
def refresh_data(self) -> None:
|
||||
|
||||
@@ -55,7 +55,8 @@ class CallbackQueryHandler(Handler[Update, CCT]):
|
||||
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.
|
||||
https://github.com/python-telegram-bot/python-telegram-bot/wiki\
|
||||
/Transition-guide-to-Version-12.0 for more info.
|
||||
* If your bot allows arbitrary objects as ``callback_data``, it may happen that the
|
||||
original ``callback_data`` for the incoming :class:`telegram.CallbackQuery`` can not be
|
||||
found. This is the case when either a malicious client tempered with the
|
||||
|
||||
@@ -35,7 +35,8 @@ class ChatJoinRequestHandler(Handler[Update, CCT]):
|
||||
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.
|
||||
https://github.com/python-telegram-bot/python-telegram-bot/wiki\
|
||||
/Transition-guide-to-Version-12.0 for more info.
|
||||
|
||||
Warning:
|
||||
When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom
|
||||
|
||||
@@ -39,7 +39,8 @@ class ChatMemberHandler(Handler[Update, CCT]):
|
||||
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.
|
||||
https://github.com/python-telegram-bot/python-telegram-bot/wiki\
|
||||
/Transition-guide-to-Version-12.0 for more info.
|
||||
|
||||
Warning:
|
||||
When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom
|
||||
|
||||
@@ -42,7 +42,8 @@ class ChosenInlineResultHandler(Handler[Update, CCT]):
|
||||
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.
|
||||
https://github.com/python-telegram-bot/python-telegram-bot/wiki\
|
||||
/Transition-guide-to-Version-12.0 for more info.
|
||||
|
||||
Warning:
|
||||
When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom
|
||||
|
||||
@@ -55,7 +55,8 @@ class CommandHandler(Handler[Update, CCT]):
|
||||
user or in the same chat, it will be the same :obj:`dict`.
|
||||
|
||||
Note that this is DEPRECATED, and you should use context based callbacks. See
|
||||
https://git.io/fxJuV for more info.
|
||||
https://github.com/python-telegram-bot/python-telegram-bot/wiki\
|
||||
/Transition-guide-to-Version-12.0 for more info.
|
||||
|
||||
Warning:
|
||||
When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom
|
||||
@@ -169,7 +170,9 @@ class CommandHandler(Handler[Update, CCT]):
|
||||
|
||||
if allow_edited is not None:
|
||||
warnings.warn(
|
||||
'allow_edited is deprecated. See https://git.io/fxJuV for more info',
|
||||
'allow_edited is deprecated. See '
|
||||
'https://github.com/python-telegram-bot/python-telegram-bot/wiki/Transition'
|
||||
'-guide-to-Version-12.0 for more info',
|
||||
TelegramDeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
@@ -288,7 +291,8 @@ class PrefixHandler(CommandHandler):
|
||||
user or in the same chat, it will be the same :obj:`dict`.
|
||||
|
||||
Note that this is DEPRECATED, and you should use context based callbacks. See
|
||||
https://git.io/fxJuV for more info.
|
||||
https://github.com/python-telegram-bot/python-telegram-bot/wiki\
|
||||
/Transition-guide-to-Version-12.0 for more info.
|
||||
|
||||
Warning:
|
||||
When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom
|
||||
|
||||
@@ -50,7 +50,8 @@ class ContextTypes(Generic[CCT, UD, CD, BD]):
|
||||
|
||||
__slots__ = ('_context', '_bot_data', '_chat_data', '_user_data')
|
||||
|
||||
# overload signatures generated with https://git.io/JtJPj
|
||||
# overload signatures generated with
|
||||
# https://gist.github.com/Bibo-Joshi/399382cda537fb01bd86b13c3d03a956
|
||||
|
||||
@overload
|
||||
def __init__(
|
||||
|
||||
@@ -79,7 +79,8 @@ class ConversationHandler(Handler[Update, CCT]):
|
||||
|
||||
Finally, ``ConversationHandler``, does *not* handle (edited) channel posts.
|
||||
|
||||
.. _`FAQ`: https://git.io/JtcyU
|
||||
.. _`FAQ`: https://github.com/python-telegram-bot/python-telegram-bot/wiki\
|
||||
/Frequently-Asked-Questions#what-do-the-per_-settings-in-conversationhandler-do
|
||||
|
||||
The first collection, a ``list`` named :attr:`entry_points`, is used to initiate the
|
||||
conversation, for example with a :class:`telegram.ext.CommandHandler` or
|
||||
@@ -119,7 +120,7 @@ class ConversationHandler(Handler[Update, CCT]):
|
||||
:attr:`END` to end the *parent* conversation from within the nested one. For an example on
|
||||
nested :class:`ConversationHandler` s, see our `examples`_.
|
||||
|
||||
.. _`examples`: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples
|
||||
.. _`examples`: https://github.com/python-telegram-bot/python-telegram-bot/blob/v13.x/examples
|
||||
|
||||
Args:
|
||||
entry_points (List[:class:`telegram.ext.Handler`]): A list of ``Handler`` objects that can
|
||||
|
||||
@@ -241,7 +241,9 @@ class Dispatcher(Generic[CCT, UD, CD, BD]):
|
||||
|
||||
if not use_context:
|
||||
warnings.warn(
|
||||
'Old Handler API is deprecated - see https://git.io/fxJuV for details',
|
||||
'Old Handler API is deprecated - see '
|
||||
'https://github.com/python-telegram-bot/python-telegram-bot/wiki/Transition'
|
||||
'-guide-to-Version-12.0 for details',
|
||||
TelegramDeprecationWarning,
|
||||
stacklevel=3,
|
||||
)
|
||||
@@ -763,7 +765,8 @@ class Dispatcher(Generic[CCT, UD, CD, BD]):
|
||||
asynchronously using :meth:`run_async`. Defaults to :obj:`False`.
|
||||
|
||||
Note:
|
||||
See https://git.io/fxJuV for more info about switching to context based API.
|
||||
See https://github.com/python-telegram-bot/python-telegram-bot/wiki\
|
||||
/Transition-guide-to-Version-12.0 for more info about switching to context based API.
|
||||
"""
|
||||
if callback in self.error_handlers:
|
||||
self.logger.debug('The callback is already registered as an error handler. Ignoring.')
|
||||
|
||||
@@ -62,7 +62,9 @@ class ExtBot(telegram.bot.Bot):
|
||||
arbitrary_callback_data (:obj:`bool` | :obj:`int`, optional): Whether to
|
||||
allow arbitrary objects as callback data for :class:`telegram.InlineKeyboardButton`.
|
||||
Pass an integer to specify the maximum number of objects cached in memory. For more
|
||||
details, please see our `wiki <https://git.io/JGBDI>`_. Defaults to :obj:`False`.
|
||||
details, please see our `wiki <https://github.com/python-telegram-bot/\
|
||||
python-telegram-bot/wiki/Arbitrary-callback_data>`_.
|
||||
Defaults to :obj:`False`.
|
||||
|
||||
Attributes:
|
||||
arbitrary_callback_data (:obj:`bool` | :obj:`int`): Whether this bot instance
|
||||
|
||||
@@ -1004,6 +1004,38 @@ officedocument.wordprocessingml.document")``.
|
||||
location = _Location()
|
||||
"""Messages that contain :class:`telegram.Location`."""
|
||||
|
||||
class _UserAttachment(UpdateFilter):
|
||||
__slots__ = ()
|
||||
name = "Filters.user_attachment"
|
||||
|
||||
def filter(self, update: Update) -> bool:
|
||||
return bool(update.effective_user) and bool(
|
||||
update.effective_user.added_to_attachment_menu
|
||||
)
|
||||
|
||||
user_attachment = _UserAttachment()
|
||||
"""This filter filters *any* message that have a user who added the bot to their
|
||||
:attr:`attachment menu <telegram.User.added_to_attachment_menu>` as
|
||||
:attr:`telegram.Update.effective_user`.
|
||||
|
||||
.. versionadded:: 13.13
|
||||
"""
|
||||
|
||||
class _UserPremium(UpdateFilter):
|
||||
__slots__ = ()
|
||||
name = "Filters.premium_user"
|
||||
|
||||
def filter(self, update: Update) -> bool:
|
||||
return bool(update.effective_user) and bool(update.effective_user.is_premium)
|
||||
|
||||
premium_user = _UserPremium()
|
||||
"""This filter filters *any* message from a
|
||||
:attr:`Telegram Premium user <telegram.User.is_premium>` as
|
||||
:attr:`telegram.Update.effective_user`.
|
||||
|
||||
.. versionadded:: 13.13
|
||||
"""
|
||||
|
||||
class _Venue(MessageFilter):
|
||||
__slots__ = ()
|
||||
name = 'Filters.venue'
|
||||
@@ -1182,6 +1214,56 @@ officedocument.wordprocessingml.document")``.
|
||||
voice_chat_participants_invited = _VoiceChatParticipantsInvited()
|
||||
"""Messages that contain :attr:`telegram.Message.voice_chat_participants_invited`."""
|
||||
|
||||
class _VideoChatScheduled(MessageFilter):
|
||||
__slots__ = ()
|
||||
name = 'Filters.status_update.video_chat_scheduled'
|
||||
|
||||
def filter(self, message: Message) -> bool:
|
||||
return bool(message.video_chat_scheduled)
|
||||
|
||||
video_chat_scheduled = _VideoChatScheduled()
|
||||
"""Messages that contain :attr:`telegram.Message.video_chat_scheduled`."""
|
||||
|
||||
class _VideoChatStarted(MessageFilter):
|
||||
__slots__ = ()
|
||||
name = 'Filters.status_update.video_chat_started'
|
||||
|
||||
def filter(self, message: Message) -> bool:
|
||||
return bool(message.video_chat_started)
|
||||
|
||||
video_chat_started = _VideoChatStarted()
|
||||
"""Messages that contain :attr:`telegram.Message.video_chat_started`."""
|
||||
|
||||
class _VideoChatEnded(MessageFilter):
|
||||
__slots__ = ()
|
||||
name = 'Filters.status_update.video_chat_ended'
|
||||
|
||||
def filter(self, message: Message) -> bool:
|
||||
return bool(message.video_chat_ended)
|
||||
|
||||
video_chat_ended = _VideoChatEnded()
|
||||
"""Messages that contain :attr:`telegram.Message.voice_chat_ended`."""
|
||||
|
||||
class _VideoChatParticipantsInvited(MessageFilter):
|
||||
__slots__ = ()
|
||||
name = 'Filters.status_update.video_chat_participants_invited'
|
||||
|
||||
def filter(self, message: Message) -> bool:
|
||||
return bool(message.video_chat_participants_invited)
|
||||
|
||||
video_chat_participants_invited = _VideoChatParticipantsInvited()
|
||||
"""Messages that contain :attr:`telegram.Message.video_chat_participants_invited`."""
|
||||
|
||||
class _WebAppData(MessageFilter):
|
||||
__slots__ = ()
|
||||
name = 'Filters.status_update.web_app_data'
|
||||
|
||||
def filter(self, message: Message) -> bool:
|
||||
return bool(message.web_app_data)
|
||||
|
||||
web_app_data = _WebAppData()
|
||||
"""Messages that contain :attr:`telegram.Message.web_app_data`."""
|
||||
|
||||
name = 'Filters.status_update'
|
||||
|
||||
def filter(self, message: Update) -> bool:
|
||||
@@ -1201,6 +1283,11 @@ officedocument.wordprocessingml.document")``.
|
||||
or self.voice_chat_started(message)
|
||||
or self.voice_chat_ended(message)
|
||||
or self.voice_chat_participants_invited(message)
|
||||
or self.video_chat_scheduled(message)
|
||||
or self.video_chat_started(message)
|
||||
or self.video_chat_ended(message)
|
||||
or self.video_chat_participants_invited(message)
|
||||
or self.web_app_data(message)
|
||||
)
|
||||
|
||||
status_update = _StatusUpdate()
|
||||
@@ -1242,18 +1329,38 @@ officedocument.wordprocessingml.document")``.
|
||||
:attr:`telegram.Message.voice_chat_scheduled`.
|
||||
|
||||
.. versionadded:: 13.5
|
||||
.. deprecated:: 13.12
|
||||
voice_chat_started: Messages that contain
|
||||
:attr:`telegram.Message.voice_chat_started`.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
.. deprecated:: 13.12
|
||||
voice_chat_ended: Messages that contain
|
||||
:attr:`telegram.Message.voice_chat_ended`.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
.. deprecated:: 13.12
|
||||
voice_chat_participants_invited: Messages that contain
|
||||
:attr:`telegram.Message.voice_chat_participants_invited`.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
.. deprecated:: 13.12
|
||||
video_chat_scheduled: Messages that contain
|
||||
:attr:`telegram.Message.video_chat_scheduled`.
|
||||
|
||||
.. versionadded:: 13.12
|
||||
video_chat_started: Messages that contain
|
||||
:attr:`telegram.Message.video_chat_started`.
|
||||
|
||||
.. versionadded:: 13.12
|
||||
video_chat_ended: Messages that contain
|
||||
:attr:`telegram.Message.video_chat_ended`.
|
||||
|
||||
.. versionadded:: 13.12
|
||||
video_chat_participants_invited: Messages that contain
|
||||
:attr:`telegram.Message.video_chat_participants_invited`.
|
||||
|
||||
.. versionadded:: 13.12
|
||||
|
||||
"""
|
||||
|
||||
|
||||
@@ -45,7 +45,8 @@ class Handler(Generic[UT, CCT], ABC):
|
||||
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.
|
||||
https://github.com/python-telegram-bot/python-telegram-bot/wiki\
|
||||
/Transition-guide-to-Version-12.0 for more info.
|
||||
|
||||
Warning:
|
||||
When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom
|
||||
@@ -232,7 +233,8 @@ class Handler(Generic[UT, CCT], ABC):
|
||||
it should subclass this method, but remember to call this super method.
|
||||
|
||||
DEPRECATED: This method is being replaced by new context based callbacks. Please see
|
||||
https://git.io/fxJuV for more info.
|
||||
https://github.com/python-telegram-bot/python-telegram-bot/wiki\
|
||||
/Transition-guide-to-Version-12.0 for more info.
|
||||
|
||||
Args:
|
||||
dispatcher (:class:`telegram.ext.Dispatcher`): The dispatcher.
|
||||
|
||||
@@ -55,7 +55,8 @@ class InlineQueryHandler(Handler[Update, CCT]):
|
||||
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.
|
||||
https://github.com/python-telegram-bot/python-telegram-bot/wiki\
|
||||
/Transition-guide-to-Version-12.0 for more info.
|
||||
|
||||
Warning:
|
||||
* When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom
|
||||
|
||||
@@ -45,7 +45,8 @@ class MessageHandler(Handler[Update, CCT]):
|
||||
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.
|
||||
https://github.com/python-telegram-bot/python-telegram-bot/wiki\
|
||||
/Transition-guide-to-Version-12.0 for more info.
|
||||
|
||||
Warning:
|
||||
When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom
|
||||
@@ -155,7 +156,9 @@ class MessageHandler(Handler[Update, CCT]):
|
||||
self.filters = Filters.update
|
||||
if message_updates is not None:
|
||||
warnings.warn(
|
||||
'message_updates is deprecated. See https://git.io/fxJuV for more info',
|
||||
'message_updates is deprecated. See '
|
||||
'https://github.com/python-telegram-bot/python-telegram-bot/wiki/Transition'
|
||||
'-guide-to-Version-12.0 for more info',
|
||||
TelegramDeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
@@ -164,7 +167,10 @@ class MessageHandler(Handler[Update, CCT]):
|
||||
|
||||
if channel_post_updates is not None:
|
||||
warnings.warn(
|
||||
'channel_post_updates is deprecated. See https://git.io/fxJuV ' 'for more info',
|
||||
'channel_post_updates is deprecated. See '
|
||||
'https://github.com/python-telegram-bot/python-telegram-bot/wiki/Transition'
|
||||
'-guide-to-Version-12.0 '
|
||||
'for more info',
|
||||
TelegramDeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
@@ -173,7 +179,9 @@ class MessageHandler(Handler[Update, CCT]):
|
||||
|
||||
if edited_updates is not None:
|
||||
warnings.warn(
|
||||
'edited_updates is deprecated. See https://git.io/fxJuV for more info',
|
||||
'edited_updates is deprecated. See '
|
||||
'https://github.com/python-telegram-bot/python-telegram-bot/wiki/Transition'
|
||||
'-guide-to-Version-12.0 for more info',
|
||||
TelegramDeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
@@ -50,7 +50,8 @@ class DelayQueue(threading.Thread):
|
||||
|
||||
.. deprecated:: 13.3
|
||||
:class:`telegram.ext.DelayQueue` in its current form is deprecated and will be reinvented
|
||||
in a future release. See `this thread <https://git.io/JtDbF>`_ for a list of known bugs.
|
||||
in a future release. See `this thread <https://github.com/python-telegram-bot/\
|
||||
python-telegram-bot/issues/2139>`_ for a list of known bugs.
|
||||
|
||||
Args:
|
||||
queue (:obj:`Queue`, optional): Used to pass callbacks to thread. Creates ``Queue``
|
||||
@@ -92,7 +93,8 @@ class DelayQueue(threading.Thread):
|
||||
):
|
||||
warnings.warn(
|
||||
'DelayQueue in its current form is deprecated and will be reinvented in a future '
|
||||
'release. See https://git.io/JtDbF for a list of known bugs.',
|
||||
'release. See https://github.com/python-telegram-bot/python-telegram-bot/issues/2139 '
|
||||
'for a list of known bugs.',
|
||||
category=TelegramDeprecationWarning,
|
||||
)
|
||||
|
||||
@@ -194,7 +196,8 @@ class MessageQueue:
|
||||
|
||||
.. deprecated:: 13.3
|
||||
:class:`telegram.ext.MessageQueue` in its current form is deprecated and will be reinvented
|
||||
in a future release. See `this thread <https://git.io/JtDbF>`_ for a list of known bugs.
|
||||
in a future release. See `this thread <https://github.com/python-telegram-bot/\
|
||||
python-telegram-bot/issues/2139>`_ for a list of known bugs.
|
||||
|
||||
Args:
|
||||
all_burst_limit (:obj:`int`, optional): Number of maximum *all-type* callbacks to process
|
||||
@@ -226,7 +229,8 @@ class MessageQueue:
|
||||
):
|
||||
warnings.warn(
|
||||
'MessageQueue in its current form is deprecated and will be reinvented in a future '
|
||||
'release. See https://git.io/JtDbF for a list of known bugs.',
|
||||
'release. See https://github.com/python-telegram-bot/python-telegram-bot/issues/2139 '
|
||||
'for a list of known bugs.',
|
||||
category=TelegramDeprecationWarning,
|
||||
)
|
||||
|
||||
|
||||
@@ -35,7 +35,8 @@ class PollAnswerHandler(Handler[Update, CCT]):
|
||||
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.
|
||||
https://github.com/python-telegram-bot/python-telegram-bot/wiki\
|
||||
/Transition-guide-to-Version-12.0 for more info.
|
||||
|
||||
Warning:
|
||||
When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom
|
||||
|
||||
@@ -35,7 +35,8 @@ class PollHandler(Handler[Update, CCT]):
|
||||
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.
|
||||
https://github.com/python-telegram-bot/python-telegram-bot/wiki\
|
||||
/Transition-guide-to-Version-12.0 for more info.
|
||||
|
||||
Warning:
|
||||
When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom
|
||||
|
||||
@@ -35,7 +35,8 @@ class PreCheckoutQueryHandler(Handler[Update, CCT]):
|
||||
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.
|
||||
https://github.com/python-telegram-bot/python-telegram-bot/wiki\
|
||||
/Transition-guide-to-Version-12.0 for more info.
|
||||
|
||||
Warning:
|
||||
When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom
|
||||
|
||||
@@ -128,7 +128,9 @@ class RegexHandler(MessageHandler):
|
||||
run_async: Union[bool, DefaultValue] = DEFAULT_FALSE,
|
||||
):
|
||||
warnings.warn(
|
||||
'RegexHandler is deprecated. See https://git.io/fxJuV for more info',
|
||||
'RegexHandler is deprecated. See '
|
||||
'https://github.com/python-telegram-bot/python-telegram-bot/wiki/Transition-guide-to'
|
||||
'-Version-12.0 for more info',
|
||||
TelegramDeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
@@ -34,7 +34,8 @@ class ShippingQueryHandler(Handler[Update, CCT]):
|
||||
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.
|
||||
https://github.com/python-telegram-bot/python-telegram-bot/wiki\
|
||||
/Transition-guide-to-Version-12.0 for more info.
|
||||
|
||||
Warning:
|
||||
When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom
|
||||
|
||||
@@ -463,6 +463,11 @@ class Updater(Generic[CCT, UD, CD, BD]):
|
||||
application. Else, the webhook will be started on
|
||||
https://listen:port/url_path. Also calls :meth:`telegram.Bot.set_webhook` as required.
|
||||
|
||||
Note:
|
||||
``telegram.Bot.set_webhook.secret_token`` is not checked by this webhook
|
||||
implementation. If you want to use this new security parameter, either build your own
|
||||
webhook server or update your code to version 20.0a2+.
|
||||
|
||||
.. versionchanged:: 13.4
|
||||
:meth:`start_webhook` now *always* calls :meth:`telegram.Bot.set_webhook`, so pass
|
||||
``webhook_url`` instead of calling ``updater.bot.set_webhook(webhook_url)`` manually.
|
||||
|
||||
@@ -60,6 +60,10 @@ class Sticker(TelegramObject):
|
||||
position where the mask should be placed.
|
||||
file_size (:obj:`int`, optional): File size.
|
||||
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods.
|
||||
premium_animation (:class:`telegram.File`, optional): Premium animation for the sticker,
|
||||
if the sticker is premium.
|
||||
|
||||
.. versionadded:: 13.13
|
||||
**kwargs (obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
Attributes:
|
||||
@@ -80,6 +84,10 @@ class Sticker(TelegramObject):
|
||||
mask_position (:class:`telegram.MaskPosition`): Optional. For mask stickers, the position
|
||||
where the mask should be placed.
|
||||
file_size (:obj:`int`): Optional. File size.
|
||||
premium_animation (:class:`telegram.File`): Optional. Premium animation for the sticker,
|
||||
if the sticker is premium.
|
||||
|
||||
.. versionadded:: 13.13
|
||||
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
|
||||
|
||||
"""
|
||||
@@ -97,6 +105,7 @@ class Sticker(TelegramObject):
|
||||
'height',
|
||||
'file_unique_id',
|
||||
'emoji',
|
||||
'premium_animation',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
@@ -114,6 +123,7 @@ class Sticker(TelegramObject):
|
||||
set_name: str = None,
|
||||
mask_position: 'MaskPosition' = None,
|
||||
bot: 'Bot' = None,
|
||||
premium_animation: 'File' = None,
|
||||
**_kwargs: Any,
|
||||
):
|
||||
# Required
|
||||
@@ -130,12 +140,17 @@ class Sticker(TelegramObject):
|
||||
self.set_name = set_name
|
||||
self.mask_position = mask_position
|
||||
self.bot = bot
|
||||
self.premium_animation = premium_animation
|
||||
|
||||
self._id_attrs = (self.file_unique_id,)
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Sticker']:
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
# needs to be here to avoid circular imports
|
||||
# pylint: disable=import-outside-toplevel
|
||||
from telegram import File
|
||||
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
@@ -143,6 +158,7 @@ class Sticker(TelegramObject):
|
||||
|
||||
data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot)
|
||||
data['mask_position'] = MaskPosition.de_json(data.get('mask_position'), bot)
|
||||
data["premium_animation"] = File.de_json(data.get("premium_animation"), bot)
|
||||
|
||||
return cls(bot=bot, **data)
|
||||
|
||||
|
||||
@@ -17,11 +17,23 @@
|
||||
# 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 CallbackGame."""
|
||||
from typing import Optional, TYPE_CHECKING
|
||||
|
||||
from telegram import TelegramObject
|
||||
from telegram.utils.types import JSONDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Bot
|
||||
|
||||
|
||||
class CallbackGame(TelegramObject):
|
||||
"""A placeholder, currently holds no information. Use BotFather to set up your game."""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['CallbackGame']:
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
if data is None:
|
||||
return None
|
||||
return cls()
|
||||
|
||||
@@ -18,12 +18,13 @@
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents a Telegram InlineKeyboardButton."""
|
||||
|
||||
from typing import TYPE_CHECKING, Any
|
||||
from typing import TYPE_CHECKING, Any, Optional
|
||||
|
||||
from telegram import TelegramObject
|
||||
from telegram import TelegramObject, WebAppInfo, CallbackGame, LoginUrl
|
||||
from telegram.utils.types import JSONDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import CallbackGame, LoginUrl
|
||||
from telegram import Bot
|
||||
|
||||
|
||||
class InlineKeyboardButton(TelegramObject):
|
||||
@@ -50,11 +51,12 @@ class InlineKeyboardButton(TelegramObject):
|
||||
Older clients will display *unsupported message*.
|
||||
|
||||
Warning:
|
||||
If your bot allows your arbitrary callback data, buttons whose callback data is a
|
||||
non-hashable object will become unhashable. Trying to evaluate ``hash(button)`` will
|
||||
result in a :class:`TypeError`.
|
||||
* If your bot allows your arbitrary callback data, buttons whose callback data is a
|
||||
non-hashable object will become unhashable. Trying to evaluate ``hash(button)`` will
|
||||
result in a :class:`TypeError`.
|
||||
|
||||
.. versionchanged:: 13.6
|
||||
.. versionchanged:: 13.6
|
||||
* After Bot API 6.1, only ``HTTPS`` links will be allowed in :attr:`login_url`.
|
||||
|
||||
Args:
|
||||
text (:obj:`str`): Label text on the button.
|
||||
@@ -64,11 +66,21 @@ class InlineKeyboardButton(TelegramObject):
|
||||
|
||||
.. versionchanged:: 13.9
|
||||
You can now mention a user using ``tg://user?id=<user_id>``.
|
||||
login_url (:class:`telegram.LoginUrl`, optional): An HTTP URL used to automatically
|
||||
login_url (:class:`telegram.LoginUrl`, optional): An ``HTTPS`` URL used to automatically
|
||||
authorize the user. Can be used as a replacement for the Telegram Login Widget.
|
||||
|
||||
Caution:
|
||||
Only ``HTTPS`` links are allowed after Bot API 6.1.
|
||||
callback_data (:obj:`str` | :obj:`Any`, optional): Data to be sent in a callback query to
|
||||
the bot when button is pressed, UTF-8 1-64 bytes. If the bot instance allows arbitrary
|
||||
callback data, anything can be passed.
|
||||
web_app (:obj:`telegram.WebAppInfo`, optional): Description of the `Web App
|
||||
<https://core.telegram.org/bots/webapps>`_ that will be launched when the user presses
|
||||
the button. The Web App will be able to send an arbitrary message on behalf of the user
|
||||
using the method :meth:`~telegram.Bot.answer_web_app_query`. Available only in
|
||||
private chats between a user and the bot.
|
||||
|
||||
.. versionadded:: 13.12
|
||||
switch_inline_query (:obj:`str`, optional): If set, pressing the button will prompt the
|
||||
user to select one of their chats, open that chat and insert the bot's username and the
|
||||
specified inline query in the input field. Can be empty, in which case just the bot's
|
||||
@@ -97,10 +109,20 @@ class InlineKeyboardButton(TelegramObject):
|
||||
|
||||
.. versionchanged:: 13.9
|
||||
You can now mention a user using ``tg://user?id=<user_id>``.
|
||||
login_url (:class:`telegram.LoginUrl`): Optional. An HTTP URL used to automatically
|
||||
login_url (:class:`telegram.LoginUrl`): Optional. An ``HTTPS`` URL used to automatically
|
||||
authorize the user. Can be used as a replacement for the Telegram Login Widget.
|
||||
|
||||
Caution:
|
||||
Only ``HTTPS`` links are allowed after Bot API 6.1.
|
||||
callback_data (:obj:`str` | :obj:`object`): Optional. Data to be sent in a callback query
|
||||
to the bot when button is pressed, UTF-8 1-64 bytes.
|
||||
web_app (:obj:`telegram.WebAppInfo`): Optional. Description of the `Web App
|
||||
<https://core.telegram.org/bots/webapps>`_ that will be launched when the user presses
|
||||
the button. The Web App will be able to send an arbitrary message on behalf of the user
|
||||
using the method :meth:`~telegram.Bot.answer_web_app_query`. Available only in
|
||||
private chats between a user and the bot.
|
||||
|
||||
.. versionadded:: 13.12
|
||||
switch_inline_query (:obj:`str`): Optional. Will prompt the user to select one of their
|
||||
chats, open that chat and insert the bot's username and the specified inline query in
|
||||
the input field. Can be empty, in which case just the bot’s username will be inserted.
|
||||
@@ -123,6 +145,7 @@ class InlineKeyboardButton(TelegramObject):
|
||||
'text',
|
||||
'_id_attrs',
|
||||
'login_url',
|
||||
'web_app',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
@@ -132,9 +155,10 @@ class InlineKeyboardButton(TelegramObject):
|
||||
callback_data: object = None,
|
||||
switch_inline_query: str = None,
|
||||
switch_inline_query_current_chat: str = None,
|
||||
callback_game: 'CallbackGame' = None,
|
||||
callback_game: CallbackGame = None,
|
||||
pay: bool = None,
|
||||
login_url: 'LoginUrl' = None,
|
||||
login_url: LoginUrl = None,
|
||||
web_app: WebAppInfo = None,
|
||||
**_kwargs: Any,
|
||||
):
|
||||
# Required
|
||||
@@ -148,6 +172,7 @@ class InlineKeyboardButton(TelegramObject):
|
||||
self.switch_inline_query_current_chat = switch_inline_query_current_chat
|
||||
self.callback_game = callback_game
|
||||
self.pay = pay
|
||||
self.web_app = web_app
|
||||
self._id_attrs = ()
|
||||
self._set_id_attrs()
|
||||
|
||||
@@ -163,6 +188,20 @@ class InlineKeyboardButton(TelegramObject):
|
||||
self.pay,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['InlineKeyboardButton']:
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data['login_url'] = LoginUrl.de_json(data.get('login_url'), bot)
|
||||
data['web_app'] = WebAppInfo.de_json(data.get('web_app'), bot)
|
||||
data['callback_game'] = CallbackGame.de_json(data.get('callback_game'), bot)
|
||||
|
||||
return cls(**data)
|
||||
|
||||
def update_callback_data(self, callback_data: object) -> None:
|
||||
"""
|
||||
Sets :attr:`callback_data` to the passed object. Intended to be used by
|
||||
|
||||
@@ -18,9 +18,13 @@
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents a Telegram KeyboardButton."""
|
||||
|
||||
from typing import Any
|
||||
from typing import TYPE_CHECKING, Any, Optional
|
||||
|
||||
from telegram import TelegramObject, KeyboardButtonPollType
|
||||
from telegram import TelegramObject, KeyboardButtonPollType, WebAppInfo
|
||||
from telegram.utils.types import JSONDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Bot
|
||||
|
||||
|
||||
class KeyboardButton(TelegramObject):
|
||||
@@ -35,9 +39,11 @@ class KeyboardButton(TelegramObject):
|
||||
Note:
|
||||
* Optional fields are mutually exclusive.
|
||||
* :attr:`request_contact` and :attr:`request_location` options will only work in Telegram
|
||||
versions released after 9 April, 2016. Older clients will ignore them.
|
||||
versions released after 9 April, 2016. Older clients will display unsupported message.
|
||||
* :attr:`request_poll` option will only work in Telegram versions released after 23
|
||||
January, 2020. Older clients will receive unsupported message.
|
||||
* :attr:`web_app` option will only work in Telegram versions released after 16 April, 2022.
|
||||
Older clients will display unsupported message.
|
||||
|
||||
Args:
|
||||
text (:obj:`str`): Text of the button. If none of the optional fields are used, it will be
|
||||
@@ -49,16 +55,32 @@ class KeyboardButton(TelegramObject):
|
||||
request_poll (:class:`KeyboardButtonPollType`, optional): If specified, the user will be
|
||||
asked to create a poll and send it to the bot when the button is pressed. Available in
|
||||
private chats only.
|
||||
web_app (:class:`WebAppInfo`, optional): If specified, the described `Web App
|
||||
<https://core.telegram.org/bots/webapps>`_ will be launched when the button is pressed.
|
||||
The Web App will be able to send a :attr:`Message.web_app_data` service message.
|
||||
Available in private chats only.
|
||||
|
||||
.. versionadded:: 13.12
|
||||
|
||||
Attributes:
|
||||
text (:obj:`str`): Text of the button.
|
||||
request_contact (:obj:`bool`): Optional. The user's phone number will be sent.
|
||||
request_location (:obj:`bool`): Optional. The user's current location will be sent.
|
||||
request_poll (:class:`KeyboardButtonPollType`): Optional. If the user should create a poll.
|
||||
web_app (:class:`WebAppInfo`): Optional. If the described Web App will be launched when the
|
||||
button is pressed.
|
||||
|
||||
.. versionadded:: 13.12
|
||||
"""
|
||||
|
||||
__slots__ = ('request_location', 'request_contact', 'request_poll', 'text', '_id_attrs')
|
||||
__slots__ = (
|
||||
'request_location',
|
||||
'request_contact',
|
||||
'request_poll',
|
||||
'text',
|
||||
'web_app',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -66,6 +88,7 @@ class KeyboardButton(TelegramObject):
|
||||
request_contact: bool = None,
|
||||
request_location: bool = None,
|
||||
request_poll: KeyboardButtonPollType = None,
|
||||
web_app: WebAppInfo = None,
|
||||
**_kwargs: Any,
|
||||
):
|
||||
# Required
|
||||
@@ -74,6 +97,7 @@ class KeyboardButton(TelegramObject):
|
||||
self.request_contact = request_contact
|
||||
self.request_location = request_location
|
||||
self.request_poll = request_poll
|
||||
self.web_app = web_app
|
||||
|
||||
self._id_attrs = (
|
||||
self.text,
|
||||
@@ -81,3 +105,16 @@ class KeyboardButton(TelegramObject):
|
||||
self.request_location,
|
||||
self.request_poll,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['KeyboardButton']:
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data['request_poll'] = KeyboardButtonPollType.de_json(data.get('request_poll'), bot)
|
||||
data['web_app'] = WebAppInfo.de_json(data.get('web_app'), bot)
|
||||
|
||||
return cls(**data)
|
||||
|
||||
@@ -40,7 +40,7 @@ class LoginUrl(TelegramObject):
|
||||
`Checking authorization <https://core.telegram.org/widgets/login#checking-authorization>`_
|
||||
|
||||
Args:
|
||||
url (:obj:`str`): An HTTP URL to be opened with user authorization data added to the query
|
||||
url (:obj:`str`): An HTTPS URL to be opened with user authorization data added to the query
|
||||
string when the button is pressed. If the user refuses to provide authorization data,
|
||||
the original URL without information about the user will be opened. The data added is
|
||||
the same as described in
|
||||
@@ -60,7 +60,7 @@ class LoginUrl(TelegramObject):
|
||||
for your bot to send messages to the user.
|
||||
|
||||
Attributes:
|
||||
url (:obj:`str`): An HTTP URL to be opened with user authorization data.
|
||||
url (:obj:`str`): An HTTPS URL to be opened with user authorization data.
|
||||
forward_text (:obj:`str`): Optional. New text of the button in forwarded messages.
|
||||
bot_username (:obj:`str`): Optional. Username of a bot, which will be used for user
|
||||
authorization.
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=too-few-public-methods
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2022
|
||||
# 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 menu buttons."""
|
||||
from typing import Any, ClassVar, Optional, TYPE_CHECKING, Dict, Type
|
||||
|
||||
from telegram import TelegramObject, constants, WebAppInfo
|
||||
from telegram.utils.types import JSONDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Bot
|
||||
|
||||
|
||||
class MenuButton(TelegramObject):
|
||||
"""This object describes the bot's menu button in a private chat. It should be one of
|
||||
|
||||
* :class:`telegram.MenuButtonCommands`
|
||||
* :class:`telegram.MenuButtonWebApp`
|
||||
* :class:`telegram.MenuButtonDefault`
|
||||
|
||||
If a menu button other than :class:`telegram.MenuButtonDefault` is set for a private chat,
|
||||
then it is applied in the chat. Otherwise the default menu button is applied. By default, the
|
||||
menu button opens the list of bot commands.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`type` is equal. For subclasses with additional attributes,
|
||||
the notion of equality is overridden.
|
||||
|
||||
.. versionadded:: 13.12
|
||||
|
||||
Args:
|
||||
type (:obj:`str`): Type of menu button that the instance represents.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): Type of menu button that the instance represents.
|
||||
"""
|
||||
|
||||
__slots__ = ('type', '_id_attrs')
|
||||
|
||||
def __init__(self, type: str, **_kwargs: Any): # pylint: disable=redefined-builtin
|
||||
self.type = type
|
||||
|
||||
self._id_attrs = (self.type,)
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['MenuButton']:
|
||||
"""Converts JSON data to the appropriate :class:`MenuButton` object, i.e. takes
|
||||
care of selecting the correct subclass.
|
||||
|
||||
Args:
|
||||
data (Dict[:obj:`str`, ...]): The JSON data.
|
||||
bot (:class:`telegram.Bot`): The bot associated with this object.
|
||||
|
||||
Returns:
|
||||
The Telegram object.
|
||||
|
||||
"""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
_class_mapping: Dict[str, Type['MenuButton']] = {
|
||||
cls.COMMANDS: MenuButtonCommands,
|
||||
cls.WEB_APP: MenuButtonWebApp,
|
||||
cls.DEFAULT: MenuButtonDefault,
|
||||
}
|
||||
|
||||
if cls is MenuButton and data['type'] in _class_mapping:
|
||||
return _class_mapping[data['type']].de_json(data, bot=bot)
|
||||
return cls(**data, bot=bot)
|
||||
|
||||
COMMANDS: ClassVar[str] = constants.MENU_BUTTON_COMMANDS
|
||||
""":const:`telegram.constants.MENU_BUTTON_COMMANDS`"""
|
||||
WEB_APP: ClassVar[str] = constants.MENU_BUTTON_WEB_APP
|
||||
""":const:`telegram.constants.MENU_BUTTON_WEB_APP`"""
|
||||
DEFAULT: ClassVar[str] = constants.MENU_BUTTON_DEFAULT
|
||||
""":const:`telegram.constants.MENU_BUTTON_DEFAULT`"""
|
||||
|
||||
|
||||
class MenuButtonCommands(MenuButton):
|
||||
"""Represents a menu button, which opens the bot's list of commands.
|
||||
|
||||
.. versionadded:: 13.12
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): :const:`telegram.constants.MENU_BUTTON_COMMANDS`.
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
def __init__(self, **_kwargs: Any):
|
||||
super().__init__(type=constants.MENU_BUTTON_COMMANDS)
|
||||
|
||||
|
||||
class MenuButtonWebApp(MenuButton):
|
||||
"""Represents a menu button, which launches a
|
||||
`Web App <https://core.telegram.org/bots/webapps>`_.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`type`, :attr:`text` and :attr:`web_app`
|
||||
are equal.
|
||||
|
||||
.. versionadded:: 13.12
|
||||
|
||||
Args:
|
||||
text (:obj:`str`): Text of the button.
|
||||
web_app (:class:`telegram.WebAppInfo`): Description of the Web App that will be launched
|
||||
when the user presses the button. The Web App will be able to send an arbitrary
|
||||
message on behalf of the user using the method :meth:`~telegram.Bot.answerWebAppQuery`.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): :const:`telegram.constants.MENU_BUTTON_WEB_APP`.
|
||||
text (:obj:`str`): Text of the button.
|
||||
web_app (:class:`telegram.WebAppInfo`): Description of the Web App that will be launched
|
||||
when the user presses the button. The Web App will be able to send an arbitrary
|
||||
message on behalf of the user using the method :meth:`~telegram.Bot.answerWebAppQuery`.
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
'text',
|
||||
'web_app',
|
||||
)
|
||||
|
||||
def __init__(self, text: str, web_app: WebAppInfo, **_kwargs: Any):
|
||||
super().__init__(type=constants.MENU_BUTTON_WEB_APP)
|
||||
self.text = text
|
||||
self.web_app = web_app
|
||||
|
||||
self._id_attrs = (self.type, self.text, self.web_app)
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['MenuButtonWebApp']:
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data['web_app'] = WebAppInfo.de_json(data.get('web_app'), bot)
|
||||
|
||||
return cls(bot=bot, **data)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
data['web_app'] = self.web_app.to_dict()
|
||||
return data
|
||||
|
||||
|
||||
class MenuButtonDefault(MenuButton):
|
||||
"""Describes that no specific value for the menu button was set.
|
||||
|
||||
.. versionadded:: 13.12
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): :const:`telegram.constants.MENU_BUTTON_DEFAULT`.
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
def __init__(self, **_kwargs: Any):
|
||||
super().__init__(type=constants.MENU_BUTTON_DEFAULT)
|
||||
+129
-8
@@ -54,6 +54,11 @@ from telegram import (
|
||||
ReplyMarkup,
|
||||
MessageAutoDeleteTimerChanged,
|
||||
VoiceChatScheduled,
|
||||
VideoChatStarted,
|
||||
VideoChatEnded,
|
||||
VideoChatParticipantsInvited,
|
||||
WebAppData,
|
||||
VideoChatScheduled,
|
||||
)
|
||||
from telegram.utils.helpers import (
|
||||
escape_markdown,
|
||||
@@ -88,6 +93,9 @@ class Message(TelegramObject):
|
||||
Note:
|
||||
In Python ``from`` is a reserved word, use ``from_user`` instead.
|
||||
|
||||
.. versionchanged:: 13.12
|
||||
Since Bot API 6.0, voice chat was renamed to video chat.
|
||||
|
||||
Args:
|
||||
message_id (:obj:`int`): Unique message identifier inside this chat.
|
||||
from_user (:class:`telegram.User`, optional): Sender of the message; empty for messages
|
||||
@@ -218,18 +226,41 @@ class Message(TelegramObject):
|
||||
voice chat scheduled.
|
||||
|
||||
.. versionadded:: 13.5
|
||||
.. deprecated:: 13.12
|
||||
voice_chat_started (:class:`telegram.VoiceChatStarted`, optional): Service message: voice
|
||||
chat started.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
.. deprecated:: 13.12
|
||||
voice_chat_ended (:class:`telegram.VoiceChatEnded`, optional): Service message: voice chat
|
||||
ended.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
.. deprecated:: 13.12
|
||||
voice_chat_participants_invited (:class:`telegram.VoiceChatParticipantsInvited` optional):
|
||||
Service message: new participants invited to a voice chat.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
.. deprecated:: 13.12
|
||||
video_chat_scheduled (:class:`telegram.VideoChatScheduled`, optional): Service message:
|
||||
video chat scheduled.
|
||||
|
||||
.. versionadded:: 13.12
|
||||
video_chat_started (:class:`telegram.VideaChatStarted`, optional): Service message: video
|
||||
chat started.
|
||||
|
||||
.. versionadded:: 13.12
|
||||
video_chat_ended (:class:`telegram.VideaChatEnded`, optional): Service message: video chat
|
||||
ended.
|
||||
|
||||
.. versionadded:: 13.12
|
||||
video_chat_participants_invited (:class:`telegram.VideoChatParticipantsInvited` optional):
|
||||
Service message: new participants invited to a video chat.
|
||||
|
||||
.. versionadded:: 13.12
|
||||
web_app_data (:class:`telegram.WebAppData`, optional): Service message: data sent by a Web
|
||||
App.
|
||||
.. versionadded:: 13.12
|
||||
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.
|
||||
@@ -332,18 +363,46 @@ class Message(TelegramObject):
|
||||
voice chat scheduled.
|
||||
|
||||
.. versionadded:: 13.5
|
||||
.. deprecated:: 13.12 contains the same value as :attr:`VideoChatScheduled`
|
||||
for backwards compatibility.
|
||||
voice_chat_started (:class:`telegram.VoiceChatStarted`): Optional. Service message: voice
|
||||
chat started.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
.. deprecated:: 13.12 contains the same value as :attr:`VideoChatStarted`
|
||||
for backwards compatibility.
|
||||
voice_chat_ended (:class:`telegram.VoiceChatEnded`): Optional. Service message: voice chat
|
||||
ended.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
.. deprecated:: 13.12 contains the same value as :attr:`VideoChatEnded`
|
||||
for backwards compatibility.
|
||||
voice_chat_participants_invited (:class:`telegram.VoiceChatParticipantsInvited`): Optional.
|
||||
Service message: new participants invited to a voice chat.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
.. deprecated:: 13.12 contains the same value as :attr:`VideoChatParticipantsInvited`
|
||||
for backwards compatibility.
|
||||
video_chat_scheduled (:class:`telegram.VideoChatScheduled`): Optional. Service message:
|
||||
video chat scheduled.
|
||||
|
||||
.. versionadded:: 13.12
|
||||
video_chat_started (:class:`telegram.VideoChatStarted`): Optional. Service message: video
|
||||
chat started.
|
||||
|
||||
.. versionadded:: 13.12
|
||||
video_chat_ended (:class:`telegram.VideoChatEnded`): Optional. Service message: video chat
|
||||
ended.
|
||||
|
||||
.. versionadded:: 13.12
|
||||
video_chat_participants_invited (:class:`telegram.VideoChatParticipantsInvited`): Optional.
|
||||
Service message: new participants invited to a video chat.
|
||||
|
||||
.. versionadded:: 13.12
|
||||
web_app_data (:class:`telegram.WebAppData`): Optional. Service message: data sent by a Web
|
||||
App.
|
||||
|
||||
.. versionadded:: 13.12
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached
|
||||
to the message.
|
||||
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
|
||||
@@ -410,8 +469,13 @@ class Message(TelegramObject):
|
||||
'voice_chat_participants_invited',
|
||||
'voice_chat_started',
|
||||
'voice_chat_scheduled',
|
||||
'video_chat_ended',
|
||||
'video_chat_participants_invited',
|
||||
'video_chat_started',
|
||||
'video_chat_scheduled',
|
||||
'is_automatic_forward',
|
||||
'has_protected_content',
|
||||
'web_app_data',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
@@ -516,8 +580,36 @@ class Message(TelegramObject):
|
||||
voice_chat_scheduled: VoiceChatScheduled = None,
|
||||
is_automatic_forward: bool = None,
|
||||
has_protected_content: bool = None,
|
||||
video_chat_scheduled: VideoChatScheduled = None,
|
||||
video_chat_started: VideoChatStarted = None,
|
||||
video_chat_ended: VideoChatEnded = None,
|
||||
video_chat_participants_invited: VideoChatParticipantsInvited = None,
|
||||
web_app_data: WebAppData = None,
|
||||
**_kwargs: Any,
|
||||
):
|
||||
if (
|
||||
voice_chat_scheduled is not None
|
||||
and video_chat_scheduled is not None
|
||||
and voice_chat_scheduled != video_chat_scheduled
|
||||
):
|
||||
raise ValueError(
|
||||
"Only supply one of `video_chat_scheduled`/`voice_chat_scheduled`, not both."
|
||||
)
|
||||
if (
|
||||
voice_chat_ended is not None
|
||||
and video_chat_ended is not None
|
||||
and voice_chat_ended != video_chat_ended
|
||||
):
|
||||
raise ValueError("Only supply one of `video_chat_ended`/`voice_chat_ended`, not both.")
|
||||
if (
|
||||
voice_chat_participants_invited is not None
|
||||
and video_chat_participants_invited is not None
|
||||
) and voice_chat_participants_invited != video_chat_participants_invited:
|
||||
raise ValueError(
|
||||
"Only supply one of `video_chat_participants_invited`/"
|
||||
"`voice_chat_participants_invited`, not both."
|
||||
)
|
||||
|
||||
# Required
|
||||
self.message_id = int(message_id)
|
||||
# Optionals
|
||||
@@ -573,11 +665,30 @@ class Message(TelegramObject):
|
||||
self.dice = dice
|
||||
self.via_bot = via_bot
|
||||
self.proximity_alert_triggered = proximity_alert_triggered
|
||||
self.voice_chat_scheduled = voice_chat_scheduled
|
||||
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
|
||||
|
||||
temp0 = video_chat_scheduled if video_chat_scheduled is not None else voice_chat_scheduled
|
||||
self.voice_chat_scheduled = temp0
|
||||
self.video_chat_scheduled = temp0
|
||||
|
||||
# those are empty classes, so they don't need a comparison like the others.
|
||||
temp1 = video_chat_started if video_chat_started is not None else voice_chat_started
|
||||
self.voice_chat_started = temp1
|
||||
self.video_chat_started = temp1
|
||||
|
||||
temp2 = video_chat_ended if video_chat_ended is not None else voice_chat_ended
|
||||
self.voice_chat_ended = temp2
|
||||
self.video_chat_ended = temp2
|
||||
|
||||
temp3 = (
|
||||
video_chat_participants_invited
|
||||
if video_chat_participants_invited is not None
|
||||
else voice_chat_participants_invited
|
||||
)
|
||||
self.voice_chat_participants_invited = temp3
|
||||
self.video_chat_participants_invited = temp3
|
||||
self.web_app_data = web_app_data
|
||||
|
||||
self.bot = bot
|
||||
|
||||
self._effective_attachment = DEFAULT_NONE
|
||||
@@ -651,14 +762,24 @@ class Message(TelegramObject):
|
||||
data.get('proximity_alert_triggered'), bot
|
||||
)
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
|
||||
data['voice_chat_scheduled'] = VoiceChatScheduled.de_json(
|
||||
data['voice_chat_scheduled'] = VideoChatScheduled.de_json(
|
||||
data.get('voice_chat_scheduled'), 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['voice_chat_started'] = VideoChatStarted.de_json(data.get('voice_chat_started'), bot)
|
||||
data['voice_chat_ended'] = VideoChatEnded.de_json(data.get('voice_chat_ended'), bot)
|
||||
data['voice_chat_participants_invited'] = VideoChatParticipantsInvited.de_json(
|
||||
data.get('voice_chat_participants_invited'), bot
|
||||
)
|
||||
data['video_chat_scheduled'] = VideoChatScheduled.de_json(
|
||||
data.get('video_chat_scheduled'), bot
|
||||
)
|
||||
data['video_chat_started'] = VideoChatStarted.de_json(data.get('video_chat_started'), bot)
|
||||
data['video_chat_ended'] = VideoChatEnded.de_json(data.get('video_chat_ended'), bot)
|
||||
data['video_chat_participants_invited'] = VideoChatParticipantsInvited.de_json(
|
||||
data.get('video_chat_participants_invited'), bot
|
||||
)
|
||||
data['web_app_data'] = WebAppData.de_json(data.get('web_app_data'), bot)
|
||||
|
||||
return cls(bot=bot, **data)
|
||||
|
||||
@property
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=too-many-instance-attributes, too-many-arguments
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2022
|
||||
# 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 Sent Web App Message."""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from telegram import TelegramObject
|
||||
|
||||
|
||||
class SentWebAppMessage(TelegramObject):
|
||||
"""Contains information about an inline message sent by a Web App on behalf of a user.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`inline_message_id` are equal.
|
||||
|
||||
.. versionadded:: 13.12
|
||||
|
||||
Args:
|
||||
inline_message_id (:obj:`str`, optional): Identifier of the sent inline message. Available
|
||||
only if there is an :attr:`inline keyboard <telegram.InlineKeyboardMarkup>` attached to
|
||||
the message.
|
||||
|
||||
Attributes:
|
||||
inline_message_id (:obj:`str`): Optional. Identifier of the sent inline message. Available
|
||||
only if there is an :attr:`inline keyboard <telegram.InlineKeyboardMarkup>` attached to
|
||||
the message.
|
||||
"""
|
||||
|
||||
__slots__ = ('inline_message_id',)
|
||||
|
||||
def __init__(self, inline_message_id: str = None, **_kwargs: Any):
|
||||
# Optionals
|
||||
self.inline_message_id = inline_message_id
|
||||
|
||||
self._id_attrs = (self.inline_message_id,)
|
||||
+74
-1
@@ -21,7 +21,7 @@
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING, Any, List, Optional, Union, Tuple
|
||||
|
||||
from telegram import TelegramObject, constants
|
||||
from telegram import TelegramObject, constants, MenuButton
|
||||
from telegram.inline.inlinekeyboardbutton import InlineKeyboardButton
|
||||
from telegram.utils.helpers import (
|
||||
mention_html as util_mention_html,
|
||||
@@ -79,6 +79,13 @@ class User(TelegramObject):
|
||||
supports_inline_queries (:obj:`str`, optional): :obj:`True`, if the bot supports inline
|
||||
queries. Returned only in :attr:`telegram.Bot.get_me` requests.
|
||||
bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods.
|
||||
is_premium (:obj:`bool`, optional): :obj:`True`, if this user is a Telegram Premium user.
|
||||
|
||||
.. versionadded:: 13.13
|
||||
added_to_attachment_menu (:obj:`bool`, optional): :obj:`True`, if this user added
|
||||
the bot to the attachment menu.
|
||||
|
||||
.. versionadded:: 13.13
|
||||
|
||||
Attributes:
|
||||
id (:obj:`int`): Unique identifier for this user or bot.
|
||||
@@ -94,6 +101,14 @@ class User(TelegramObject):
|
||||
supports_inline_queries (:obj:`str`): Optional. :obj:`True`, if the bot supports inline
|
||||
queries. Returned only in :attr:`telegram.Bot.get_me` requests.
|
||||
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
|
||||
is_premium (:obj:`bool`): Optional. :obj:`True`, if this user is a Telegram
|
||||
Premium user.
|
||||
|
||||
.. versionadded:: 13.13
|
||||
added_to_attachment_menu (:obj:`bool`): Optional. :obj:`True`, if this user added
|
||||
the bot to the attachment menu.
|
||||
|
||||
.. versionadded:: 13.13
|
||||
|
||||
"""
|
||||
|
||||
@@ -108,6 +123,8 @@ class User(TelegramObject):
|
||||
'id',
|
||||
'bot',
|
||||
'language_code',
|
||||
'is_premium',
|
||||
'added_to_attachment_menu',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
@@ -123,6 +140,8 @@ class User(TelegramObject):
|
||||
can_read_all_group_messages: bool = None,
|
||||
supports_inline_queries: bool = None,
|
||||
bot: 'Bot' = None,
|
||||
is_premium: bool = None,
|
||||
added_to_attachment_menu: bool = None,
|
||||
**_kwargs: Any,
|
||||
):
|
||||
# Required
|
||||
@@ -136,6 +155,8 @@ class User(TelegramObject):
|
||||
self.can_join_groups = can_join_groups
|
||||
self.can_read_all_group_messages = can_read_all_group_messages
|
||||
self.supports_inline_queries = supports_inline_queries
|
||||
self.is_premium = is_premium
|
||||
self.added_to_attachment_menu = added_to_attachment_menu
|
||||
self.bot = bot
|
||||
|
||||
self._id_attrs = (self.id,)
|
||||
@@ -1241,3 +1262,55 @@ class User(TelegramObject):
|
||||
return self.bot.decline_chat_join_request(
|
||||
user_id=self.id, chat_id=chat_id, timeout=timeout, api_kwargs=api_kwargs
|
||||
)
|
||||
|
||||
def set_menu_button(
|
||||
self,
|
||||
menu_button: MenuButton = None,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
) -> bool:
|
||||
"""Shortcut for::
|
||||
|
||||
bot.set_chat_menu_button(chat_id=update.effective_user.id, *args, **kwargs)
|
||||
|
||||
For the documentation of the arguments, please see
|
||||
:meth:`telegram.Bot.set_chat_menu_button`.
|
||||
|
||||
..seealso:: :meth:`get_menu_button`
|
||||
|
||||
.. versionadded:: 13.12
|
||||
|
||||
Returns:
|
||||
:obj:`bool`: On success, :obj:`True` is returned.
|
||||
"""
|
||||
return self.bot.set_chat_menu_button(
|
||||
chat_id=self.id,
|
||||
menu_button=menu_button,
|
||||
timeout=timeout,
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
|
||||
def get_menu_button(
|
||||
self,
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
) -> MenuButton:
|
||||
"""Shortcut for::
|
||||
|
||||
bot.get_chat_menu_button(chat_id=update.effective_user.id, *args, **kwargs)
|
||||
|
||||
For the documentation of the arguments, please see
|
||||
:meth:`telegram.Bot.get_chat_menu_button`.
|
||||
|
||||
..seealso:: :meth:`set_menu_button`
|
||||
|
||||
.. versionadded:: 13.12
|
||||
|
||||
Returns:
|
||||
:class:`telegram.MenuButton`: On success, the current menu button is returned.
|
||||
"""
|
||||
return self.bot.get_chat_menu_button(
|
||||
chat_id=self.id,
|
||||
timeout=timeout,
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
|
||||
+1
-1
@@ -20,5 +20,5 @@
|
||||
|
||||
from telegram import constants
|
||||
|
||||
__version__ = '13.11'
|
||||
__version__ = '13.13'
|
||||
bot_api_version = constants.BOT_API_VERSION # pylint: disable=C0103
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=R0903
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2022
|
||||
# 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 video chats."""
|
||||
|
||||
import datetime as dtm
|
||||
from typing import TYPE_CHECKING, Any, Optional, List
|
||||
|
||||
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 VideoChatStarted(TelegramObject):
|
||||
"""
|
||||
This object represents a service message about a video
|
||||
chat started in the chat. Currently holds no information.
|
||||
|
||||
.. versionadded:: 13.12
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
def __init__(self, **_kwargs: Any): # skipcq: PTC-W0049
|
||||
pass
|
||||
|
||||
|
||||
class VideoChatEnded(TelegramObject):
|
||||
"""
|
||||
This object represents a service message about a
|
||||
video 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.12
|
||||
|
||||
Args:
|
||||
duration (:obj:`int`): Voice chat duration in seconds.
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
Attributes:
|
||||
duration (:obj:`int`): Voice chat duration in seconds.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ('duration', '_id_attrs')
|
||||
|
||||
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 VideoChatParticipantsInvited(TelegramObject):
|
||||
"""
|
||||
This object represents a service message about new members invited to a video 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.12
|
||||
|
||||
Args:
|
||||
users (List[:class:`telegram.User`]): New members that were invited to the video chat.
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
Attributes:
|
||||
users (List[:class:`telegram.User`]): New members that were invited to the video chat.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ('users', '_id_attrs')
|
||||
|
||||
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['VideoChatParticipantsInvited']:
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
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:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
|
||||
data["users"] = [u.to_dict() for u in self.users]
|
||||
return data
|
||||
|
||||
|
||||
class VideoChatScheduled(TelegramObject):
|
||||
"""This object represents a service message about a video chat scheduled in the chat.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`start_date` are equal.
|
||||
|
||||
.. versionadded:: 13.12
|
||||
|
||||
Args:
|
||||
start_date (:obj:`datetime.datetime`): Point in time (Unix timestamp) when the video
|
||||
chat is supposed to be started by a chat administrator
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
Attributes:
|
||||
start_date (:obj:`datetime.datetime`): Point in time (Unix timestamp) when the video
|
||||
chat is supposed to be started by a chat administrator
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ('start_date', '_id_attrs')
|
||||
|
||||
def __init__(self, start_date: dtm.datetime, **_kwargs: Any) -> None:
|
||||
self.start_date = start_date
|
||||
|
||||
self._id_attrs = (self.start_date,)
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['VideoChatScheduled']:
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data['start_date'] = from_timestamp(data['start_date'])
|
||||
|
||||
return cls(**data, bot=bot)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
|
||||
# Required
|
||||
data['start_date'] = to_timestamp(self.start_date)
|
||||
|
||||
return data
|
||||
+10
-144
@@ -19,151 +19,17 @@
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains objects related to Telegram voice chats."""
|
||||
|
||||
import datetime as dtm
|
||||
from typing import TYPE_CHECKING, Any, Optional, List
|
||||
from telegram.videochat import (
|
||||
VideoChatStarted,
|
||||
VideoChatEnded,
|
||||
VideoChatScheduled,
|
||||
VideoChatParticipantsInvited,
|
||||
)
|
||||
|
||||
from telegram import TelegramObject, User
|
||||
from telegram.utils.helpers import from_timestamp, to_timestamp
|
||||
from telegram.utils.types import JSONDict
|
||||
VoiceChatStarted = VideoChatStarted
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Bot
|
||||
VoiceChatEnded = VideoChatEnded
|
||||
|
||||
VoiceChatParticipantsInvited = VideoChatParticipantsInvited
|
||||
|
||||
class VoiceChatStarted(TelegramObject):
|
||||
"""
|
||||
This object represents a service message about a voice
|
||||
chat started in the chat. Currently holds no information.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
def __init__(self, **_kwargs: Any): # skipcq: PTC-W0049
|
||||
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.
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
Attributes:
|
||||
duration (:obj:`int`): Voice chat duration in seconds.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ('duration', '_id_attrs')
|
||||
|
||||
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.
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
Attributes:
|
||||
users (List[:class:`telegram.User`]): New members that
|
||||
were invited to the voice chat.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ('users', '_id_attrs')
|
||||
|
||||
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']:
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
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:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
|
||||
data["users"] = [u.to_dict() for u in self.users]
|
||||
return data
|
||||
|
||||
|
||||
class VoiceChatScheduled(TelegramObject):
|
||||
"""This object represents a service message about a voice chat scheduled in the chat.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`start_date` are equal.
|
||||
|
||||
Args:
|
||||
start_date (:obj:`datetime.datetime`): Point in time (Unix timestamp) when the voice
|
||||
chat is supposed to be started by a chat administrator
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
Attributes:
|
||||
start_date (:obj:`datetime.datetime`): Point in time (Unix timestamp) when the voice
|
||||
chat is supposed to be started by a chat administrator
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ('start_date', '_id_attrs')
|
||||
|
||||
def __init__(self, start_date: dtm.datetime, **_kwargs: Any) -> None:
|
||||
self.start_date = start_date
|
||||
|
||||
self._id_attrs = (self.start_date,)
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['VoiceChatScheduled']:
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data['start_date'] = from_timestamp(data['start_date'])
|
||||
|
||||
return cls(**data, bot=bot)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
|
||||
# Required
|
||||
data['start_date'] = to_timestamp(self.start_date)
|
||||
|
||||
return data
|
||||
VoiceChatScheduled = VideoChatScheduled
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=too-many-instance-attributes, too-many-arguments
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2022
|
||||
# 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 WebAppData."""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from telegram import TelegramObject
|
||||
|
||||
|
||||
class WebAppData(TelegramObject):
|
||||
"""Contains data sent from a `Web App <https://core.telegram.org/bots/webapps>`_ to the bot.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`data` and :attr:`button_text` are equal.
|
||||
|
||||
.. versionadded:: 13.12
|
||||
|
||||
Args:
|
||||
data (:obj:`str`): The data. Be aware that a bad client can send arbitrary data in this
|
||||
field.
|
||||
button_text (:obj:`str`): Text of the :attr:`~telegram.KeyboardButton.web_app` keyboard
|
||||
button, from which the Web App was opened.
|
||||
|
||||
Attributes:
|
||||
data (:obj:`str`): The data. Be aware that a bad client can send arbitrary data in this
|
||||
field.
|
||||
button_text (:obj:`str`): Text of the :attr:`~telegram.KeyboardButton.web_app` keyboard
|
||||
button, from which the Web App was opened.
|
||||
|
||||
Warning:
|
||||
Be aware that a bad client can send arbitrary data in this field.
|
||||
"""
|
||||
|
||||
__slots__ = ('data', 'button_text', '_id_attrs')
|
||||
|
||||
def __init__(self, data: str, button_text: str, **_kwargs: Any):
|
||||
# Required
|
||||
self.data = data
|
||||
self.button_text = button_text
|
||||
|
||||
self._id_attrs = (self.data, self.button_text)
|
||||
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=too-many-instance-attributes, too-many-arguments
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2022
|
||||
# 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 Web App Info."""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from telegram import TelegramObject
|
||||
|
||||
|
||||
class WebAppInfo(TelegramObject):
|
||||
"""
|
||||
This object contains information about a `Web App <https://core.telegram.org/bots/webapps>`_.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`url` are equal.
|
||||
|
||||
.. versionadded:: 13.12
|
||||
|
||||
Args:
|
||||
url (:obj:`str`): An HTTPS URL of a Web App to be opened with additional data as specified
|
||||
in `Initializing Web Apps \
|
||||
<https://core.telegram.org/bots/webapps#initializing-web-apps>`_.
|
||||
|
||||
Attributes:
|
||||
url (:obj:`str`): An HTTPS URL of a Web App to be opened with additional data as specified
|
||||
in `Initializing Web Apps \
|
||||
<https://core.telegram.org/bots/webapps#initializing-web-apps>`_.
|
||||
"""
|
||||
|
||||
__slots__ = ('url', '_id_attrs')
|
||||
|
||||
def __init__(self, url: str, **_kwargs: Any):
|
||||
# Required
|
||||
self.url = url
|
||||
self._id_attrs = (url,)
|
||||
+32
-1
@@ -18,9 +18,14 @@
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents a Telegram WebhookInfo."""
|
||||
|
||||
from typing import Any, List
|
||||
from typing import Any, List, Optional, TYPE_CHECKING
|
||||
|
||||
from telegram import TelegramObject
|
||||
from telegram.utils.types import JSONDict
|
||||
from telegram.utils.helpers import from_timestamp
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Bot
|
||||
|
||||
|
||||
class WebhookInfo(TelegramObject):
|
||||
@@ -47,6 +52,10 @@ class WebhookInfo(TelegramObject):
|
||||
connections to the webhook for update delivery.
|
||||
allowed_updates (List[:obj:`str`], optional): A list of update types the bot is subscribed
|
||||
to. Defaults to all update types, except :attr:`telegram.Update.chat_member`.
|
||||
last_synchronization_error_date (:obj:`int`, optional): Unix time of the most recent error
|
||||
that happened when trying to synchronize available updates with Telegram datacenters.
|
||||
|
||||
.. versionadded:: 13.12
|
||||
|
||||
Attributes:
|
||||
url (:obj:`str`): Webhook URL.
|
||||
@@ -59,6 +68,10 @@ class WebhookInfo(TelegramObject):
|
||||
connections.
|
||||
allowed_updates (List[:obj:`str`]): Optional. A list of update types the bot is subscribed
|
||||
to. Defaults to all update types, except :attr:`telegram.Update.chat_member`.
|
||||
last_synchronization_error_date (:obj:`int`): Optional. Unix time of the most recent error
|
||||
that happened when trying to synchronize available updates with Telegram datacenters.
|
||||
|
||||
.. versionadded:: 13.12
|
||||
|
||||
"""
|
||||
|
||||
@@ -71,6 +84,7 @@ class WebhookInfo(TelegramObject):
|
||||
'last_error_message',
|
||||
'pending_update_count',
|
||||
'has_custom_certificate',
|
||||
'last_synchronization_error_date',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
@@ -84,6 +98,7 @@ class WebhookInfo(TelegramObject):
|
||||
max_connections: int = None,
|
||||
allowed_updates: List[str] = None,
|
||||
ip_address: str = None,
|
||||
last_synchronization_error_date: int = None,
|
||||
**_kwargs: Any,
|
||||
):
|
||||
# Required
|
||||
@@ -97,6 +112,7 @@ class WebhookInfo(TelegramObject):
|
||||
self.last_error_message = last_error_message
|
||||
self.max_connections = max_connections
|
||||
self.allowed_updates = allowed_updates
|
||||
self.last_synchronization_error_date = last_synchronization_error_date
|
||||
|
||||
self._id_attrs = (
|
||||
self.url,
|
||||
@@ -108,3 +124,18 @@ class WebhookInfo(TelegramObject):
|
||||
self.max_connections,
|
||||
self.allowed_updates,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['WebhookInfo']:
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data['last_error_date'] = from_timestamp(data.get('last_error_date'))
|
||||
data['last_synchronization_error_date'] = from_timestamp(
|
||||
data.get('last_synchronization_error_date')
|
||||
)
|
||||
|
||||
return cls(bot=bot, **data)
|
||||
|
||||
+166
-2
@@ -27,6 +27,7 @@ import pytest
|
||||
import pytz
|
||||
from flaky import flaky
|
||||
|
||||
from telegram import constants
|
||||
from telegram import (
|
||||
Bot,
|
||||
Update,
|
||||
@@ -52,6 +53,13 @@ from telegram import (
|
||||
InlineQueryResultVoice,
|
||||
PollOption,
|
||||
BotCommandScopeChat,
|
||||
SentWebAppMessage,
|
||||
ChatAdministratorRights,
|
||||
MenuButton,
|
||||
MenuButtonWebApp,
|
||||
WebAppInfo,
|
||||
MenuButtonCommands,
|
||||
MenuButtonDefault,
|
||||
)
|
||||
from telegram.constants import MAX_INLINE_QUERY_RESULTS
|
||||
from telegram.ext import ExtBot, Defaults
|
||||
@@ -752,6 +760,27 @@ class TestBot:
|
||||
with pytest.raises(BadRequest, match='Wrong parameter action'):
|
||||
bot.send_chat_action(chat_id, 'unknown action')
|
||||
|
||||
def test_answer_web_app_query(self, bot, monkeypatch):
|
||||
params = False
|
||||
|
||||
# For now just test that our internals pass the correct data
|
||||
|
||||
def make_assertion(url, data, *args, **kwargs):
|
||||
nonlocal params
|
||||
params = data == {
|
||||
'web_app_query_id': '12345',
|
||||
'result': result,
|
||||
}
|
||||
web_app_msg = SentWebAppMessage('321').to_dict()
|
||||
return web_app_msg
|
||||
|
||||
monkeypatch.setattr(bot.request, 'post', make_assertion)
|
||||
result = InlineQueryResultArticle('1', 'title', InputTextMessageContent('text'))
|
||||
web_app_msg = bot.answer_web_app_query('12345', result)
|
||||
assert params, "something went wrong with passing arguments to the request"
|
||||
assert isinstance(web_app_msg, SentWebAppMessage)
|
||||
assert web_app_msg.inline_message_id == '321'
|
||||
|
||||
# TODO: Needs improvement. We need incoming inline query to test answer.
|
||||
def test_answer_inline_query(self, monkeypatch, bot):
|
||||
# For now just test that our internals pass the correct data
|
||||
@@ -1381,6 +1410,32 @@ class TestBot:
|
||||
assert bot.set_webhook(drop_pending_updates=drop_pending_updates)
|
||||
assert bot.delete_webhook(drop_pending_updates=drop_pending_updates)
|
||||
|
||||
def test_set_webhook_params(self, bot, monkeypatch):
|
||||
# actually making calls to TG is done in
|
||||
# test_set_webhook_get_webhook_info_and_delete_webhook. Sadly secret_token can't be tested
|
||||
# there so we have this function \o/
|
||||
def make_assertion(*args, **_):
|
||||
kwargs = args[1]
|
||||
return (
|
||||
kwargs["url"] == "example.com"
|
||||
and kwargs["max_connections"] == 7
|
||||
and kwargs["allowed_updates"] == ["messages"]
|
||||
and kwargs["ip_address"] == "127.0.0.1"
|
||||
and kwargs["drop_pending_updates"]
|
||||
and kwargs["secret_token"] == "SoSecretToken"
|
||||
)
|
||||
|
||||
monkeypatch.setattr(bot, "_post", make_assertion)
|
||||
|
||||
assert bot.set_webhook(
|
||||
"example.com",
|
||||
max_connections=7,
|
||||
allowed_updates=["messages"],
|
||||
ip_address="127.0.0.1",
|
||||
drop_pending_updates=True,
|
||||
secret_token="SoSecretToken",
|
||||
)
|
||||
|
||||
@flaky(3, 1)
|
||||
def test_leave_chat(self, bot):
|
||||
with pytest.raises(BadRequest, match='Chat not found'):
|
||||
@@ -1701,6 +1756,28 @@ class TestBot:
|
||||
can_manage_voice_chats=True,
|
||||
)
|
||||
|
||||
with pytest.raises(
|
||||
ValueError,
|
||||
match='Only supply one of `can_manage_video_chats`/`'
|
||||
'can_manage_voice_chats`, not both.',
|
||||
):
|
||||
assert bot.promote_chat_member(
|
||||
channel_id,
|
||||
95205500,
|
||||
is_anonymous=True,
|
||||
can_change_info=True,
|
||||
can_post_messages=True,
|
||||
can_edit_messages=True,
|
||||
can_delete_messages=True,
|
||||
can_invite_users=True,
|
||||
can_restrict_members=True,
|
||||
can_pin_messages=True,
|
||||
can_promote_members=True,
|
||||
can_manage_chat=True,
|
||||
can_manage_voice_chats=True,
|
||||
can_manage_video_chats=True,
|
||||
)
|
||||
|
||||
# Test that we pass the correct params to TG
|
||||
def make_assertion(*args, **_):
|
||||
data = args[1]
|
||||
@@ -1717,7 +1794,7 @@ class TestBot:
|
||||
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
|
||||
and data.get('can_manage_video_chats') == 11
|
||||
)
|
||||
|
||||
monkeypatch.setattr(bot, '_post', make_assertion)
|
||||
@@ -1737,6 +1814,42 @@ class TestBot:
|
||||
can_manage_voice_chats=11,
|
||||
)
|
||||
|
||||
# Test that video_chats also works
|
||||
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_video_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_video_chats=11,
|
||||
)
|
||||
|
||||
@flaky(3, 1)
|
||||
def test_export_chat_invite_link(self, bot, channel_id):
|
||||
# Each link is unique apparently
|
||||
@@ -1886,7 +1999,10 @@ class TestBot:
|
||||
# TODO: Need incoming join request to properly test
|
||||
# Since we can't create join requests on the fly, we just tests the call to TG
|
||||
# by checking that it complains about declining a user who is already in the chat
|
||||
with pytest.raises(BadRequest, match='User_already_participant'):
|
||||
#
|
||||
# The error message Hide_requester_missing started showing up instead of
|
||||
# User_already_participant. Don't know why …
|
||||
with pytest.raises(BadRequest, match="User_already_participant|Hide_requester_missing"):
|
||||
bot.decline_chat_join_request(chat_id=channel_id, user_id=chat_id)
|
||||
|
||||
@flaky(3, 1)
|
||||
@@ -2068,6 +2184,54 @@ class TestBot:
|
||||
chat_id, 'test', reply_to_message_id=reply_to_message.message_id
|
||||
)
|
||||
|
||||
def test_get_set_my_default_administrator_rights(self, bot):
|
||||
# Test that my default administrator rights for group are as all False
|
||||
bot.set_my_default_administrator_rights()
|
||||
my_admin_rights_grp = bot.get_my_default_administrator_rights()
|
||||
assert isinstance(my_admin_rights_grp, ChatAdministratorRights)
|
||||
for attribute in my_admin_rights_grp.__slots__:
|
||||
if attribute == "_id_attrs":
|
||||
continue
|
||||
assert not getattr(my_admin_rights_grp, attribute)
|
||||
|
||||
# Test setting my default admin rights for channel
|
||||
my_rights = ChatAdministratorRights.all_rights()
|
||||
bot.set_my_default_administrator_rights(my_rights, for_channels=True)
|
||||
my_admin_rights_ch = bot.get_my_default_administrator_rights(for_channels=True)
|
||||
# tg bug? is_anonymous is False despite setting it True for channels:
|
||||
assert my_admin_rights_ch.is_anonymous is not my_rights.is_anonymous
|
||||
|
||||
assert my_admin_rights_ch.can_invite_users is my_rights.can_invite_users
|
||||
assert my_admin_rights_ch.can_manage_chat is my_rights.can_manage_chat
|
||||
assert my_admin_rights_ch.can_delete_messages is my_rights.can_delete_messages
|
||||
assert my_admin_rights_ch.can_edit_messages is my_rights.can_edit_messages
|
||||
assert my_admin_rights_ch.can_post_messages is my_rights.can_post_messages
|
||||
assert my_admin_rights_ch.can_change_info is my_rights.can_change_info
|
||||
assert my_admin_rights_ch.can_promote_members is my_rights.can_promote_members
|
||||
assert my_admin_rights_ch.can_restrict_members is my_rights.can_restrict_members
|
||||
assert my_admin_rights_ch.can_pin_messages is None # Not returned for channels
|
||||
|
||||
def test_get_set_chat_menu_button(self, bot, chat_id):
|
||||
# Test our chat menu button is commands-
|
||||
menu_button = bot.get_chat_menu_button()
|
||||
assert isinstance(menu_button, MenuButton)
|
||||
assert isinstance(menu_button, MenuButtonCommands)
|
||||
assert menu_button.type == constants.MENU_BUTTON_COMMANDS
|
||||
|
||||
# Test setting our chat menu button to Webapp.
|
||||
my_menu = MenuButtonWebApp('click me!', WebAppInfo('https://telegram.org/'))
|
||||
|
||||
bot.set_chat_menu_button(chat_id, my_menu)
|
||||
menu_button = bot.get_chat_menu_button(chat_id)
|
||||
assert isinstance(menu_button, MenuButtonWebApp)
|
||||
assert menu_button.type == constants.MENU_BUTTON_WEB_APP
|
||||
assert menu_button.text == my_menu.text
|
||||
assert menu_button.web_app.url == my_menu.web_app.url
|
||||
|
||||
bot.set_chat_menu_button(chat_id=chat_id, menu_button=MenuButtonDefault())
|
||||
menu_button = bot.get_chat_menu_button(chat_id=chat_id)
|
||||
assert isinstance(menu_button, MenuButtonDefault)
|
||||
|
||||
@flaky(3, 1)
|
||||
def test_set_and_get_my_commands(self, bot):
|
||||
commands = [
|
||||
|
||||
@@ -43,6 +43,8 @@ def chat(bot):
|
||||
location=TestChat.location,
|
||||
has_private_forwards=True,
|
||||
has_protected_content=True,
|
||||
join_to_send_messages=True,
|
||||
join_by_request=True,
|
||||
)
|
||||
|
||||
|
||||
@@ -66,6 +68,8 @@ class TestChat:
|
||||
location = ChatLocation(Location(123, 456), 'Barbie World')
|
||||
has_protected_content = True
|
||||
has_private_forwards = True
|
||||
join_to_send_messages = True
|
||||
join_by_request = True
|
||||
|
||||
def test_slot_behaviour(self, chat, recwarn, mro_slots):
|
||||
for attr in chat.__slots__:
|
||||
@@ -92,6 +96,8 @@ class TestChat:
|
||||
'has_private_forwards': self.has_private_forwards,
|
||||
'linked_chat_id': self.linked_chat_id,
|
||||
'location': self.location.to_dict(),
|
||||
'join_to_send_messages': self.join_to_send_messages,
|
||||
'join_by_request': self.join_by_request,
|
||||
}
|
||||
chat = Chat.de_json(json_dict, bot)
|
||||
|
||||
@@ -111,6 +117,8 @@ class TestChat:
|
||||
assert chat.linked_chat_id == self.linked_chat_id
|
||||
assert chat.location.location == self.location.location
|
||||
assert chat.location.address == self.location.address
|
||||
assert chat.join_to_send_messages == self.join_to_send_messages
|
||||
assert chat.join_by_request == self.join_by_request
|
||||
|
||||
def test_to_dict(self, chat):
|
||||
chat_dict = chat.to_dict()
|
||||
@@ -129,6 +137,8 @@ class TestChat:
|
||||
assert chat_dict['has_protected_content'] == chat.has_protected_content
|
||||
assert chat_dict['linked_chat_id'] == chat.linked_chat_id
|
||||
assert chat_dict['location'] == chat.location.to_dict()
|
||||
assert chat_dict["join_to_send_messages"] == chat.join_to_send_messages
|
||||
assert chat_dict["join_by_request"] == chat.join_by_request
|
||||
|
||||
def test_link(self, chat):
|
||||
assert chat.link == f'https://t.me/{chat.username}'
|
||||
@@ -706,6 +716,42 @@ class TestChat:
|
||||
monkeypatch.setattr(chat.bot, 'revoke_chat_invite_link', make_assertion)
|
||||
assert chat.revoke_invite_link(invite_link=link)
|
||||
|
||||
def test_instance_method_get_menu_button(self, monkeypatch, chat):
|
||||
def make_assertion(*_, **kwargs):
|
||||
return kwargs['chat_id'] == chat.id
|
||||
|
||||
assert check_shortcut_signature(
|
||||
Chat.get_menu_button, Bot.get_chat_menu_button, ['chat_id'], []
|
||||
)
|
||||
assert check_shortcut_call(
|
||||
chat.get_menu_button,
|
||||
chat.bot,
|
||||
'get_chat_menu_button',
|
||||
shortcut_kwargs=['chat_id'],
|
||||
)
|
||||
assert check_defaults_handling(chat.get_menu_button, chat.bot)
|
||||
|
||||
monkeypatch.setattr(chat.bot, 'get_chat_menu_button', make_assertion)
|
||||
assert chat.get_menu_button()
|
||||
|
||||
def test_instance_method_set_menu_button(self, monkeypatch, chat):
|
||||
def make_assertion(*_, **kwargs):
|
||||
return kwargs['chat_id'] == chat.id and kwargs['menu_button'] == 'menu_button'
|
||||
|
||||
assert check_shortcut_signature(
|
||||
Chat.set_menu_button, Bot.set_chat_menu_button, ['chat_id'], []
|
||||
)
|
||||
assert check_shortcut_call(
|
||||
chat.set_menu_button,
|
||||
chat.bot,
|
||||
'set_chat_menu_button',
|
||||
shortcut_kwargs=['chat_id'],
|
||||
)
|
||||
assert check_defaults_handling(chat.set_menu_button, chat.bot)
|
||||
|
||||
monkeypatch.setattr(chat.bot, 'set_chat_menu_button', make_assertion)
|
||||
assert chat.set_menu_button(menu_button='menu_button')
|
||||
|
||||
def test_approve_join_request(self, monkeypatch, chat):
|
||||
def make_assertion(*_, **kwargs):
|
||||
return kwargs['chat_id'] == chat.id and kwargs['user_id'] == 42
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2022
|
||||
# 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 ChatAdministratorRights
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(scope='class')
|
||||
def chat_admin_rights():
|
||||
return ChatAdministratorRights(
|
||||
can_change_info=True,
|
||||
can_delete_messages=True,
|
||||
can_invite_users=True,
|
||||
can_pin_messages=True,
|
||||
can_promote_members=True,
|
||||
can_restrict_members=True,
|
||||
can_post_messages=True,
|
||||
can_edit_messages=True,
|
||||
can_manage_chat=True,
|
||||
can_manage_video_chats=True,
|
||||
is_anonymous=True,
|
||||
)
|
||||
|
||||
|
||||
class TestChatAdministratorRights:
|
||||
def test_slot_behaviour(self, chat_admin_rights, mro_slots):
|
||||
inst = chat_admin_rights
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot"
|
||||
|
||||
def test_de_json(self, bot, chat_admin_rights):
|
||||
json_dict = {
|
||||
'can_change_info': True,
|
||||
'can_delete_messages': True,
|
||||
'can_invite_users': True,
|
||||
'can_pin_messages': True,
|
||||
'can_promote_members': True,
|
||||
'can_restrict_members': True,
|
||||
'can_post_messages': True,
|
||||
'can_edit_messages': True,
|
||||
'can_manage_chat': True,
|
||||
'can_manage_video_chats': True,
|
||||
'is_anonymous': True,
|
||||
}
|
||||
chat_administrator_rights_de = ChatAdministratorRights.de_json(json_dict, bot)
|
||||
|
||||
assert chat_admin_rights == chat_administrator_rights_de
|
||||
|
||||
def test_to_dict(self, chat_admin_rights):
|
||||
car = chat_admin_rights
|
||||
admin_rights_dict = car.to_dict()
|
||||
|
||||
assert isinstance(admin_rights_dict, dict)
|
||||
assert admin_rights_dict['can_change_info'] == car.can_change_info
|
||||
assert admin_rights_dict['can_delete_messages'] == car.can_delete_messages
|
||||
assert admin_rights_dict['can_invite_users'] == car.can_invite_users
|
||||
assert admin_rights_dict['can_pin_messages'] == car.can_pin_messages
|
||||
assert admin_rights_dict['can_promote_members'] == car.can_promote_members
|
||||
assert admin_rights_dict['can_restrict_members'] == car.can_restrict_members
|
||||
assert admin_rights_dict['can_post_messages'] == car.can_post_messages
|
||||
assert admin_rights_dict['can_edit_messages'] == car.can_edit_messages
|
||||
assert admin_rights_dict['can_manage_chat'] == car.can_manage_chat
|
||||
assert admin_rights_dict['is_anonymous'] == car.is_anonymous
|
||||
assert admin_rights_dict['can_manage_video_chats'] == car.can_manage_video_chats
|
||||
|
||||
def test_equality(self):
|
||||
a = ChatAdministratorRights(True, False, False, False, False, False, False, False)
|
||||
b = ChatAdministratorRights(True, False, False, False, False, False, False, False)
|
||||
c = ChatAdministratorRights(False, False, False, False, False, False, False, False)
|
||||
d = ChatAdministratorRights(True, True, False, False, False, False, False, False)
|
||||
e = ChatAdministratorRights(True, True, False, False, False, False, False, False)
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
assert a is not b
|
||||
|
||||
assert a != c
|
||||
assert hash(a) != hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
assert d == e
|
||||
assert hash(d) == hash(e)
|
||||
|
||||
def test_all_rights(self):
|
||||
f = ChatAdministratorRights(True, True, True, True, True, True, True, True)
|
||||
t = ChatAdministratorRights.all_rights()
|
||||
# if the dirs are the same, the attributes will all be there
|
||||
assert dir(f) == dir(t)
|
||||
# now we just need to check that all attributes are True. __slots__ returns all values,
|
||||
# if a new one is added without defaulting to True, this will fail. And we can skip
|
||||
# _id_attrs, doesn't have any information.
|
||||
for key in t.__slots__:
|
||||
if key == "_id_attrs":
|
||||
continue
|
||||
assert t[key] is True
|
||||
# and as a finisher, make sure the default is different.
|
||||
assert f != t
|
||||
|
||||
def test_no_rights(self):
|
||||
f = ChatAdministratorRights(False, False, False, False, False, False, False, False)
|
||||
t = ChatAdministratorRights.no_rights()
|
||||
# if the dirs are the same, the attributes will all be there
|
||||
assert dir(f) == dir(t)
|
||||
# now we just need to check that all attributes are True. __slots__ returns all values,
|
||||
# if a new one is added without defaulting to True, this will fail
|
||||
for key in t.__slots__:
|
||||
if key == "_id_attrs":
|
||||
continue
|
||||
assert t[key] is False
|
||||
# and as a finisher, make sure the default is different.
|
||||
assert f != t
|
||||
@@ -183,6 +183,9 @@ class TestChatMember:
|
||||
if chat_member_type.can_manage_voice_chats is not None:
|
||||
assert chat_member_type.can_manage_voice_chats is True
|
||||
assert type(chat_member_type) == ChatMemberAdministrator
|
||||
if chat_member_type.can_manage_video_chats is not None:
|
||||
assert chat_member_type.can_manage_video_chats is True
|
||||
assert type(chat_member_type) == ChatMemberAdministrator
|
||||
|
||||
def test_de_json_invalid_status(self, bot, user):
|
||||
json_dict = {'status': 'invalid', 'user': user.to_dict()}
|
||||
@@ -217,7 +220,7 @@ class TestChatMember:
|
||||
'can_send_other_messages': True,
|
||||
'can_add_web_page_previews': False,
|
||||
'can_manage_chat': True,
|
||||
'can_manage_voice_chats': True,
|
||||
'can_manage_video_chats': True,
|
||||
}
|
||||
assert type(cls.de_json(json_dict, bot)) is cls
|
||||
|
||||
@@ -252,3 +255,12 @@ class TestChatMember:
|
||||
|
||||
assert c != e
|
||||
assert hash(c) != hash(e)
|
||||
|
||||
def test_invalid_input(self, user):
|
||||
with pytest.raises(ValueError):
|
||||
ChatMember(
|
||||
user=user,
|
||||
status="status",
|
||||
can_manage_video_chats=True,
|
||||
can_manage_voice_chats=False,
|
||||
)
|
||||
|
||||
@@ -222,4 +222,11 @@ class TestChatMemberUpdated:
|
||||
chat_member_updated = ChatMemberUpdated(
|
||||
chat, user, datetime.datetime.utcnow(), old_chat_member, new_chat_member
|
||||
)
|
||||
assert chat_member_updated.difference() == {optional_attribute: (old_value, new_value)}
|
||||
# for backwards compatibility, supplying video/voice chats right also leads
|
||||
# to the other name being generated. So we need to add them to the compare to dict
|
||||
compare_to_dict = {optional_attribute: (old_value, new_value)}
|
||||
if optional_attribute == "can_manage_video_chats":
|
||||
compare_to_dict["can_manage_voice_chats"] = (old_value, new_value)
|
||||
elif optional_attribute == "can_manage_voice_chats":
|
||||
compare_to_dict["can_manage_video_chats"] = (old_value, new_value)
|
||||
assert chat_member_updated.difference() == compare_to_dict
|
||||
|
||||
@@ -70,7 +70,7 @@ class TestChatPhoto:
|
||||
def test_get_and_download(self, bot, chat_photo):
|
||||
new_file = bot.get_file(chat_photo.small_file_id)
|
||||
|
||||
assert new_file.file_id == chat_photo.small_file_id
|
||||
assert new_file.file_unique_id == chat_photo.small_file_unique_id
|
||||
assert new_file.file_path.startswith('https://')
|
||||
|
||||
new_file.download('telegram.jpg')
|
||||
@@ -79,7 +79,7 @@ class TestChatPhoto:
|
||||
|
||||
new_file = bot.get_file(chat_photo.big_file_id)
|
||||
|
||||
assert new_file.file_id == chat_photo.big_file_id
|
||||
assert new_file.file_unique_id == chat_photo.big_file_unique_id
|
||||
assert new_file.file_path.startswith('https://')
|
||||
|
||||
new_file.download('telegram.jpg')
|
||||
|
||||
@@ -204,7 +204,11 @@ class TestCommandHandler(BaseTest):
|
||||
|
||||
def test_deprecation_warning(self):
|
||||
"""``allow_edited`` deprecated in favor of filters"""
|
||||
with pytest.warns(TelegramDeprecationWarning, match='See https://git.io/fxJuV'):
|
||||
with pytest.warns(
|
||||
TelegramDeprecationWarning,
|
||||
match='See https://github.com/python-telegram-bot/python-telegram-bot/wiki'
|
||||
'/Transition-guide-to-Version-12.0',
|
||||
):
|
||||
self.make_default_handler(allow_edited=True)
|
||||
|
||||
def test_edited(self, command_message):
|
||||
|
||||
@@ -943,6 +943,32 @@ class TestFilters:
|
||||
assert Filters.status_update.voice_chat_participants_invited(update)
|
||||
update.message.voice_chat_participants_invited = None
|
||||
|
||||
# same as above, just with video instead of voice.
|
||||
update.message.video_chat_scheduled = 'scheduled'
|
||||
assert Filters.status_update(update)
|
||||
assert Filters.status_update.video_chat_scheduled(update)
|
||||
update.message.video_chat_scheduled = None
|
||||
|
||||
update.message.video_chat_started = 'hello'
|
||||
assert Filters.status_update(update)
|
||||
assert Filters.status_update.video_chat_started(update)
|
||||
update.message.video_chat_started = None
|
||||
|
||||
update.message.video_chat_ended = 'bye'
|
||||
assert Filters.status_update(update)
|
||||
assert Filters.status_update.video_chat_ended(update)
|
||||
update.message.video_chat_ended = None
|
||||
|
||||
update.message.video_chat_participants_invited = 'invited'
|
||||
assert Filters.status_update(update)
|
||||
assert Filters.status_update.video_chat_participants_invited(update)
|
||||
update.message.video_chat_participants_invited = None
|
||||
|
||||
update.message.web_app_data = 'data'
|
||||
assert Filters.status_update(update)
|
||||
assert Filters.status_update.web_app_data(update)
|
||||
update.message.web_app_data = None
|
||||
|
||||
def test_filters_forwarded(self, update):
|
||||
assert not Filters.forwarded(update)
|
||||
update.message.forward_date = datetime.datetime.utcnow()
|
||||
@@ -1163,6 +1189,19 @@ class TestFilters:
|
||||
with pytest.raises(RuntimeError, match='Cannot set name'):
|
||||
f.name = 'foo'
|
||||
|
||||
def test_filters_user_attributes(self, update):
|
||||
assert not Filters.user_attachment(update)
|
||||
assert not Filters.premium_user(update)
|
||||
update.message.from_user.added_to_attachment_menu = True
|
||||
assert Filters.user_attachment(update)
|
||||
assert not Filters.premium_user(update)
|
||||
update.message.from_user.is_premium = True
|
||||
assert Filters.user_attachment(update)
|
||||
assert Filters.premium_user(update)
|
||||
update.message.from_user.added_to_attachment_menu = False
|
||||
assert not Filters.user_attachment(update)
|
||||
assert Filters.premium_user(update)
|
||||
|
||||
def test_filters_chat_init(self):
|
||||
with pytest.raises(RuntimeError, match='in conjunction with'):
|
||||
Filters.chat(chat_id=1, username='chat')
|
||||
|
||||
@@ -126,7 +126,7 @@ class TestHelpers:
|
||||
"""Conversion from timezone-naive datetime to timestamp.
|
||||
Naive datetimes should be assumed to be in UTC.
|
||||
"""
|
||||
datetime = dtm.datetime(2019, 11, 11, 0, 26, 16, 10 ** 5)
|
||||
datetime = dtm.datetime(2019, 11, 11, 0, 26, 16, 10**5)
|
||||
assert helpers.to_float_timestamp(datetime) == 1573431976.1
|
||||
|
||||
def test_to_float_timestamp_absolute_naive_no_pytz(self, monkeypatch):
|
||||
@@ -134,14 +134,14 @@ class TestHelpers:
|
||||
Naive datetimes should be assumed to be in UTC.
|
||||
"""
|
||||
monkeypatch.setattr(helpers, 'UTC', helpers.DTM_UTC)
|
||||
datetime = dtm.datetime(2019, 11, 11, 0, 26, 16, 10 ** 5)
|
||||
datetime = dtm.datetime(2019, 11, 11, 0, 26, 16, 10**5)
|
||||
assert helpers.to_float_timestamp(datetime) == 1573431976.1
|
||||
|
||||
def test_to_float_timestamp_absolute_aware(self, timezone):
|
||||
"""Conversion from timezone-aware datetime to timestamp"""
|
||||
# we're parametrizing this with two different UTC offsets to exclude the possibility
|
||||
# of an xpass when the test is run in a timezone with the same UTC offset
|
||||
test_datetime = dtm.datetime(2019, 11, 11, 0, 26, 16, 10 ** 5)
|
||||
test_datetime = dtm.datetime(2019, 11, 11, 0, 26, 16, 10**5)
|
||||
datetime = timezone.localize(test_datetime)
|
||||
assert (
|
||||
helpers.to_float_timestamp(datetime)
|
||||
@@ -217,7 +217,7 @@ class TestHelpers:
|
||||
def test_from_timestamp_aware(self, timezone):
|
||||
# we're parametrizing this with two different UTC offsets to exclude the possibility
|
||||
# of an xpass when the test is run in a timezone with the same UTC offset
|
||||
test_datetime = dtm.datetime(2019, 11, 11, 0, 26, 16, 10 ** 5)
|
||||
test_datetime = dtm.datetime(2019, 11, 11, 0, 26, 16, 10**5)
|
||||
datetime = timezone.localize(test_datetime)
|
||||
assert (
|
||||
helpers.from_timestamp(
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
import pytest
|
||||
|
||||
from telegram import InlineKeyboardButton, LoginUrl
|
||||
from telegram import InlineKeyboardButton, LoginUrl, WebAppInfo, CallbackGame
|
||||
|
||||
|
||||
@pytest.fixture(scope='class')
|
||||
@@ -33,6 +33,7 @@ def inline_keyboard_button():
|
||||
callback_game=TestInlineKeyboardButton.callback_game,
|
||||
pay=TestInlineKeyboardButton.pay,
|
||||
login_url=TestInlineKeyboardButton.login_url,
|
||||
web_app=TestInlineKeyboardButton.web_app,
|
||||
)
|
||||
|
||||
|
||||
@@ -42,9 +43,10 @@ class TestInlineKeyboardButton:
|
||||
callback_data = 'callback data'
|
||||
switch_inline_query = 'switch_inline_query'
|
||||
switch_inline_query_current_chat = 'switch_inline_query_current_chat'
|
||||
callback_game = 'callback_game'
|
||||
pay = 'pay'
|
||||
callback_game = CallbackGame()
|
||||
pay = True
|
||||
login_url = LoginUrl("http://google.com")
|
||||
web_app = WebAppInfo(url="https://example.com")
|
||||
|
||||
def test_slot_behaviour(self, inline_keyboard_button, recwarn, mro_slots):
|
||||
inst = inline_keyboard_button
|
||||
@@ -64,9 +66,10 @@ class TestInlineKeyboardButton:
|
||||
inline_keyboard_button.switch_inline_query_current_chat
|
||||
== self.switch_inline_query_current_chat
|
||||
)
|
||||
assert inline_keyboard_button.callback_game == self.callback_game
|
||||
assert isinstance(inline_keyboard_button.callback_game, CallbackGame)
|
||||
assert inline_keyboard_button.pay == self.pay
|
||||
assert inline_keyboard_button.login_url == self.login_url
|
||||
assert inline_keyboard_button.web_app == self.web_app
|
||||
|
||||
def test_to_dict(self, inline_keyboard_button):
|
||||
inline_keyboard_button_dict = inline_keyboard_button.to_dict()
|
||||
@@ -83,11 +86,15 @@ class TestInlineKeyboardButton:
|
||||
inline_keyboard_button_dict['switch_inline_query_current_chat']
|
||||
== inline_keyboard_button.switch_inline_query_current_chat
|
||||
)
|
||||
assert inline_keyboard_button_dict['callback_game'] == inline_keyboard_button.callback_game
|
||||
assert (
|
||||
inline_keyboard_button_dict['callback_game']
|
||||
== inline_keyboard_button.callback_game.to_dict()
|
||||
)
|
||||
assert inline_keyboard_button_dict['pay'] == inline_keyboard_button.pay
|
||||
assert (
|
||||
inline_keyboard_button_dict['login_url'] == inline_keyboard_button.login_url.to_dict()
|
||||
) # NOQA: E127
|
||||
assert inline_keyboard_button_dict['web_app'] == inline_keyboard_button.web_app.to_dict()
|
||||
|
||||
def test_de_json(self, bot):
|
||||
json_dict = {
|
||||
@@ -96,7 +103,9 @@ class TestInlineKeyboardButton:
|
||||
'callback_data': self.callback_data,
|
||||
'switch_inline_query': self.switch_inline_query,
|
||||
'switch_inline_query_current_chat': self.switch_inline_query_current_chat,
|
||||
'callback_game': self.callback_game,
|
||||
'callback_game': self.callback_game.to_dict(),
|
||||
'web_app': self.web_app.to_dict(),
|
||||
'login_url': self.login_url.to_dict(),
|
||||
'pay': self.pay,
|
||||
}
|
||||
|
||||
@@ -109,8 +118,14 @@ class TestInlineKeyboardButton:
|
||||
inline_keyboard_button.switch_inline_query_current_chat
|
||||
== self.switch_inline_query_current_chat
|
||||
)
|
||||
assert inline_keyboard_button.callback_game == self.callback_game
|
||||
# CallbackGame has empty _id_attrs, so just test if the class is created.
|
||||
assert isinstance(inline_keyboard_button.callback_game, CallbackGame)
|
||||
assert inline_keyboard_button.pay == self.pay
|
||||
assert inline_keyboard_button.login_url == self.login_url
|
||||
assert inline_keyboard_button.web_app == self.web_app
|
||||
|
||||
none = InlineKeyboardButton.de_json({}, bot)
|
||||
assert none is None
|
||||
|
||||
def test_equality(self):
|
||||
a = InlineKeyboardButton('text', callback_data='data')
|
||||
|
||||
+62
-1
@@ -100,8 +100,19 @@ class TestInvoice:
|
||||
assert message.invoice.title == self.title
|
||||
assert message.invoice.total_amount == self.total_amount
|
||||
|
||||
link = bot.create_invoice_link(
|
||||
title=self.title,
|
||||
description=self.description,
|
||||
payload=self.payload,
|
||||
provider_token=provider_token,
|
||||
currency=self.currency,
|
||||
prices=self.prices,
|
||||
)
|
||||
assert isinstance(link, str)
|
||||
assert link != ""
|
||||
|
||||
@flaky(3, 1)
|
||||
def test_send_all_args(self, bot, chat_id, provider_token, monkeypatch):
|
||||
def test_send_all_args_send_invoice(self, bot, chat_id, provider_token, monkeypatch):
|
||||
message = bot.send_invoice(
|
||||
chat_id,
|
||||
self.title,
|
||||
@@ -195,6 +206,56 @@ class TestInvoice:
|
||||
protect_content=True,
|
||||
)
|
||||
|
||||
def test_send_all_args_create_invoice_link(self, bot, chat_id, provider_token, monkeypatch):
|
||||
def make_assertion(*args, **_):
|
||||
kwargs = args[1]
|
||||
return (
|
||||
kwargs["title"] == "title"
|
||||
and kwargs["description"] == "description"
|
||||
and kwargs["payload"] == "payload"
|
||||
and kwargs["provider_token"] == "provider_token"
|
||||
and kwargs["currency"] == "currency"
|
||||
and kwargs["prices"] == [p.to_dict() for p in self.prices]
|
||||
and kwargs["max_tip_amount"] == "max_tip_amount"
|
||||
and kwargs["suggested_tip_amounts"] == "suggested_tip_amounts"
|
||||
and kwargs["provider_data"] == "provider_data"
|
||||
and kwargs["photo_url"] == "photo_url"
|
||||
and kwargs["photo_size"] == "photo_size"
|
||||
and kwargs["photo_width"] == "photo_width"
|
||||
and kwargs["photo_height"] == "photo_height"
|
||||
and kwargs["need_name"] == "need_name"
|
||||
and kwargs["need_phone_number"] == "need_phone_number"
|
||||
and kwargs["need_email"] == "need_email"
|
||||
and kwargs["need_shipping_address"] == "need_shipping_address"
|
||||
and kwargs["send_phone_number_to_provider"] == "send_phone_number_to_provider"
|
||||
and kwargs["send_email_to_provider"] == "send_email_to_provider"
|
||||
and kwargs["is_flexible"] == "is_flexible"
|
||||
)
|
||||
|
||||
monkeypatch.setattr(bot, "_post", make_assertion)
|
||||
assert bot.create_invoice_link(
|
||||
title="title",
|
||||
description="description",
|
||||
payload="payload",
|
||||
provider_token="provider_token",
|
||||
currency="currency",
|
||||
prices=self.prices,
|
||||
max_tip_amount="max_tip_amount",
|
||||
suggested_tip_amounts="suggested_tip_amounts",
|
||||
provider_data="provider_data",
|
||||
photo_url="photo_url",
|
||||
photo_size="photo_size",
|
||||
photo_width="photo_width",
|
||||
photo_height="photo_height",
|
||||
need_name="need_name",
|
||||
need_phone_number="need_phone_number",
|
||||
need_email="need_email",
|
||||
need_shipping_address="need_shipping_address",
|
||||
send_phone_number_to_provider="send_phone_number_to_provider",
|
||||
send_email_to_provider="send_email_to_provider",
|
||||
is_flexible="is_flexible",
|
||||
)
|
||||
|
||||
def test_send_object_as_provider_data(self, monkeypatch, bot, chat_id, provider_token):
|
||||
def test(url, data, **kwargs):
|
||||
# depends on whether we're using ujson
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
import pytest
|
||||
|
||||
from telegram import KeyboardButton, InlineKeyboardButton
|
||||
from telegram import KeyboardButton, InlineKeyboardButton, WebAppInfo
|
||||
from telegram.keyboardbuttonpolltype import KeyboardButtonPollType
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ def keyboard_button():
|
||||
request_location=TestKeyboardButton.request_location,
|
||||
request_contact=TestKeyboardButton.request_contact,
|
||||
request_poll=TestKeyboardButton.request_poll,
|
||||
web_app=TestKeyboardButton.web_app,
|
||||
)
|
||||
|
||||
|
||||
@@ -37,6 +38,7 @@ class TestKeyboardButton:
|
||||
request_location = True
|
||||
request_contact = True
|
||||
request_poll = KeyboardButtonPollType("quiz")
|
||||
web_app = WebAppInfo(url="https://example.com")
|
||||
|
||||
def test_slot_behaviour(self, keyboard_button, recwarn, mro_slots):
|
||||
inst = keyboard_button
|
||||
@@ -52,6 +54,7 @@ class TestKeyboardButton:
|
||||
assert keyboard_button.request_location == self.request_location
|
||||
assert keyboard_button.request_contact == self.request_contact
|
||||
assert keyboard_button.request_poll == self.request_poll
|
||||
assert keyboard_button.web_app == self.web_app
|
||||
|
||||
def test_to_dict(self, keyboard_button):
|
||||
keyboard_button_dict = keyboard_button.to_dict()
|
||||
@@ -61,6 +64,26 @@ class TestKeyboardButton:
|
||||
assert keyboard_button_dict['request_location'] == keyboard_button.request_location
|
||||
assert keyboard_button_dict['request_contact'] == keyboard_button.request_contact
|
||||
assert keyboard_button_dict['request_poll'] == keyboard_button.request_poll.to_dict()
|
||||
assert keyboard_button_dict['web_app'] == keyboard_button.web_app.to_dict()
|
||||
|
||||
def test_de_json(self, bot):
|
||||
json_dict = {
|
||||
'text': self.text,
|
||||
'request_location': self.request_location,
|
||||
'request_contact': self.request_contact,
|
||||
'request_poll': self.request_poll.to_dict(),
|
||||
'web_app': self.web_app.to_dict(),
|
||||
}
|
||||
|
||||
inline_keyboard_button = KeyboardButton.de_json(json_dict, None)
|
||||
assert inline_keyboard_button.text == self.text
|
||||
assert inline_keyboard_button.request_location == self.request_location
|
||||
assert inline_keyboard_button.request_contact == self.request_contact
|
||||
assert inline_keyboard_button.request_poll == self.request_poll
|
||||
assert inline_keyboard_button.web_app == self.web_app
|
||||
|
||||
none = KeyboardButton.de_json({}, None)
|
||||
assert none is None
|
||||
|
||||
def test_equality(self):
|
||||
a = KeyboardButton('test', request_contact=True)
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2022
|
||||
# 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 copy import deepcopy
|
||||
|
||||
import pytest
|
||||
|
||||
from telegram import (
|
||||
Dice,
|
||||
MenuButton,
|
||||
MenuButtonDefault,
|
||||
MenuButtonCommands,
|
||||
MenuButtonWebApp,
|
||||
WebAppInfo,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(
|
||||
scope="class",
|
||||
params=[
|
||||
MenuButton.DEFAULT,
|
||||
MenuButton.WEB_APP,
|
||||
MenuButton.COMMANDS,
|
||||
],
|
||||
)
|
||||
def scope_type(request):
|
||||
return request.param
|
||||
|
||||
|
||||
@pytest.fixture(
|
||||
scope="class",
|
||||
params=[
|
||||
MenuButtonDefault,
|
||||
MenuButtonCommands,
|
||||
MenuButtonWebApp,
|
||||
],
|
||||
ids=[
|
||||
MenuButton.DEFAULT,
|
||||
MenuButton.COMMANDS,
|
||||
MenuButton.WEB_APP,
|
||||
],
|
||||
)
|
||||
def scope_class(request):
|
||||
return request.param
|
||||
|
||||
|
||||
@pytest.fixture(
|
||||
scope="class",
|
||||
params=[
|
||||
(MenuButtonDefault, MenuButton.DEFAULT),
|
||||
(MenuButtonCommands, MenuButton.COMMANDS),
|
||||
(MenuButtonWebApp, MenuButton.WEB_APP),
|
||||
],
|
||||
ids=[
|
||||
MenuButton.DEFAULT,
|
||||
MenuButton.COMMANDS,
|
||||
MenuButton.WEB_APP,
|
||||
],
|
||||
)
|
||||
def scope_class_and_type(request):
|
||||
return request.param
|
||||
|
||||
|
||||
@pytest.fixture(scope='class')
|
||||
def menu_button(scope_class_and_type):
|
||||
return scope_class_and_type[0](
|
||||
type=scope_class_and_type[1], text=TestMenuButton.text, web_app=TestMenuButton.web_app
|
||||
)
|
||||
|
||||
|
||||
# All the scope types are very similar, so we test everything via parametrization
|
||||
class TestMenuButton:
|
||||
text = 'button_text'
|
||||
web_app = WebAppInfo(url='https://python-telegram-bot.org/web_app')
|
||||
|
||||
def test_slot_behaviour(self, menu_button, mro_slots):
|
||||
for attr in menu_button.__slots__:
|
||||
assert getattr(menu_button, attr, 'err') != 'err', f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(menu_button)) == len(set(mro_slots(menu_button))), "duplicate slot"
|
||||
|
||||
def test_de_json(self, bot, scope_class_and_type):
|
||||
cls = scope_class_and_type[0]
|
||||
type_ = scope_class_and_type[1]
|
||||
|
||||
assert cls.de_json({}, bot) is None
|
||||
assert cls.de_json(None, bot) is None
|
||||
|
||||
json_dict = {'type': type_, 'text': self.text, 'web_app': self.web_app.to_dict()}
|
||||
menu_button = MenuButton.de_json(json_dict, bot)
|
||||
|
||||
assert isinstance(menu_button, MenuButton)
|
||||
assert type(menu_button) is cls
|
||||
assert menu_button.type == type_
|
||||
if 'web_app' in cls.__slots__:
|
||||
assert menu_button.web_app == self.web_app
|
||||
if 'text' in cls.__slots__:
|
||||
assert menu_button.text == self.text
|
||||
|
||||
def test_de_json_invalid_type(self, bot):
|
||||
json_dict = {'type': 'invalid', 'text': self.text, 'web_app': self.web_app.to_dict()}
|
||||
menu_button = MenuButton.de_json(json_dict, bot)
|
||||
|
||||
assert type(menu_button) is MenuButton
|
||||
assert menu_button.type == 'invalid'
|
||||
|
||||
def test_de_json_subclass(self, scope_class, bot):
|
||||
"""This makes sure that e.g. MenuButtonDefault(data) never returns a
|
||||
MenuButtonChat instance."""
|
||||
json_dict = {'type': 'invalid', 'text': self.text, 'web_app': self.web_app.to_dict()}
|
||||
assert type(scope_class.de_json(json_dict, bot)) is scope_class
|
||||
|
||||
def test_to_dict(self, menu_button):
|
||||
menu_button_dict = menu_button.to_dict()
|
||||
|
||||
assert isinstance(menu_button_dict, dict)
|
||||
assert menu_button_dict['type'] == menu_button.type
|
||||
if hasattr(menu_button, 'web_app'):
|
||||
assert menu_button_dict['web_app'] == menu_button.web_app.to_dict()
|
||||
if hasattr(menu_button, 'text'):
|
||||
assert menu_button_dict['text'] == menu_button.text
|
||||
|
||||
def test_equality(self, menu_button, bot):
|
||||
a = MenuButton('base_type')
|
||||
b = MenuButton('base_type')
|
||||
c = menu_button
|
||||
d = deepcopy(menu_button)
|
||||
e = Dice(4, 'emoji')
|
||||
|
||||
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)
|
||||
|
||||
assert c == d
|
||||
assert hash(c) == hash(d)
|
||||
|
||||
assert c != e
|
||||
assert hash(c) != hash(e)
|
||||
|
||||
if hasattr(c, 'web_app'):
|
||||
json_dict = c.to_dict()
|
||||
json_dict['web_app'] = WebAppInfo('https://foo.bar/web_app').to_dict()
|
||||
f = c.__class__.de_json(json_dict, bot)
|
||||
|
||||
assert c != f
|
||||
assert hash(c) != hash(f)
|
||||
|
||||
if hasattr(c, 'text'):
|
||||
json_dict = c.to_dict()
|
||||
json_dict['text'] = 'other text'
|
||||
g = c.__class__.de_json(json_dict, bot)
|
||||
|
||||
assert c != g
|
||||
assert hash(c) != hash(g)
|
||||
+53
-1
@@ -48,11 +48,16 @@ from telegram import (
|
||||
Dice,
|
||||
Bot,
|
||||
ChatAction,
|
||||
VoiceChatScheduled,
|
||||
VoiceChatStarted,
|
||||
VoiceChatEnded,
|
||||
VoiceChatParticipantsInvited,
|
||||
VideoChatScheduled,
|
||||
VideoChatStarted,
|
||||
VideoChatEnded,
|
||||
VideoChatParticipantsInvited,
|
||||
MessageAutoDeleteTimerChanged,
|
||||
VoiceChatScheduled,
|
||||
WebAppData,
|
||||
)
|
||||
from telegram.ext import Defaults
|
||||
from tests.conftest import check_shortcut_signature, check_shortcut_call, check_defaults_handling
|
||||
@@ -182,6 +187,15 @@ def message(bot):
|
||||
{'sender_chat': Chat(-123, 'discussion_channel')},
|
||||
{'is_automatic_forward': True},
|
||||
{'has_protected_content': True},
|
||||
{'video_chat_scheduled': VideoChatScheduled(datetime.utcnow())},
|
||||
{'video_chat_started': VideoChatStarted()},
|
||||
{'video_chat_ended': VideoChatEnded(100)},
|
||||
{
|
||||
'video_chat_participants_invited': VideoChatParticipantsInvited(
|
||||
[User(1, 'Rem', False), User(2, 'Emilia', False)]
|
||||
)
|
||||
},
|
||||
{'web_app_data': WebAppData('some_data', 'some_button_text')},
|
||||
],
|
||||
ids=[
|
||||
'forwarded_user',
|
||||
@@ -230,9 +244,14 @@ def message(bot):
|
||||
'voice_chat_started',
|
||||
'voice_chat_ended',
|
||||
'voice_chat_participants_invited',
|
||||
'video_chat_scheduled',
|
||||
'video_chat_started',
|
||||
'video_chat_ended',
|
||||
'video_chat_participants_invited',
|
||||
'sender_chat',
|
||||
'is_automatic_forward',
|
||||
'has_protected_content',
|
||||
'web_app_data',
|
||||
],
|
||||
)
|
||||
def message_params(bot, request):
|
||||
@@ -1585,3 +1604,36 @@ class TestMessage:
|
||||
|
||||
assert a != e
|
||||
assert hash(a) != hash(e)
|
||||
|
||||
def test_invalid_input(
|
||||
self,
|
||||
):
|
||||
with pytest.raises(ValueError):
|
||||
Message(
|
||||
self.id_,
|
||||
self.date,
|
||||
self.chat,
|
||||
from_user=self.from_user,
|
||||
voice_chat_scheduled=True,
|
||||
video_chat_scheduled=False,
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
Message(
|
||||
self.id_,
|
||||
self.date,
|
||||
self.chat,
|
||||
from_user=self.from_user,
|
||||
voice_chat_ended=True,
|
||||
video_chat_ended=False,
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
Message(
|
||||
self.id_,
|
||||
self.date,
|
||||
self.chat,
|
||||
from_user=self.from_user,
|
||||
voice_chat_participants_invited=True,
|
||||
video_chat_participants_invited=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/].
|
||||
|
||||
from telegram import MessageAutoDeleteTimerChanged, VoiceChatEnded
|
||||
from telegram import MessageAutoDeleteTimerChanged, VoiceChatEnded, VideoChatEnded
|
||||
|
||||
|
||||
class TestMessageAutoDeleteTimerChanged:
|
||||
@@ -49,6 +49,7 @@ class TestMessageAutoDeleteTimerChanged:
|
||||
b = MessageAutoDeleteTimerChanged(100)
|
||||
c = MessageAutoDeleteTimerChanged(50)
|
||||
d = VoiceChatEnded(25)
|
||||
e = VideoChatEnded(30)
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
@@ -58,3 +59,6 @@ class TestMessageAutoDeleteTimerChanged:
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
|
||||
assert a != e
|
||||
assert hash(a) != hash(e)
|
||||
|
||||
@@ -149,11 +149,23 @@ class TestMessageHandler:
|
||||
assert self.test_flag
|
||||
|
||||
def test_deprecation_warning(self):
|
||||
with pytest.warns(TelegramDeprecationWarning, match='See https://git.io/fxJuV'):
|
||||
with pytest.warns(
|
||||
TelegramDeprecationWarning,
|
||||
match='See https://github.com/python-telegram-bot/python-telegram-bot/wiki'
|
||||
'/Transition-guide-to-Version-12.0',
|
||||
):
|
||||
MessageHandler(None, self.callback_basic, edited_updates=True)
|
||||
with pytest.warns(TelegramDeprecationWarning, match='See https://git.io/fxJuV'):
|
||||
with pytest.warns(
|
||||
TelegramDeprecationWarning,
|
||||
match='See https://github.com/python-telegram-bot/python-telegram-bot/wiki'
|
||||
'/Transition-guide-to-Version-12.0',
|
||||
):
|
||||
MessageHandler(None, self.callback_basic, message_updates=False)
|
||||
with pytest.warns(TelegramDeprecationWarning, match='See https://git.io/fxJuV'):
|
||||
with pytest.warns(
|
||||
TelegramDeprecationWarning,
|
||||
match='See https://github.com/python-telegram-bot/python-telegram-bot/wiki'
|
||||
'/Transition-guide-to-Version-12.0',
|
||||
):
|
||||
MessageHandler(None, self.callback_basic, channel_post_updates=True)
|
||||
|
||||
def test_edited_deprecated(self, message):
|
||||
|
||||
+20
-1
@@ -100,6 +100,8 @@ def check_method(h4):
|
||||
ignored |= {'venue'} # Added for ease of use
|
||||
elif name == 'answerInlineQuery':
|
||||
ignored |= {'current_offset'} # Added for ease of use
|
||||
elif name == 'promoteChatMember':
|
||||
ignored |= {'can_manage_voice_chats'} # for backwards compatibility
|
||||
|
||||
assert (sig.parameters.keys() ^ checked) - ignored == set()
|
||||
|
||||
@@ -121,6 +123,7 @@ def check_object(h4):
|
||||
name.startswith('InlineQueryResult')
|
||||
or name.startswith('InputMedia')
|
||||
or name.startswith('BotCommandScope')
|
||||
or name.startswith('MenuButton')
|
||||
) and field == 'type':
|
||||
continue
|
||||
elif (name.startswith('ChatMember')) and field == 'status':
|
||||
@@ -142,7 +145,11 @@ def check_object(h4):
|
||||
if name == 'InlineQueryResult':
|
||||
ignored |= {'id', 'type'} # attributes common to all subclasses
|
||||
if name == 'ChatMember':
|
||||
ignored |= {'user', 'status'} # attributes common to all subclasses
|
||||
ignored |= {
|
||||
'user',
|
||||
'status',
|
||||
'can_manage_video_chats',
|
||||
} # attributes common to all subclasses
|
||||
if name == 'ChatMember':
|
||||
ignored |= {
|
||||
'can_add_web_page_previews', # for backwards compatibility
|
||||
@@ -168,6 +175,8 @@ def check_object(h4):
|
||||
}
|
||||
if name == 'BotCommandScope':
|
||||
ignored |= {'type'} # attributes common to all subclasses
|
||||
if name == 'MenuButton':
|
||||
ignored |= {'type'} # attributes common to all subclasses
|
||||
elif name == 'User':
|
||||
ignored |= {'type'} # TODO: Deprecation
|
||||
elif name in ('PassportFile', 'EncryptedPassportElement'):
|
||||
@@ -176,6 +185,16 @@ def check_object(h4):
|
||||
ignored |= {'message', 'type', 'source'}
|
||||
elif name.startswith('InputMedia'):
|
||||
ignored |= {'filename'} # Convenience parameter
|
||||
elif name == 'ChatMemberAdministrator':
|
||||
ignored |= {'can_manage_voice_chats'} # for backwards compatibility
|
||||
elif name == 'Message':
|
||||
# for backwards compatibility
|
||||
ignored |= {
|
||||
'voice_chat_ended',
|
||||
'voice_chat_participants_invited',
|
||||
'voice_chat_scheduled',
|
||||
'voice_chat_started',
|
||||
}
|
||||
|
||||
assert (sig.parameters.keys() ^ checked) - ignored == set()
|
||||
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2022
|
||||
# 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 SentWebAppMessage
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(scope='class')
|
||||
def sent_web_app_message():
|
||||
return SentWebAppMessage(
|
||||
inline_message_id=TestSentWebAppMessage.inline_message_id,
|
||||
)
|
||||
|
||||
|
||||
class TestSentWebAppMessage:
|
||||
inline_message_id = '123'
|
||||
|
||||
def test_slot_behaviour(self, sent_web_app_message, mro_slots):
|
||||
inst = sent_web_app_message
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot"
|
||||
|
||||
def test_to_dict(self, sent_web_app_message):
|
||||
sent_web_app_message_dict = sent_web_app_message.to_dict()
|
||||
|
||||
assert isinstance(sent_web_app_message_dict, dict)
|
||||
assert sent_web_app_message_dict['inline_message_id'] == self.inline_message_id
|
||||
|
||||
def test_de_json(self, bot):
|
||||
data = {'inline_message_id': self.inline_message_id}
|
||||
m = SentWebAppMessage.de_json(data, None)
|
||||
assert m.inline_message_id == self.inline_message_id
|
||||
|
||||
def test_equality(self):
|
||||
a = SentWebAppMessage(self.inline_message_id)
|
||||
b = SentWebAppMessage(self.inline_message_id)
|
||||
c = SentWebAppMessage("")
|
||||
d = SentWebAppMessage("not_inline_message_id")
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
assert a is not b
|
||||
|
||||
assert a != c
|
||||
assert hash(a) != hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
+27
-1
@@ -23,7 +23,7 @@ from time import sleep
|
||||
import pytest
|
||||
from flaky import flaky
|
||||
|
||||
from telegram import Sticker, PhotoSize, TelegramError, StickerSet, Audio, MaskPosition, Bot
|
||||
from telegram import Sticker, PhotoSize, TelegramError, StickerSet, Audio, MaskPosition, Bot, File
|
||||
from telegram.error import BadRequest
|
||||
from tests.conftest import check_shortcut_call, check_shortcut_signature, check_defaults_handling
|
||||
|
||||
@@ -87,6 +87,8 @@ class TestSticker:
|
||||
sticker_file_id = '5a3128a4d2a04750b5b58397f3b5e812'
|
||||
sticker_file_unique_id = 'adc3145fd2e84d95b64d68eaa22aa33e'
|
||||
|
||||
premium_animation = File("this_is_an_id", "this_is_an_unique_id")
|
||||
|
||||
def test_slot_behaviour(self, sticker, mro_slots, recwarn):
|
||||
for attr in sticker.__slots__:
|
||||
assert getattr(sticker, attr, 'err') != 'err', f"got extra slot '{attr}'"
|
||||
@@ -117,6 +119,8 @@ class TestSticker:
|
||||
assert sticker.thumb.width == self.thumb_width
|
||||
assert sticker.thumb.height == self.thumb_height
|
||||
assert sticker.thumb.file_size == self.thumb_file_size
|
||||
# we need to be a premium TG user to send a premium sticker, so the below is not tested
|
||||
# assert sticker.premium_animation == self.premium_animation
|
||||
|
||||
@flaky(3, 1)
|
||||
def test_send_all_args(self, bot, chat_id, sticker_file, sticker):
|
||||
@@ -134,6 +138,8 @@ class TestSticker:
|
||||
assert message.sticker.is_animated == sticker.is_animated
|
||||
assert message.sticker.is_video == sticker.is_video
|
||||
assert message.sticker.file_size == sticker.file_size
|
||||
# we need to be a premium TG user to send a premium sticker, so the below is not tested
|
||||
# assert message.sticker.premium_animation == sticker.premium_animation
|
||||
|
||||
assert isinstance(message.sticker.thumb, PhotoSize)
|
||||
assert isinstance(message.sticker.thumb.file_id, str)
|
||||
@@ -207,6 +213,7 @@ class TestSticker:
|
||||
'thumb': sticker.thumb.to_dict(),
|
||||
'emoji': self.emoji,
|
||||
'file_size': self.file_size,
|
||||
'premium_animation': self.premium_animation.to_dict(),
|
||||
}
|
||||
json_sticker = Sticker.de_json(json_dict, bot)
|
||||
|
||||
@@ -219,6 +226,7 @@ class TestSticker:
|
||||
assert json_sticker.emoji == self.emoji
|
||||
assert json_sticker.file_size == self.file_size
|
||||
assert json_sticker.thumb == sticker.thumb
|
||||
assert json_sticker.premium_animation == self.premium_animation
|
||||
|
||||
def test_send_with_sticker(self, monkeypatch, bot, chat_id, sticker):
|
||||
def test(url, data, **kwargs):
|
||||
@@ -304,6 +312,24 @@ class TestSticker:
|
||||
with pytest.raises(TypeError):
|
||||
bot.send_sticker(chat_id)
|
||||
|
||||
@flaky(3, 1)
|
||||
def test_premium_animation(self, bot):
|
||||
# testing animation sucks a bit since we can't create a premium sticker. What we can do is
|
||||
# get a sticker set which includes a premium sticker and check that specific one.
|
||||
premium_sticker_set = bot.get_sticker_set("Flame")
|
||||
# the first one to appear here is a sticker with unique file id of AQADOBwAAifPOElr
|
||||
# this could change in the future ofc.
|
||||
premium_sticker = premium_sticker_set.stickers[20]
|
||||
assert premium_sticker.premium_animation.file_unique_id == "AQADOBwAAifPOElr"
|
||||
assert isinstance(premium_sticker.premium_animation.file_id, str)
|
||||
assert premium_sticker.premium_animation.file_id != ""
|
||||
premium_sticker_dict = {
|
||||
"file_unique_id": "AQADOBwAAifPOElr",
|
||||
"file_id": premium_sticker.premium_animation.file_id,
|
||||
"file_size": premium_sticker.premium_animation.file_size,
|
||||
}
|
||||
assert premium_sticker.premium_animation.to_dict() == premium_sticker_dict
|
||||
|
||||
def test_equality(self, sticker):
|
||||
a = Sticker(
|
||||
sticker.file_id,
|
||||
|
||||
@@ -35,6 +35,8 @@ def json_dict():
|
||||
'can_join_groups': TestUser.can_join_groups,
|
||||
'can_read_all_group_messages': TestUser.can_read_all_group_messages,
|
||||
'supports_inline_queries': TestUser.supports_inline_queries,
|
||||
'is_premium': TestUser.is_premium,
|
||||
'added_to_attachment_menu': TestUser.added_to_attachment_menu,
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +53,8 @@ def user(bot):
|
||||
can_read_all_group_messages=TestUser.can_read_all_group_messages,
|
||||
supports_inline_queries=TestUser.supports_inline_queries,
|
||||
bot=bot,
|
||||
is_premium=TestUser.is_premium,
|
||||
added_to_attachment_menu=TestUser.added_to_attachment_menu,
|
||||
)
|
||||
|
||||
|
||||
@@ -64,6 +68,8 @@ class TestUser:
|
||||
can_join_groups = True
|
||||
can_read_all_group_messages = True
|
||||
supports_inline_queries = False
|
||||
is_premium = True
|
||||
added_to_attachment_menu = False
|
||||
|
||||
def test_slot_behaviour(self, user, mro_slots, recwarn):
|
||||
for attr in user.__slots__:
|
||||
@@ -85,6 +91,8 @@ class TestUser:
|
||||
assert user.can_join_groups == self.can_join_groups
|
||||
assert user.can_read_all_group_messages == self.can_read_all_group_messages
|
||||
assert user.supports_inline_queries == self.supports_inline_queries
|
||||
assert user.is_premium == self.is_premium
|
||||
assert user.added_to_attachment_menu == self.added_to_attachment_menu
|
||||
|
||||
def test_de_json_without_username(self, json_dict, bot):
|
||||
del json_dict['username']
|
||||
@@ -100,6 +108,8 @@ class TestUser:
|
||||
assert user.can_join_groups == self.can_join_groups
|
||||
assert user.can_read_all_group_messages == self.can_read_all_group_messages
|
||||
assert user.supports_inline_queries == self.supports_inline_queries
|
||||
assert user.is_premium == self.is_premium
|
||||
assert user.added_to_attachment_menu == self.added_to_attachment_menu
|
||||
|
||||
def test_de_json_without_username_and_last_name(self, json_dict, bot):
|
||||
del json_dict['username']
|
||||
@@ -116,6 +126,8 @@ class TestUser:
|
||||
assert user.can_join_groups == self.can_join_groups
|
||||
assert user.can_read_all_group_messages == self.can_read_all_group_messages
|
||||
assert user.supports_inline_queries == self.supports_inline_queries
|
||||
assert user.is_premium == self.is_premium
|
||||
assert user.added_to_attachment_menu == self.added_to_attachment_menu
|
||||
|
||||
def test_name(self, user):
|
||||
assert user.name == '@username'
|
||||
@@ -430,6 +442,42 @@ class TestUser:
|
||||
monkeypatch.setattr(user.bot, 'copy_message', make_assertion)
|
||||
assert user.copy_message(chat_id='chat_id', message_id='message_id')
|
||||
|
||||
def test_instance_method_get_menu_button(self, monkeypatch, user):
|
||||
def make_assertion(*_, **kwargs):
|
||||
return kwargs['chat_id'] == user.id
|
||||
|
||||
assert check_shortcut_signature(
|
||||
User.get_menu_button, Bot.get_chat_menu_button, ['chat_id'], []
|
||||
)
|
||||
assert check_shortcut_call(
|
||||
user.get_menu_button,
|
||||
user.bot,
|
||||
'get_chat_menu_button',
|
||||
shortcut_kwargs=['chat_id'],
|
||||
)
|
||||
assert check_defaults_handling(user.get_menu_button, user.bot)
|
||||
|
||||
monkeypatch.setattr(user.bot, 'get_chat_menu_button', make_assertion)
|
||||
assert user.get_menu_button()
|
||||
|
||||
def test_instance_method_set_menu_button(self, monkeypatch, user):
|
||||
def make_assertion(*_, **kwargs):
|
||||
return kwargs['chat_id'] == user.id and kwargs['menu_button'] == 'menu_button'
|
||||
|
||||
assert check_shortcut_signature(
|
||||
User.set_menu_button, Bot.set_chat_menu_button, ['chat_id'], []
|
||||
)
|
||||
assert check_shortcut_call(
|
||||
user.set_menu_button,
|
||||
user.bot,
|
||||
'set_chat_menu_button',
|
||||
shortcut_kwargs=['chat_id'],
|
||||
)
|
||||
assert check_defaults_handling(user.set_menu_button, user.bot)
|
||||
|
||||
monkeypatch.setattr(user.bot, 'set_chat_menu_button', make_assertion)
|
||||
assert user.set_menu_button(menu_button='menu_button')
|
||||
|
||||
def test_instance_method_approve_join_request(self, monkeypatch, user):
|
||||
def make_assertion(*_, **kwargs):
|
||||
chat_id = kwargs['chat_id'] == 'chat_id'
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
#!/usr/bin/env python
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2022
|
||||
# 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 as dtm
|
||||
import pytest
|
||||
|
||||
from telegram import (
|
||||
VideoChatStarted,
|
||||
VideoChatEnded,
|
||||
VideoChatParticipantsInvited,
|
||||
User,
|
||||
VideoChatScheduled,
|
||||
)
|
||||
from telegram.utils.helpers import to_timestamp
|
||||
|
||||
|
||||
@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 TestVideoChatStarted:
|
||||
def test_slot_behaviour(self, recwarn, mro_slots):
|
||||
action = VideoChatStarted()
|
||||
for attr in action.__slots__:
|
||||
assert getattr(action, attr, 'err') != 'err', f"got extra slot '{attr}'"
|
||||
assert not action.__dict__, f"got missing slot(s): {action.__dict__}"
|
||||
assert len(mro_slots(action)) == len(set(mro_slots(action))), "duplicate slot"
|
||||
action.custom = 'should give warning'
|
||||
assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list
|
||||
|
||||
def test_de_json(self):
|
||||
video_chat_started = VideoChatStarted.de_json({}, None)
|
||||
assert isinstance(video_chat_started, VideoChatStarted)
|
||||
|
||||
def test_to_dict(self):
|
||||
video_chat_started = VideoChatStarted()
|
||||
video_chat_dict = video_chat_started.to_dict()
|
||||
assert video_chat_dict == {}
|
||||
|
||||
|
||||
class TestVideoChatEnded:
|
||||
duration = 100
|
||||
|
||||
def test_slot_behaviour(self, recwarn, mro_slots):
|
||||
action = VideoChatEnded(8)
|
||||
for attr in action.__slots__:
|
||||
assert getattr(action, attr, 'err') != 'err', f"got extra slot '{attr}'"
|
||||
assert not action.__dict__, f"got missing slot(s): {action.__dict__}"
|
||||
assert len(mro_slots(action)) == len(set(mro_slots(action))), "duplicate slot"
|
||||
action.custom = 'should give warning'
|
||||
assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list
|
||||
|
||||
def test_de_json(self):
|
||||
json_dict = {'duration': self.duration}
|
||||
video_chat_ended = VideoChatEnded.de_json(json_dict, None)
|
||||
|
||||
assert video_chat_ended.duration == self.duration
|
||||
|
||||
def test_to_dict(self):
|
||||
video_chat_ended = VideoChatEnded(self.duration)
|
||||
video_chat_dict = video_chat_ended.to_dict()
|
||||
|
||||
assert isinstance(video_chat_dict, dict)
|
||||
assert video_chat_dict["duration"] == self.duration
|
||||
|
||||
def test_equality(self):
|
||||
a = VideoChatEnded(100)
|
||||
b = VideoChatEnded(100)
|
||||
c = VideoChatEnded(50)
|
||||
d = VideoChatStarted()
|
||||
|
||||
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 TestVideoChatParticipantsInvited:
|
||||
def test_slot_behaviour(self, recwarn, mro_slots):
|
||||
action = VideoChatParticipantsInvited([user1])
|
||||
for attr in action.__slots__:
|
||||
assert getattr(action, attr, 'err') != 'err', f"got extra slot '{attr}'"
|
||||
assert not action.__dict__, f"got missing slot(s): {action.__dict__}"
|
||||
assert len(mro_slots(action)) == len(set(mro_slots(action))), "duplicate slot"
|
||||
action.custom = 'should give warning'
|
||||
assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list
|
||||
|
||||
def test_de_json(self, user1, user2, bot):
|
||||
json_data = {"users": [user1.to_dict(), user2.to_dict()]}
|
||||
video_chat_participants = VideoChatParticipantsInvited.de_json(json_data, bot)
|
||||
|
||||
assert isinstance(video_chat_participants.users, list)
|
||||
assert video_chat_participants.users[0] == user1
|
||||
assert video_chat_participants.users[1] == user2
|
||||
assert video_chat_participants.users[0].id == user1.id
|
||||
assert video_chat_participants.users[1].id == user2.id
|
||||
|
||||
def test_to_dict(self, user1, user2):
|
||||
video_chat_participants = VideoChatParticipantsInvited([user1, user2])
|
||||
video_chat_dict = video_chat_participants.to_dict()
|
||||
|
||||
assert isinstance(video_chat_dict, dict)
|
||||
assert video_chat_dict["users"] == [user1.to_dict(), user2.to_dict()]
|
||||
assert video_chat_dict["users"][0]["id"] == user1.id
|
||||
assert video_chat_dict["users"][1]["id"] == user2.id
|
||||
|
||||
def test_equality(self, user1, user2):
|
||||
a = VideoChatParticipantsInvited([user1])
|
||||
b = VideoChatParticipantsInvited([user1])
|
||||
c = VideoChatParticipantsInvited([user1, user2])
|
||||
d = VideoChatParticipantsInvited([user2])
|
||||
e = VideoChatStarted()
|
||||
|
||||
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)
|
||||
|
||||
|
||||
class TestVideoChatScheduled:
|
||||
start_date = dtm.datetime.utcnow()
|
||||
|
||||
def test_slot_behaviour(self, recwarn, mro_slots):
|
||||
inst = VideoChatScheduled(self.start_date)
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'"
|
||||
assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}"
|
||||
assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot"
|
||||
inst.custom, inst.start_date = 'should give warning', self.start_date
|
||||
assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list
|
||||
|
||||
def test_expected_values(self):
|
||||
assert pytest.approx(VideoChatScheduled(start_date=self.start_date) == self.start_date)
|
||||
|
||||
def test_de_json(self, bot):
|
||||
assert VideoChatScheduled.de_json({}, bot=bot) is None
|
||||
|
||||
json_dict = {'start_date': to_timestamp(self.start_date)}
|
||||
video_chat_scheduled = VideoChatScheduled.de_json(json_dict, bot)
|
||||
|
||||
assert pytest.approx(video_chat_scheduled.start_date == self.start_date)
|
||||
|
||||
def test_to_dict(self):
|
||||
video_chat_scheduled = VideoChatScheduled(self.start_date)
|
||||
video_chat_scheduled_dict = video_chat_scheduled.to_dict()
|
||||
|
||||
assert isinstance(video_chat_scheduled_dict, dict)
|
||||
assert video_chat_scheduled_dict["start_date"] == to_timestamp(self.start_date)
|
||||
|
||||
def test_equality(self):
|
||||
a = VideoChatScheduled(self.start_date)
|
||||
b = VideoChatScheduled(self.start_date)
|
||||
c = VideoChatScheduled(dtm.datetime.utcnow() + dtm.timedelta(seconds=5))
|
||||
d = VideoChatStarted()
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
|
||||
assert a != c
|
||||
assert hash(a) != hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2022
|
||||
# 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 WebAppData
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(scope='class')
|
||||
def web_app_data():
|
||||
return WebAppData(
|
||||
data=TestWebAppData.data,
|
||||
button_text=TestWebAppData.button_text,
|
||||
)
|
||||
|
||||
|
||||
class TestWebAppData:
|
||||
data = 'data'
|
||||
button_text = 'button_text'
|
||||
|
||||
def test_slot_behaviour(self, web_app_data, mro_slots):
|
||||
for attr in web_app_data.__slots__:
|
||||
assert getattr(web_app_data, attr, 'err') != 'err', f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(web_app_data)) == len(set(mro_slots(web_app_data))), "duplicate slot"
|
||||
|
||||
def test_to_dict(self, web_app_data):
|
||||
web_app_data_dict = web_app_data.to_dict()
|
||||
|
||||
assert isinstance(web_app_data_dict, dict)
|
||||
assert web_app_data_dict['data'] == self.data
|
||||
assert web_app_data_dict['button_text'] == self.button_text
|
||||
|
||||
def test_de_json(self, bot):
|
||||
json_dict = {'data': self.data, 'button_text': self.button_text}
|
||||
web_app_data = WebAppData.de_json(json_dict, bot)
|
||||
|
||||
assert web_app_data.data == self.data
|
||||
assert web_app_data.button_text == self.button_text
|
||||
|
||||
def test_equality(self):
|
||||
a = WebAppData(self.data, self.button_text)
|
||||
b = WebAppData(self.data, self.button_text)
|
||||
c = WebAppData("", "")
|
||||
d = WebAppData("not_data", "not_button_text")
|
||||
|
||||
assert a == b
|
||||
assert hash(a) == hash(b)
|
||||
assert a is not b
|
||||
|
||||
assert a != c
|
||||
assert hash(a) != hash(c)
|
||||
|
||||
assert a != d
|
||||
assert hash(a) != hash(d)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user