mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2025-03-14 03:41:49 +01:00
API 6.0 (#3027)
Co-authored-by: Harshil <37377066+harshil21@users.noreply.github.com>
This commit is contained in:
parent
92cb6f3ae8
commit
ef2a0527fe
64 changed files with 2728 additions and 261 deletions
28
.github/workflows/test.yml
vendored
28
.github/workflows/test.yml
vendored
|
@ -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:
|
||||
|
|
|
@ -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.0-blue?logo=telegram
|
||||
:target: https://core.telegram.org/bots/api-changelog
|
||||
:alt: Supported Bot API versions
|
||||
|
||||
|
@ -87,13 +87,14 @@ 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
|
||||
|
@ -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.0** are supported.
|
||||
|
||||
==========
|
||||
Installing
|
||||
|
|
|
@ -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.0-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.0** are supported.
|
||||
|
||||
==========
|
||||
Installing
|
||||
|
|
8
docs/source/telegram.chatadministratorrights.rst
Normal file
8
docs/source/telegram.chatadministratorrights.rst
Normal file
|
@ -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:
|
8
docs/source/telegram.menubutton.rst
Normal file
8
docs/source/telegram.menubutton.rst
Normal file
|
@ -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:
|
8
docs/source/telegram.menubuttoncommands.rst
Normal file
8
docs/source/telegram.menubuttoncommands.rst
Normal file
|
@ -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:
|
8
docs/source/telegram.menubuttondefault.rst
Normal file
8
docs/source/telegram.menubuttondefault.rst
Normal file
|
@ -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:
|
8
docs/source/telegram.menubuttonwebapp.rst
Normal file
8
docs/source/telegram.menubuttonwebapp.rst
Normal file
|
@ -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
|
||||
|
|
8
docs/source/telegram.sentwebappmessage.rst
Normal file
8
docs/source/telegram.sentwebappmessage.rst
Normal file
|
@ -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:
|
9
docs/source/telegram.videochatended.rst
Normal file
9
docs/source/telegram.videochatended.rst
Normal file
|
@ -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:
|
||||
|
8
docs/source/telegram.videochatparticipantsinvited.rst
Normal file
8
docs/source/telegram.videochatparticipantsinvited.rst
Normal file
|
@ -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:
|
8
docs/source/telegram.videochatscheduled.rst
Normal file
8
docs/source/telegram.videochatscheduled.rst
Normal file
|
@ -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:
|
8
docs/source/telegram.videochatstarted.rst
Normal file
8
docs/source/telegram.videochatstarted.rst
Normal file
|
@ -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.
|
||||
|
|
8
docs/source/telegram.webappdata.rst
Normal file
8
docs/source/telegram.webappdata.rst
Normal file
|
@ -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:
|
8
docs/source/telegram.webappinfo.rst
Normal file
8
docs/source/telegram.webappinfo.rst
Normal file
|
@ -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:
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
1
setup.py
1
setup.py
|
@ -98,7 +98,6 @@ 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',
|
||||
|
|
|
@ -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',
|
||||
)
|
||||
|
|
249
telegram/bot.py
249
telegram/bot.py
|
@ -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
|
||||
|
@ -3897,6 +3900,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 +4020,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 +4045,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 +4084,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 +4112,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 +5441,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 +5832,88 @@ 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]
|
||||
|
||||
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 +6030,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 +6098,11 @@ 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`"""
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -590,6 +590,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 +601,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 +624,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 +1818,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,
|
||||
)
|
||||
|
|
167
telegram/chatadministratorrights.py
Normal file
167
telegram/chatadministratorrights.py
Normal file
|
@ -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)
|
|
@ -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.0`. 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.0'
|
||||
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'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1182,6 +1182,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 +1251,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 +1297,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
|
||||
|
||||
"""
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
179
telegram/menubutton.py
Normal file
179
telegram/menubutton.py
Normal file
|
@ -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)
|
|
@ -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
|
||||
|
|
52
telegram/sentwebappmessage.py
Normal file
52
telegram/sentwebappmessage.py
Normal file
|
@ -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,)
|
|
@ -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,
|
||||
|
@ -1241,3 +1241,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,
|
||||
)
|
||||
|
|
167
telegram/videochat.py
Normal file
167
telegram/videochat.py
Normal file
|
@ -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
|
|
@ -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
|
||||
|
|
58
telegram/webappdata.py
Normal file
58
telegram/webappdata.py
Normal file
|
@ -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)
|
52
telegram/webappinfo.py
Normal file
52
telegram/webappinfo.py
Normal file
|
@ -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,)
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
@ -1701,6 +1730,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 +1768,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 +1788,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 +1973,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 +2158,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 = [
|
||||
|
|
|
@ -706,6 +706,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
|
||||
|
|
130
tests/test_chatadministratorrights.py
Normal file
130
tests/test_chatadministratorrights.py
Normal file
|
@ -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')
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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)
|
||||
|
|
176
tests/test_menubutton.py
Normal file
176
tests/test_menubutton.py
Normal file
|
@ -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)
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
66
tests/test_sentwebappmessage.py
Normal file
66
tests/test_sentwebappmessage.py
Normal file
|
@ -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)
|
|
@ -430,6 +430,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'
|
||||
|
|
195
tests/test_videochat.py
Normal file
195
tests/test_videochat.py
Normal file
|
@ -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)
|
70
tests/test_webappdata.py
Normal file
70
tests/test_webappdata.py
Normal file
|
@ -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)
|
64
tests/test_webappinfo.py
Normal file
64
tests/test_webappinfo.py
Normal file
|
@ -0,0 +1,64 @@
|
|||
#!/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 WebAppInfo
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(scope='class')
|
||||
def web_app_info():
|
||||
return WebAppInfo(url=TestWebAppInfo.url)
|
||||
|
||||
|
||||
class TestWebAppInfo:
|
||||
url = "https://www.example.com"
|
||||
|
||||
def test_slot_behaviour(self, web_app_info, mro_slots):
|
||||
for attr in web_app_info.__slots__:
|
||||
assert getattr(web_app_info, attr, 'err') != 'err', f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(web_app_info)) == len(set(mro_slots(web_app_info))), "duplicate slot"
|
||||
|
||||
def test_to_dict(self, web_app_info):
|
||||
web_app_info_dict = web_app_info.to_dict()
|
||||
|
||||
assert isinstance(web_app_info_dict, dict)
|
||||
assert web_app_info_dict['url'] == self.url
|
||||
|
||||
def test_de_json(self, bot):
|
||||
json_dict = {'url': self.url}
|
||||
web_app_info = WebAppInfo.de_json(json_dict, bot)
|
||||
|
||||
assert web_app_info.url == self.url
|
||||
|
||||
def test_equality(self):
|
||||
a = WebAppInfo(self.url)
|
||||
b = WebAppInfo(self.url)
|
||||
c = WebAppInfo("")
|
||||
d = WebAppInfo("not_url")
|
||||
|
||||
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)
|
|
@ -16,10 +16,12 @@
|
|||
#
|
||||
# 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 datetime import datetime
|
||||
import pytest
|
||||
import time
|
||||
|
||||
from telegram import WebhookInfo, LoginUrl
|
||||
from telegram.utils.helpers import from_timestamp
|
||||
|
||||
|
||||
@pytest.fixture(scope='class')
|
||||
|
@ -32,6 +34,7 @@ def webhook_info():
|
|||
last_error_date=TestWebhookInfo.last_error_date,
|
||||
max_connections=TestWebhookInfo.max_connections,
|
||||
allowed_updates=TestWebhookInfo.allowed_updates,
|
||||
last_synchronization_error_date=TestWebhookInfo.last_synchronization_error_date,
|
||||
)
|
||||
|
||||
|
||||
|
@ -43,6 +46,7 @@ class TestWebhookInfo:
|
|||
last_error_date = time.time()
|
||||
max_connections = 42
|
||||
allowed_updates = ['type1', 'type2']
|
||||
last_synchronization_error_date = time.time()
|
||||
|
||||
def test_slot_behaviour(self, webhook_info, mro_slots, recwarn):
|
||||
for attr in webhook_info.__slots__:
|
||||
|
@ -62,6 +66,39 @@ class TestWebhookInfo:
|
|||
assert webhook_info_dict['max_connections'] == self.max_connections
|
||||
assert webhook_info_dict['allowed_updates'] == self.allowed_updates
|
||||
assert webhook_info_dict['ip_address'] == self.ip_address
|
||||
assert (
|
||||
webhook_info_dict['last_synchronization_error_date']
|
||||
== self.last_synchronization_error_date
|
||||
)
|
||||
|
||||
def test_de_json(self, bot):
|
||||
json_dict = {
|
||||
'url': self.url,
|
||||
'has_custom_certificate': self.has_custom_certificate,
|
||||
'pending_update_count': self.pending_update_count,
|
||||
'last_error_date': self.last_error_date,
|
||||
'max_connections': self.max_connections,
|
||||
'allowed_updates': self.allowed_updates,
|
||||
'ip_address': self.ip_address,
|
||||
'last_synchronization_error_date': self.last_synchronization_error_date,
|
||||
}
|
||||
webhook_info = WebhookInfo.de_json(json_dict, bot)
|
||||
|
||||
assert webhook_info.url == self.url
|
||||
assert webhook_info.has_custom_certificate == self.has_custom_certificate
|
||||
assert webhook_info.pending_update_count == self.pending_update_count
|
||||
assert isinstance(webhook_info.last_error_date, datetime)
|
||||
assert webhook_info.last_error_date == from_timestamp(self.last_error_date)
|
||||
assert webhook_info.max_connections == self.max_connections
|
||||
assert webhook_info.allowed_updates == self.allowed_updates
|
||||
assert webhook_info.ip_address == self.ip_address
|
||||
assert isinstance(webhook_info.last_synchronization_error_date, datetime)
|
||||
assert webhook_info.last_synchronization_error_date == from_timestamp(
|
||||
self.last_synchronization_error_date
|
||||
)
|
||||
|
||||
none = WebhookInfo.de_json(None, bot)
|
||||
assert none is None
|
||||
|
||||
def test_equality(self):
|
||||
a = WebhookInfo(
|
||||
|
@ -70,6 +107,7 @@ class TestWebhookInfo:
|
|||
pending_update_count=self.pending_update_count,
|
||||
last_error_date=self.last_error_date,
|
||||
max_connections=self.max_connections,
|
||||
last_synchronization_error_date=self.last_synchronization_error_date,
|
||||
)
|
||||
b = WebhookInfo(
|
||||
url=self.url,
|
||||
|
|
Loading…
Add table
Reference in a new issue