mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2024-11-21 22:56:38 +01:00
API 6.8 (#3853)
Co-authored-by: Aditya <clot27@apx_managed.vanilla> Co-authored-by: Bibo-Joshi <22366557+Bibo-Joshi@users.noreply.github.com>
This commit is contained in:
parent
caffb9d66e
commit
bd24da29cd
23 changed files with 446 additions and 38 deletions
|
@ -14,7 +14,7 @@
|
||||||
:target: https://pypi.org/project/python-telegram-bot/
|
:target: https://pypi.org/project/python-telegram-bot/
|
||||||
:alt: Supported Python versions
|
:alt: Supported Python versions
|
||||||
|
|
||||||
.. image:: https://img.shields.io/badge/Bot%20API-6.7-blue?logo=telegram
|
.. image:: https://img.shields.io/badge/Bot%20API-6.8-blue?logo=telegram
|
||||||
:target: https://core.telegram.org/bots/api-changelog
|
:target: https://core.telegram.org/bots/api-changelog
|
||||||
:alt: Supported Bot API versions
|
:alt: Supported Bot API versions
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ Installing both ``python-telegram-bot`` and ``python-telegram-bot-raw`` in conju
|
||||||
Telegram API support
|
Telegram API support
|
||||||
====================
|
====================
|
||||||
|
|
||||||
All types and methods of the Telegram Bot API **6.7** are supported.
|
All types and methods of the Telegram Bot API **6.8** are supported.
|
||||||
|
|
||||||
Installing
|
Installing
|
||||||
==========
|
==========
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
:target: https://pypi.org/project/python-telegram-bot-raw/
|
:target: https://pypi.org/project/python-telegram-bot-raw/
|
||||||
:alt: Supported Python versions
|
:alt: Supported Python versions
|
||||||
|
|
||||||
.. image:: https://img.shields.io/badge/Bot%20API-6.7-blue?logo=telegram
|
.. image:: https://img.shields.io/badge/Bot%20API-6.8-blue?logo=telegram
|
||||||
:target: https://core.telegram.org/bots/api-changelog
|
:target: https://core.telegram.org/bots/api-changelog
|
||||||
:alt: Supported Bot API versions
|
:alt: Supported Bot API versions
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ Installing both ``python-telegram-bot`` and ``python-telegram-bot-raw`` in conju
|
||||||
Telegram API support
|
Telegram API support
|
||||||
====================
|
====================
|
||||||
|
|
||||||
All types and methods of the Telegram Bot API **6.7** are supported.
|
All types and methods of the Telegram Bot API **6.8** are supported.
|
||||||
|
|
||||||
Installing
|
Installing
|
||||||
==========
|
==========
|
||||||
|
|
|
@ -328,6 +328,8 @@
|
||||||
- Used to reopen the general topic
|
- Used to reopen the general topic
|
||||||
* - :meth:`~telegram.Bot.unpin_all_forum_topic_messages`
|
* - :meth:`~telegram.Bot.unpin_all_forum_topic_messages`
|
||||||
- Used to unpin all messages in a forum topic
|
- Used to unpin all messages in a forum topic
|
||||||
|
* - :meth:`~telegram.Bot.unpin_all_general_forum_topic_messages`
|
||||||
|
- Used to unpin all messages in the general forum topic
|
||||||
|
|
||||||
.. raw:: html
|
.. raw:: html
|
||||||
|
|
||||||
|
|
|
@ -79,6 +79,7 @@ Available Types
|
||||||
telegram.replykeyboardmarkup
|
telegram.replykeyboardmarkup
|
||||||
telegram.replykeyboardremove
|
telegram.replykeyboardremove
|
||||||
telegram.sentwebappmessage
|
telegram.sentwebappmessage
|
||||||
|
telegram.story
|
||||||
telegram.switchinlinequerychosenchat
|
telegram.switchinlinequerychosenchat
|
||||||
telegram.telegramobject
|
telegram.telegramobject
|
||||||
telegram.update
|
telegram.update
|
||||||
|
|
6
docs/source/telegram.story.rst
Normal file
6
docs/source/telegram.story.rst
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
Story
|
||||||
|
=====
|
||||||
|
|
||||||
|
.. autoclass:: telegram.Story
|
||||||
|
:members:
|
||||||
|
:show-inheritance:
|
|
@ -170,6 +170,7 @@ __all__ = ( # Keep this alphabetically ordered
|
||||||
"ShippingQuery",
|
"ShippingQuery",
|
||||||
"Sticker",
|
"Sticker",
|
||||||
"StickerSet",
|
"StickerSet",
|
||||||
|
"Story",
|
||||||
"SuccessfulPayment",
|
"SuccessfulPayment",
|
||||||
"SwitchInlineQueryChosenChat",
|
"SwitchInlineQueryChosenChat",
|
||||||
"TelegramObject",
|
"TelegramObject",
|
||||||
|
@ -341,6 +342,7 @@ from ._replykeyboardmarkup import ReplyKeyboardMarkup
|
||||||
from ._replykeyboardremove import ReplyKeyboardRemove
|
from ._replykeyboardremove import ReplyKeyboardRemove
|
||||||
from ._sentwebappmessage import SentWebAppMessage
|
from ._sentwebappmessage import SentWebAppMessage
|
||||||
from ._shared import ChatShared, UserShared
|
from ._shared import ChatShared, UserShared
|
||||||
|
from ._story import Story
|
||||||
from ._switchinlinequerychosenchat import SwitchInlineQueryChosenChat
|
from ._switchinlinequerychosenchat import SwitchInlineQueryChosenChat
|
||||||
from ._telegramobject import TelegramObject
|
from ._telegramobject import TelegramObject
|
||||||
from ._update import Update
|
from ._update import Update
|
||||||
|
|
|
@ -7814,6 +7814,46 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||||
api_kwargs=api_kwargs,
|
api_kwargs=api_kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@_log
|
||||||
|
async def unpin_all_general_forum_topic_messages(
|
||||||
|
self,
|
||||||
|
chat_id: Union[str, int],
|
||||||
|
*,
|
||||||
|
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||||
|
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||||
|
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||||
|
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||||
|
api_kwargs: Optional[JSONDict] = None,
|
||||||
|
) -> bool:
|
||||||
|
"""
|
||||||
|
Use this method to clear the list of pinned messages in a General forum topic. The bot must
|
||||||
|
be an administrator in the chat for this to work and must have
|
||||||
|
:paramref:`~telegram.ChatAdministratorRights.can_pin_messages` administrator rights in the
|
||||||
|
supergroup.
|
||||||
|
|
||||||
|
.. versionadded:: NEXT.VERSION
|
||||||
|
|
||||||
|
Args:
|
||||||
|
chat_id (:obj:`int` | :obj:`str`): |chat_id_group|
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
:obj:`bool`: On success, :obj:`True` is returned.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
:class:`telegram.error.TelegramError`
|
||||||
|
"""
|
||||||
|
data: JSONDict = {"chat_id": chat_id}
|
||||||
|
|
||||||
|
return await self._post(
|
||||||
|
"unpinAllGeneralForumTopicMessages",
|
||||||
|
data,
|
||||||
|
read_timeout=read_timeout,
|
||||||
|
write_timeout=write_timeout,
|
||||||
|
connect_timeout=connect_timeout,
|
||||||
|
pool_timeout=pool_timeout,
|
||||||
|
api_kwargs=api_kwargs,
|
||||||
|
)
|
||||||
|
|
||||||
@_log
|
@_log
|
||||||
async def edit_general_forum_topic(
|
async def edit_general_forum_topic(
|
||||||
self,
|
self,
|
||||||
|
@ -8527,3 +8567,5 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
||||||
"""Alias for :meth:`set_my_name`"""
|
"""Alias for :meth:`set_my_name`"""
|
||||||
getMyName = get_my_name
|
getMyName = get_my_name
|
||||||
"""Alias for :meth:`get_my_name`"""
|
"""Alias for :meth:`get_my_name`"""
|
||||||
|
unpinAllGeneralForumTopicMessages = unpin_all_general_forum_topic_messages
|
||||||
|
"""Alias for :meth:`unpin_all_general_forum_topic_messages`"""
|
||||||
|
|
|
@ -31,6 +31,7 @@ from telegram._menubutton import MenuButton
|
||||||
from telegram._telegramobject import TelegramObject
|
from telegram._telegramobject import TelegramObject
|
||||||
from telegram._utils import enum
|
from telegram._utils import enum
|
||||||
from telegram._utils.argumentparsing import parse_sequence_arg
|
from telegram._utils.argumentparsing import parse_sequence_arg
|
||||||
|
from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp
|
||||||
from telegram._utils.defaultvalue import DEFAULT_NONE
|
from telegram._utils.defaultvalue import DEFAULT_NONE
|
||||||
from telegram._utils.types import (
|
from telegram._utils.types import (
|
||||||
CorrectOptionID,
|
CorrectOptionID,
|
||||||
|
@ -172,6 +173,12 @@ class Chat(TelegramObject):
|
||||||
:meth:`telegram.Bot.get_chat`.
|
:meth:`telegram.Bot.get_chat`.
|
||||||
|
|
||||||
.. versionadded:: 20.0
|
.. versionadded:: 20.0
|
||||||
|
emoji_status_expiration_date (:class:`datetime.datetime`, optional): Expiration date of
|
||||||
|
emoji status of the other party in a private chat, in seconds. Returned only in
|
||||||
|
:meth:`telegram.Bot.get_chat`.
|
||||||
|
|datetime_localization|
|
||||||
|
|
||||||
|
.. versionadded:: NEXT.VERSION
|
||||||
has_aggressive_anti_spam_enabled (:obj:`bool`, optional): :obj:`True`, if aggressive
|
has_aggressive_anti_spam_enabled (:obj:`bool`, optional): :obj:`True`, if aggressive
|
||||||
anti-spam checks are enabled in the supergroup. The field is only available to chat
|
anti-spam checks are enabled in the supergroup. The field is only available to chat
|
||||||
administrators. Returned only in :meth:`telegram.Bot.get_chat`.
|
administrators. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||||
|
@ -265,6 +272,12 @@ class Chat(TelegramObject):
|
||||||
:meth:`telegram.Bot.get_chat`.
|
:meth:`telegram.Bot.get_chat`.
|
||||||
|
|
||||||
.. versionadded:: 20.0
|
.. versionadded:: 20.0
|
||||||
|
emoji_status_expiration_date (:class:`datetime.datetime`, optional): Expiration date of
|
||||||
|
emoji status of the other party in a private chat, in seconds. Returned only in
|
||||||
|
:meth:`telegram.Bot.get_chat`.
|
||||||
|
|datetime_localization|
|
||||||
|
|
||||||
|
.. versionadded:: NEXT.VERSION
|
||||||
has_aggressive_anti_spam_enabled (:obj:`bool`): Optional. :obj:`True`, if aggressive
|
has_aggressive_anti_spam_enabled (:obj:`bool`): Optional. :obj:`True`, if aggressive
|
||||||
anti-spam checks are enabled in the supergroup. The field is only available to chat
|
anti-spam checks are enabled in the supergroup. The field is only available to chat
|
||||||
administrators. Returned only in :meth:`telegram.Bot.get_chat`.
|
administrators. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||||
|
@ -306,6 +319,7 @@ class Chat(TelegramObject):
|
||||||
"is_forum",
|
"is_forum",
|
||||||
"active_usernames",
|
"active_usernames",
|
||||||
"emoji_status_custom_emoji_id",
|
"emoji_status_custom_emoji_id",
|
||||||
|
"emoji_status_expiration_date",
|
||||||
"has_hidden_members",
|
"has_hidden_members",
|
||||||
"has_aggressive_anti_spam_enabled",
|
"has_aggressive_anti_spam_enabled",
|
||||||
)
|
)
|
||||||
|
@ -352,6 +366,7 @@ class Chat(TelegramObject):
|
||||||
is_forum: Optional[bool] = None,
|
is_forum: Optional[bool] = None,
|
||||||
active_usernames: Optional[Sequence[str]] = None,
|
active_usernames: Optional[Sequence[str]] = None,
|
||||||
emoji_status_custom_emoji_id: Optional[str] = None,
|
emoji_status_custom_emoji_id: Optional[str] = None,
|
||||||
|
emoji_status_expiration_date: Optional[datetime] = None,
|
||||||
has_aggressive_anti_spam_enabled: Optional[bool] = None,
|
has_aggressive_anti_spam_enabled: Optional[bool] = None,
|
||||||
has_hidden_members: Optional[bool] = None,
|
has_hidden_members: Optional[bool] = None,
|
||||||
*,
|
*,
|
||||||
|
@ -390,6 +405,7 @@ class Chat(TelegramObject):
|
||||||
self.is_forum: Optional[bool] = is_forum
|
self.is_forum: Optional[bool] = is_forum
|
||||||
self.active_usernames: Tuple[str, ...] = parse_sequence_arg(active_usernames)
|
self.active_usernames: Tuple[str, ...] = parse_sequence_arg(active_usernames)
|
||||||
self.emoji_status_custom_emoji_id: Optional[str] = emoji_status_custom_emoji_id
|
self.emoji_status_custom_emoji_id: Optional[str] = emoji_status_custom_emoji_id
|
||||||
|
self.emoji_status_expiration_date: Optional[datetime] = emoji_status_expiration_date
|
||||||
self.has_aggressive_anti_spam_enabled: Optional[bool] = has_aggressive_anti_spam_enabled
|
self.has_aggressive_anti_spam_enabled: Optional[bool] = has_aggressive_anti_spam_enabled
|
||||||
self.has_hidden_members: Optional[bool] = has_hidden_members
|
self.has_hidden_members: Optional[bool] = has_hidden_members
|
||||||
|
|
||||||
|
@ -446,6 +462,13 @@ class Chat(TelegramObject):
|
||||||
if not data:
|
if not data:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
# Get the local timezone from the bot if it has defaults
|
||||||
|
loc_tzinfo = extract_tzinfo_from_defaults(bot)
|
||||||
|
|
||||||
|
data["emoji_status_expiration_date"] = from_timestamp(
|
||||||
|
data.get("emoji_status_expiration_date"), tzinfo=loc_tzinfo
|
||||||
|
)
|
||||||
|
|
||||||
data["photo"] = ChatPhoto.de_json(data.get("photo"), bot)
|
data["photo"] = ChatPhoto.de_json(data.get("photo"), bot)
|
||||||
from telegram import Message # pylint: disable=import-outside-toplevel
|
from telegram import Message # pylint: disable=import-outside-toplevel
|
||||||
|
|
||||||
|
@ -2904,6 +2927,37 @@ class Chat(TelegramObject):
|
||||||
api_kwargs=api_kwargs,
|
api_kwargs=api_kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def unpin_all_general_forum_topic_messages(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||||
|
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||||
|
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||||
|
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||||
|
api_kwargs: Optional[JSONDict] = None,
|
||||||
|
) -> bool:
|
||||||
|
"""Shortcut for::
|
||||||
|
|
||||||
|
await bot.unpin_all_general_forum_topic_messages(chat_id=update.effective_chat.id,
|
||||||
|
*args, **kwargs)
|
||||||
|
|
||||||
|
For the documentation of the arguments, please see
|
||||||
|
:meth:`telegram.Bot.unpin_all_general_forum_topic_messages`.
|
||||||
|
|
||||||
|
.. versionadded:: NEXT.VERSION
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
:obj:`bool`: On success, :obj:`True` is returned.
|
||||||
|
"""
|
||||||
|
return await self.get_bot().unpin_all_general_forum_topic_messages(
|
||||||
|
chat_id=self.id,
|
||||||
|
read_timeout=read_timeout,
|
||||||
|
write_timeout=write_timeout,
|
||||||
|
connect_timeout=connect_timeout,
|
||||||
|
pool_timeout=pool_timeout,
|
||||||
|
api_kwargs=api_kwargs,
|
||||||
|
)
|
||||||
|
|
||||||
async def edit_general_forum_topic(
|
async def edit_general_forum_topic(
|
||||||
self,
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
|
|
|
@ -53,6 +53,7 @@ from telegram._payment.successfulpayment import SuccessfulPayment
|
||||||
from telegram._poll import Poll
|
from telegram._poll import Poll
|
||||||
from telegram._proximityalerttriggered import ProximityAlertTriggered
|
from telegram._proximityalerttriggered import ProximityAlertTriggered
|
||||||
from telegram._shared import ChatShared, UserShared
|
from telegram._shared import ChatShared, UserShared
|
||||||
|
from telegram._story import Story
|
||||||
from telegram._telegramobject import TelegramObject
|
from telegram._telegramobject import TelegramObject
|
||||||
from telegram._user import User
|
from telegram._user import User
|
||||||
from telegram._utils.argumentparsing import parse_sequence_arg
|
from telegram._utils.argumentparsing import parse_sequence_arg
|
||||||
|
@ -201,6 +202,9 @@ class Message(TelegramObject):
|
||||||
|
|
||||||
sticker (:class:`telegram.Sticker`, optional): Message is a sticker, information
|
sticker (:class:`telegram.Sticker`, optional): Message is a sticker, information
|
||||||
about the sticker.
|
about the sticker.
|
||||||
|
story (:class:`telegram.Story`, optional): Message is a forwarded story.
|
||||||
|
|
||||||
|
.. versionadded:: NEXT.VERSION
|
||||||
video (:class:`telegram.Video`, optional): Message is a video, information about the
|
video (:class:`telegram.Video`, optional): Message is a video, information about the
|
||||||
video.
|
video.
|
||||||
voice (:class:`telegram.Voice`, optional): Message is a voice message, information about
|
voice (:class:`telegram.Voice`, optional): Message is a voice message, information about
|
||||||
|
@ -435,6 +439,9 @@ class Message(TelegramObject):
|
||||||
about the sticker.
|
about the sticker.
|
||||||
|
|
||||||
.. seealso:: :wiki:`Working with Files and Media <Working-with-Files-and-Media>`
|
.. seealso:: :wiki:`Working with Files and Media <Working-with-Files-and-Media>`
|
||||||
|
story (:class:`telegram.Story`): Optional. Message is a forwarded story.
|
||||||
|
|
||||||
|
.. versionadded:: NEXT.VERSION
|
||||||
video (:class:`telegram.Video`): Optional. Message is a video, information about the
|
video (:class:`telegram.Video`): Optional. Message is a video, information about the
|
||||||
video.
|
video.
|
||||||
|
|
||||||
|
@ -671,6 +678,7 @@ class Message(TelegramObject):
|
||||||
"has_media_spoiler",
|
"has_media_spoiler",
|
||||||
"user_shared",
|
"user_shared",
|
||||||
"chat_shared",
|
"chat_shared",
|
||||||
|
"story",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
|
@ -746,6 +754,7 @@ class Message(TelegramObject):
|
||||||
has_media_spoiler: Optional[bool] = None,
|
has_media_spoiler: Optional[bool] = None,
|
||||||
user_shared: Optional[UserShared] = None,
|
user_shared: Optional[UserShared] = None,
|
||||||
chat_shared: Optional[ChatShared] = None,
|
chat_shared: Optional[ChatShared] = None,
|
||||||
|
story: Optional[Story] = None,
|
||||||
*,
|
*,
|
||||||
api_kwargs: Optional[JSONDict] = None,
|
api_kwargs: Optional[JSONDict] = None,
|
||||||
):
|
):
|
||||||
|
@ -834,6 +843,7 @@ class Message(TelegramObject):
|
||||||
self.has_media_spoiler: Optional[bool] = has_media_spoiler
|
self.has_media_spoiler: Optional[bool] = has_media_spoiler
|
||||||
self.user_shared: Optional[UserShared] = user_shared
|
self.user_shared: Optional[UserShared] = user_shared
|
||||||
self.chat_shared: Optional[ChatShared] = chat_shared
|
self.chat_shared: Optional[ChatShared] = chat_shared
|
||||||
|
self.story: Optional[Story] = story
|
||||||
|
|
||||||
self._effective_attachment = DEFAULT_NONE
|
self._effective_attachment = DEFAULT_NONE
|
||||||
|
|
||||||
|
@ -903,6 +913,7 @@ class Message(TelegramObject):
|
||||||
data["game"] = Game.de_json(data.get("game"), bot)
|
data["game"] = Game.de_json(data.get("game"), bot)
|
||||||
data["photo"] = PhotoSize.de_list(data.get("photo"), bot)
|
data["photo"] = PhotoSize.de_list(data.get("photo"), bot)
|
||||||
data["sticker"] = Sticker.de_json(data.get("sticker"), bot)
|
data["sticker"] = Sticker.de_json(data.get("sticker"), bot)
|
||||||
|
data["story"] = Story.de_json(data.get("story"), bot)
|
||||||
data["video"] = Video.de_json(data.get("video"), bot)
|
data["video"] = Video.de_json(data.get("video"), bot)
|
||||||
data["voice"] = Voice.de_json(data.get("voice"), bot)
|
data["voice"] = Voice.de_json(data.get("voice"), bot)
|
||||||
data["video_note"] = VideoNote.de_json(data.get("video_note"), bot)
|
data["video_note"] = VideoNote.de_json(data.get("video_note"), bot)
|
||||||
|
@ -973,6 +984,7 @@ class Message(TelegramObject):
|
||||||
Sequence[PhotoSize],
|
Sequence[PhotoSize],
|
||||||
Poll,
|
Poll,
|
||||||
Sticker,
|
Sticker,
|
||||||
|
Story,
|
||||||
SuccessfulPayment,
|
SuccessfulPayment,
|
||||||
Venue,
|
Venue,
|
||||||
Video,
|
Video,
|
||||||
|
@ -995,6 +1007,7 @@ class Message(TelegramObject):
|
||||||
* List[:class:`telegram.PhotoSize`]
|
* List[:class:`telegram.PhotoSize`]
|
||||||
* :class:`telegram.Poll`
|
* :class:`telegram.Poll`
|
||||||
* :class:`telegram.Sticker`
|
* :class:`telegram.Sticker`
|
||||||
|
* :class:`telegram.Story`
|
||||||
* :class:`telegram.SuccessfulPayment`
|
* :class:`telegram.SuccessfulPayment`
|
||||||
* :class:`telegram.Venue`
|
* :class:`telegram.Venue`
|
||||||
* :class:`telegram.Video`
|
* :class:`telegram.Video`
|
||||||
|
|
|
@ -21,6 +21,7 @@ import datetime
|
||||||
from typing import TYPE_CHECKING, Dict, Final, List, Optional, Sequence, Tuple
|
from typing import TYPE_CHECKING, Dict, Final, List, Optional, Sequence, Tuple
|
||||||
|
|
||||||
from telegram import constants
|
from telegram import constants
|
||||||
|
from telegram._chat import Chat
|
||||||
from telegram._messageentity import MessageEntity
|
from telegram._messageentity import MessageEntity
|
||||||
from telegram._telegramobject import TelegramObject
|
from telegram._telegramobject import TelegramObject
|
||||||
from telegram._user import User
|
from telegram._user import User
|
||||||
|
@ -28,6 +29,8 @@ from telegram._utils import enum
|
||||||
from telegram._utils.argumentparsing import parse_sequence_arg
|
from telegram._utils.argumentparsing import parse_sequence_arg
|
||||||
from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp
|
from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp
|
||||||
from telegram._utils.types import JSONDict
|
from telegram._utils.types import JSONDict
|
||||||
|
from telegram._utils.warnings import warn
|
||||||
|
from telegram.warnings import PTBDeprecationWarning
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from telegram import Bot
|
from telegram import Bot
|
||||||
|
@ -84,42 +87,86 @@ class PollAnswer(TelegramObject):
|
||||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||||
considered equal, if their :attr:`poll_id`, :attr:`user` and :attr:`option_ids` are equal.
|
considered equal, if their :attr:`poll_id`, :attr:`user` and :attr:`option_ids` are equal.
|
||||||
|
|
||||||
|
.. versionchanged:: NEXT.VERSION
|
||||||
|
The order of :paramref:`option_ids` and :paramref:`user` is changed in
|
||||||
|
NEXT.VERSION as the latter one became optional. We currently provide
|
||||||
|
backward compatibility for this but it will be removed in the future.
|
||||||
|
Please update your code to use the new order.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
poll_id (:obj:`str`): Unique poll identifier.
|
poll_id (:obj:`str`): Unique poll identifier.
|
||||||
user (:class:`telegram.User`): The user, who changed the answer to the poll.
|
option_ids (Sequence[:obj:`int`]): Identifiers of answer options, chosen by the user. May
|
||||||
option_ids (Sequence[:obj:`int`]): 0-based identifiers of answer options, chosen by the
|
be empty if the user retracted their vote.
|
||||||
user. May be empty if the user retracted their vote.
|
|
||||||
|
|
||||||
.. versionchanged:: 20.0
|
.. versionchanged:: 20.0
|
||||||
|sequenceclassargs|
|
|sequenceclassargs|
|
||||||
|
user (:class:`telegram.User`, optional): The user that changed the answer to the poll,
|
||||||
|
if the voter isn't anonymous. If the voter is anonymous, this field will contain the
|
||||||
|
user :tg-const:`telegram.constants.ChatID.FAKE_CHANNEL` for backwards compatibility.
|
||||||
|
|
||||||
|
.. versionchanged:: NEXT.VERSION
|
||||||
|
:paramref:`user` became optional.
|
||||||
|
voter_chat (:class:`telegram.Chat`, optional): The chat that changed the answer to the
|
||||||
|
poll, if the voter is anonymous.
|
||||||
|
|
||||||
|
.. versionadded:: NEXT.VERSION
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
poll_id (:obj:`str`): Unique poll identifier.
|
poll_id (:obj:`str`): Unique poll identifier.
|
||||||
user (:class:`telegram.User`): The user, who changed the answer to the poll.
|
option_ids (Tuple[:obj:`int`]): Identifiers of answer options, chosen by the user. May
|
||||||
option_ids (Tuple[:obj:`int`]): Identifiers of answer options, chosen by the user. May be
|
be empty if the user retracted their vote.
|
||||||
empty if the user retracted their vote.
|
|
||||||
|
|
||||||
.. versionchanged:: 20.0
|
.. versionchanged:: 20.0
|
||||||
|tupleclassattrs|
|
|tupleclassattrs|
|
||||||
|
user (:class:`telegram.User`): Optional. The user, who changed the answer to the
|
||||||
|
poll, if the voter isn't anonymous. If the voter is anonymous, this field will contain
|
||||||
|
the user :tg-const:`telegram.constants.ChatID.FAKE_CHANNEL` for backwards compatibility
|
||||||
|
|
||||||
|
.. versionchanged:: NEXT.VERSION
|
||||||
|
:paramref:`user` became optional.
|
||||||
|
voter_chat (:class:`telegram.Chat`): Optional. The chat that changed the answer to the
|
||||||
|
poll, if the voter is anonymous.
|
||||||
|
|
||||||
|
.. versionadded:: NEXT.VERSION
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ("option_ids", "user", "poll_id")
|
__slots__ = ("option_ids", "poll_id", "user", "voter_chat")
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
poll_id: str,
|
poll_id: str,
|
||||||
user: User,
|
|
||||||
option_ids: Sequence[int],
|
option_ids: Sequence[int],
|
||||||
|
user: Optional[User] = None,
|
||||||
|
voter_chat: Optional[Chat] = None,
|
||||||
*,
|
*,
|
||||||
api_kwargs: Optional[JSONDict] = None,
|
api_kwargs: Optional[JSONDict] = None,
|
||||||
):
|
):
|
||||||
super().__init__(api_kwargs=api_kwargs)
|
super().__init__(api_kwargs=api_kwargs)
|
||||||
self.poll_id: str = poll_id
|
self.poll_id: str = poll_id
|
||||||
self.user: User = user
|
self.voter_chat: Optional[Chat] = voter_chat
|
||||||
self.option_ids: Tuple[int, ...] = parse_sequence_arg(option_ids)
|
|
||||||
|
|
||||||
self._id_attrs = (self.poll_id, self.user, tuple(self.option_ids))
|
if isinstance(option_ids, User) or isinstance(user, tuple):
|
||||||
|
warn(
|
||||||
|
"From v20.5 the order of `option_ids` and `user` is changed as the latter one"
|
||||||
|
" became optional. Please update your code to use the new order.",
|
||||||
|
category=PTBDeprecationWarning,
|
||||||
|
stacklevel=2,
|
||||||
|
)
|
||||||
|
self.option_ids: Tuple[int, ...] = parse_sequence_arg(user)
|
||||||
|
self.user: Optional[User] = option_ids
|
||||||
|
else:
|
||||||
|
self.option_ids: Tuple[int, ...] = parse_sequence_arg( # type: ignore[no-redef]
|
||||||
|
option_ids
|
||||||
|
)
|
||||||
|
self.user: Optional[User] = user # type: ignore[no-redef]
|
||||||
|
|
||||||
|
self._id_attrs = (
|
||||||
|
self.poll_id,
|
||||||
|
self.option_ids,
|
||||||
|
self.user,
|
||||||
|
self.voter_chat,
|
||||||
|
)
|
||||||
|
|
||||||
self._freeze()
|
self._freeze()
|
||||||
|
|
||||||
|
@ -132,6 +179,7 @@ class PollAnswer(TelegramObject):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
data["user"] = User.de_json(data.get("user"), bot)
|
data["user"] = User.de_json(data.get("user"), bot)
|
||||||
|
data["voter_chat"] = Chat.de_json(data.get("voter_chat"), bot)
|
||||||
|
|
||||||
return super().de_json(data=data, bot=bot)
|
return super().de_json(data=data, bot=bot)
|
||||||
|
|
||||||
|
|
41
telegram/_story.py
Normal file
41
telegram/_story.py
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
#
|
||||||
|
# A library that provides a Python interface to the Telegram Bot API
|
||||||
|
# Copyright (C) 2015-2023
|
||||||
|
# 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 related to a Telegram Story."""
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from telegram._telegramobject import TelegramObject
|
||||||
|
from telegram._utils.types import JSONDict
|
||||||
|
|
||||||
|
|
||||||
|
class Story(TelegramObject):
|
||||||
|
"""
|
||||||
|
This object represents a message about a forwarded story in the chat. Currently holds no
|
||||||
|
information.
|
||||||
|
|
||||||
|
.. versionadded:: NEXT.VERSION
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = ()
|
||||||
|
|
||||||
|
def __init__(self, *, api_kwargs: Optional[JSONDict] = None) -> None:
|
||||||
|
super().__init__(api_kwargs=api_kwargs)
|
||||||
|
|
||||||
|
self._freeze()
|
|
@ -116,7 +116,7 @@ class _BotAPIVersion(NamedTuple):
|
||||||
#: :data:`telegram.__bot_api_version_info__`.
|
#: :data:`telegram.__bot_api_version_info__`.
|
||||||
#:
|
#:
|
||||||
#: .. versionadded:: 20.0
|
#: .. versionadded:: 20.0
|
||||||
BOT_API_VERSION_INFO: Final[_BotAPIVersion] = _BotAPIVersion(major=6, minor=7)
|
BOT_API_VERSION_INFO: Final[_BotAPIVersion] = _BotAPIVersion(major=6, minor=8)
|
||||||
#: :obj:`str`: Telegram Bot API
|
#: :obj:`str`: Telegram Bot API
|
||||||
#: version supported by this version of `python-telegram-bot`. Also available as
|
#: version supported by this version of `python-telegram-bot`. Also available as
|
||||||
#: :data:`telegram.__bot_api_version__`.
|
#: :data:`telegram.__bot_api_version__`.
|
||||||
|
@ -285,7 +285,8 @@ class ChatID(IntEnum):
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
ANONYMOUS_ADMIN = 1087968824
|
ANONYMOUS_ADMIN = 1087968824
|
||||||
""":obj:`int`: User ID in groups for messages sent by anonymous admins.
|
""":obj:`int`: User ID in groups for messages sent by anonymous admins. Telegram chat:
|
||||||
|
`@GroupAnonymousBot <https://t.me/GroupAnonymousBot>`_.
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
:attr:`telegram.Message.from_user` will contain this ID for backwards compatibility only.
|
:attr:`telegram.Message.from_user` will contain this ID for backwards compatibility only.
|
||||||
|
@ -293,19 +294,21 @@ class ChatID(IntEnum):
|
||||||
"""
|
"""
|
||||||
SERVICE_CHAT = 777000
|
SERVICE_CHAT = 777000
|
||||||
""":obj:`int`: Telegram service chat, that also acts as sender of channel posts forwarded to
|
""":obj:`int`: Telegram service chat, that also acts as sender of channel posts forwarded to
|
||||||
discussion groups.
|
discussion groups. Telegram chat: `Telegram <https://t.me/+42777>`_.
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
:attr:`telegram.Message.from_user` will contain this ID for backwards compatibility only.
|
:attr:`telegram.Message.from_user` will contain this ID for backwards compatibility only.
|
||||||
It's recommended to use :attr:`telegram.Message.sender_chat` instead.
|
It's recommended to use :attr:`telegram.Message.sender_chat` instead.
|
||||||
"""
|
"""
|
||||||
FAKE_CHANNEL = 136817688
|
FAKE_CHANNEL = 136817688
|
||||||
""":obj:`int`: User ID in groups when message is sent on behalf of a channel.
|
""":obj:`int`: User ID in groups when message is sent on behalf of a channel, or when a channel
|
||||||
|
votes on a poll. Telegram chat: `@Channel_Bot <https://t.me/Channel_Bot>`_.
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
* :attr:`telegram.Message.from_user` will contain this ID for backwards compatibility only.
|
* :attr:`telegram.Message.from_user` will contain this ID for backwards compatibility only.
|
||||||
It's recommended to use :attr:`telegram.Message.sender_chat` instead.
|
It's recommended to use :attr:`telegram.Message.sender_chat` instead.
|
||||||
* This value is undocumented and might be changed by Telegram.
|
* :attr:`telegram.PollAnswer.user` will contain this ID for backwards compatibility only.
|
||||||
|
It's recommended to use :attr:`telegram.PollAnswer.voter_chat` instead.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@ -1065,6 +1068,8 @@ class MessageAttachmentType(StringEnum):
|
||||||
""":obj:`str`: Messages with :attr:`telegram.Message.poll`."""
|
""":obj:`str`: Messages with :attr:`telegram.Message.poll`."""
|
||||||
STICKER = "sticker"
|
STICKER = "sticker"
|
||||||
""":obj:`str`: Messages with :attr:`telegram.Message.sticker`."""
|
""":obj:`str`: Messages with :attr:`telegram.Message.sticker`."""
|
||||||
|
STORY = "story"
|
||||||
|
""":obj:`str`: Messages with :attr:`telegram.Message.story`."""
|
||||||
SUCCESSFUL_PAYMENT = "successful_payment"
|
SUCCESSFUL_PAYMENT = "successful_payment"
|
||||||
""":obj:`str`: Messages with :attr:`telegram.Message.successful_payment`."""
|
""":obj:`str`: Messages with :attr:`telegram.Message.successful_payment`."""
|
||||||
VIDEO = "video"
|
VIDEO = "video"
|
||||||
|
@ -1219,6 +1224,8 @@ class MessageType(StringEnum):
|
||||||
""":obj:`str`: Messages with :attr:`telegram.Message.poll`."""
|
""":obj:`str`: Messages with :attr:`telegram.Message.poll`."""
|
||||||
STICKER = "sticker"
|
STICKER = "sticker"
|
||||||
""":obj:`str`: Messages with :attr:`telegram.Message.sticker`."""
|
""":obj:`str`: Messages with :attr:`telegram.Message.sticker`."""
|
||||||
|
STORY = "story"
|
||||||
|
""":obj:`str`: Messages with :attr:`telegram.Message.story`."""
|
||||||
SUCCESSFUL_PAYMENT = "successful_payment"
|
SUCCESSFUL_PAYMENT = "successful_payment"
|
||||||
""":obj:`str`: Messages with :attr:`telegram.Message.successful_payment`."""
|
""":obj:`str`: Messages with :attr:`telegram.Message.successful_payment`."""
|
||||||
VIDEO = "video"
|
VIDEO = "video"
|
||||||
|
|
|
@ -3486,6 +3486,26 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||||
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
|
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def unpin_all_general_forum_topic_messages(
|
||||||
|
self,
|
||||||
|
chat_id: Union[str, int],
|
||||||
|
*,
|
||||||
|
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||||
|
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||||
|
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||||
|
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||||
|
api_kwargs: Optional[JSONDict] = None,
|
||||||
|
rate_limit_args: Optional[RLARGS] = None,
|
||||||
|
) -> bool:
|
||||||
|
return await super().unpin_all_general_forum_topic_messages(
|
||||||
|
chat_id=chat_id,
|
||||||
|
read_timeout=read_timeout,
|
||||||
|
write_timeout=write_timeout,
|
||||||
|
connect_timeout=connect_timeout,
|
||||||
|
pool_timeout=pool_timeout,
|
||||||
|
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
|
||||||
|
)
|
||||||
|
|
||||||
async def upload_sticker_file(
|
async def upload_sticker_file(
|
||||||
self,
|
self,
|
||||||
user_id: Union[str, int],
|
user_id: Union[str, int],
|
||||||
|
@ -3884,3 +3904,4 @@ class ExtBot(Bot, Generic[RLARGS]):
|
||||||
setStickerMaskPosition = set_sticker_mask_position
|
setStickerMaskPosition = set_sticker_mask_position
|
||||||
setMyName = set_my_name
|
setMyName = set_my_name
|
||||||
getMyName = get_my_name
|
getMyName = get_my_name
|
||||||
|
unpinAllGeneralForumTopicMessages = unpin_all_general_forum_topic_messages
|
||||||
|
|
|
@ -72,6 +72,7 @@ __all__ = (
|
||||||
"REPLY",
|
"REPLY",
|
||||||
"Regex",
|
"Regex",
|
||||||
"Sticker",
|
"Sticker",
|
||||||
|
"STORY",
|
||||||
"SUCCESSFUL_PAYMENT",
|
"SUCCESSFUL_PAYMENT",
|
||||||
"SenderChat",
|
"SenderChat",
|
||||||
"StatusUpdate",
|
"StatusUpdate",
|
||||||
|
@ -2143,6 +2144,20 @@ class Sticker:
|
||||||
# neither mask nor emoji can be a message.sticker, so no filters for them
|
# neither mask nor emoji can be a message.sticker, so no filters for them
|
||||||
|
|
||||||
|
|
||||||
|
class _Story(MessageFilter):
|
||||||
|
__slots__ = ()
|
||||||
|
|
||||||
|
def filter(self, message: Message) -> bool:
|
||||||
|
return bool(message.story)
|
||||||
|
|
||||||
|
|
||||||
|
STORY = _Story(name="filters.STORY")
|
||||||
|
"""Messages that contain :attr:`telegram.Message.story`.
|
||||||
|
|
||||||
|
.. versionadded:: NEXT.VERSION
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class _SuccessfulPayment(MessageFilter):
|
class _SuccessfulPayment(MessageFilter):
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
|
|
|
@ -33,8 +33,6 @@ class TestFiles:
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("string", "expected"),
|
("string", "expected"),
|
||||||
[
|
[
|
||||||
(str(data_file("game.gif")), True),
|
|
||||||
(str(TEST_DATA_PATH), False),
|
|
||||||
(str(data_file("game.gif")), True),
|
(str(data_file("game.gif")), True),
|
||||||
(str(TEST_DATA_PATH), False),
|
(str(TEST_DATA_PATH), False),
|
||||||
(data_file("game.gif"), True),
|
(data_file("game.gif"), True),
|
||||||
|
|
|
@ -884,6 +884,11 @@ class TestFilters:
|
||||||
assert filters.Sticker.VIDEO.check_update(update)
|
assert filters.Sticker.VIDEO.check_update(update)
|
||||||
assert filters.Sticker.PREMIUM.check_update(update)
|
assert filters.Sticker.PREMIUM.check_update(update)
|
||||||
|
|
||||||
|
def test_filters_story(self, update):
|
||||||
|
assert not filters.STORY.check_update(update)
|
||||||
|
update.message.story = "test"
|
||||||
|
assert filters.STORY.check_update(update)
|
||||||
|
|
||||||
def test_filters_video(self, update):
|
def test_filters_video(self, update):
|
||||||
assert not filters.VIDEO.check_update(update)
|
assert not filters.VIDEO.check_update(update)
|
||||||
update.message.video = "test"
|
update.message.video = "test"
|
||||||
|
|
|
@ -69,7 +69,7 @@ def false_update(request):
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def poll_answer(bot):
|
def poll_answer(bot):
|
||||||
return Update(0, poll_answer=PollAnswer(1, User(2, "test user", False), [0, 1]))
|
return Update(0, poll_answer=PollAnswer(1, [0, 1], User(2, "test user", False), Chat(1, "")))
|
||||||
|
|
||||||
|
|
||||||
class TestPollAnswerHandler:
|
class TestPollAnswerHandler:
|
||||||
|
|
|
@ -16,10 +16,12 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU Lesser Public License
|
# You should have received a copy of the GNU Lesser Public License
|
||||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||||
|
import time
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from telegram import Bot, Chat, ChatLocation, ChatPermissions, Location, User
|
from telegram import Bot, Chat, ChatLocation, ChatPermissions, Location, User
|
||||||
|
from telegram._utils.datetime import UTC, from_timestamp
|
||||||
from telegram.constants import ChatAction, ChatType
|
from telegram.constants import ChatAction, ChatType
|
||||||
from telegram.helpers import escape_markdown
|
from telegram.helpers import escape_markdown
|
||||||
from tests.auxil.bot_method_checks import (
|
from tests.auxil.bot_method_checks import (
|
||||||
|
@ -52,6 +54,7 @@ def chat(bot):
|
||||||
is_forum=True,
|
is_forum=True,
|
||||||
active_usernames=TestChatBase.active_usernames,
|
active_usernames=TestChatBase.active_usernames,
|
||||||
emoji_status_custom_emoji_id=TestChatBase.emoji_status_custom_emoji_id,
|
emoji_status_custom_emoji_id=TestChatBase.emoji_status_custom_emoji_id,
|
||||||
|
emoji_status_expiration_date=TestChatBase.emoji_status_expiration_date,
|
||||||
has_aggressive_anti_spam_enabled=TestChatBase.has_aggressive_anti_spam_enabled,
|
has_aggressive_anti_spam_enabled=TestChatBase.has_aggressive_anti_spam_enabled,
|
||||||
has_hidden_members=TestChatBase.has_hidden_members,
|
has_hidden_members=TestChatBase.has_hidden_members,
|
||||||
)
|
)
|
||||||
|
@ -85,6 +88,7 @@ class TestChatBase:
|
||||||
is_forum = True
|
is_forum = True
|
||||||
active_usernames = ["These", "Are", "Usernames!"]
|
active_usernames = ["These", "Are", "Usernames!"]
|
||||||
emoji_status_custom_emoji_id = "VeryUniqueCustomEmojiID"
|
emoji_status_custom_emoji_id = "VeryUniqueCustomEmojiID"
|
||||||
|
emoji_status_expiration_date = time.time()
|
||||||
has_aggressive_anti_spam_enabled = True
|
has_aggressive_anti_spam_enabled = True
|
||||||
has_hidden_members = True
|
has_hidden_members = True
|
||||||
|
|
||||||
|
@ -119,6 +123,7 @@ class TestChatWithoutRequest(TestChatBase):
|
||||||
"is_forum": self.is_forum,
|
"is_forum": self.is_forum,
|
||||||
"active_usernames": self.active_usernames,
|
"active_usernames": self.active_usernames,
|
||||||
"emoji_status_custom_emoji_id": self.emoji_status_custom_emoji_id,
|
"emoji_status_custom_emoji_id": self.emoji_status_custom_emoji_id,
|
||||||
|
"emoji_status_expiration_date": self.emoji_status_expiration_date,
|
||||||
"has_aggressive_anti_spam_enabled": self.has_aggressive_anti_spam_enabled,
|
"has_aggressive_anti_spam_enabled": self.has_aggressive_anti_spam_enabled,
|
||||||
"has_hidden_members": self.has_hidden_members,
|
"has_hidden_members": self.has_hidden_members,
|
||||||
}
|
}
|
||||||
|
@ -150,9 +155,32 @@ class TestChatWithoutRequest(TestChatBase):
|
||||||
assert chat.is_forum == self.is_forum
|
assert chat.is_forum == self.is_forum
|
||||||
assert chat.active_usernames == tuple(self.active_usernames)
|
assert chat.active_usernames == tuple(self.active_usernames)
|
||||||
assert chat.emoji_status_custom_emoji_id == self.emoji_status_custom_emoji_id
|
assert chat.emoji_status_custom_emoji_id == self.emoji_status_custom_emoji_id
|
||||||
|
assert chat.emoji_status_expiration_date == from_timestamp(
|
||||||
|
self.emoji_status_expiration_date
|
||||||
|
)
|
||||||
assert chat.has_aggressive_anti_spam_enabled == self.has_aggressive_anti_spam_enabled
|
assert chat.has_aggressive_anti_spam_enabled == self.has_aggressive_anti_spam_enabled
|
||||||
assert chat.has_hidden_members == self.has_hidden_members
|
assert chat.has_hidden_members == self.has_hidden_members
|
||||||
|
|
||||||
|
def test_de_json_localization(self, bot, raw_bot, tz_bot):
|
||||||
|
json_dict = {
|
||||||
|
"id": self.id_,
|
||||||
|
"type": self.type_,
|
||||||
|
"emoji_status_expiration_date": self.emoji_status_expiration_date,
|
||||||
|
}
|
||||||
|
chat_bot = Chat.de_json(json_dict, bot)
|
||||||
|
chat_bot_raw = Chat.de_json(json_dict, raw_bot)
|
||||||
|
chat_bot_tz = Chat.de_json(json_dict, tz_bot)
|
||||||
|
|
||||||
|
# comparing utcoffsets because comparing tzinfo objects is not reliable
|
||||||
|
emoji_expire_offset = chat_bot_tz.emoji_status_expiration_date.utcoffset()
|
||||||
|
emoji_expire_offset_tz = tz_bot.defaults.tzinfo.utcoffset(
|
||||||
|
chat_bot_tz.emoji_status_expiration_date.replace(tzinfo=None)
|
||||||
|
)
|
||||||
|
|
||||||
|
assert chat_bot.emoji_status_expiration_date.tzinfo == UTC
|
||||||
|
assert chat_bot_raw.emoji_status_expiration_date.tzinfo == UTC
|
||||||
|
assert emoji_expire_offset_tz == emoji_expire_offset
|
||||||
|
|
||||||
def test_to_dict(self, chat):
|
def test_to_dict(self, chat):
|
||||||
chat_dict = chat.to_dict()
|
chat_dict = chat.to_dict()
|
||||||
|
|
||||||
|
@ -177,6 +205,7 @@ class TestChatWithoutRequest(TestChatBase):
|
||||||
assert chat_dict["is_forum"] == chat.is_forum
|
assert chat_dict["is_forum"] == chat.is_forum
|
||||||
assert chat_dict["active_usernames"] == list(chat.active_usernames)
|
assert chat_dict["active_usernames"] == list(chat.active_usernames)
|
||||||
assert chat_dict["emoji_status_custom_emoji_id"] == chat.emoji_status_custom_emoji_id
|
assert chat_dict["emoji_status_custom_emoji_id"] == chat.emoji_status_custom_emoji_id
|
||||||
|
assert chat_dict["emoji_status_expiration_date"] == chat.emoji_status_expiration_date
|
||||||
assert (
|
assert (
|
||||||
chat_dict["has_aggressive_anti_spam_enabled"] == chat.has_aggressive_anti_spam_enabled
|
chat_dict["has_aggressive_anti_spam_enabled"] == chat.has_aggressive_anti_spam_enabled
|
||||||
)
|
)
|
||||||
|
@ -1075,6 +1104,31 @@ class TestChatWithoutRequest(TestChatBase):
|
||||||
monkeypatch.setattr(chat.get_bot(), "unpin_all_forum_topic_messages", make_assertion)
|
monkeypatch.setattr(chat.get_bot(), "unpin_all_forum_topic_messages", make_assertion)
|
||||||
assert await chat.unpin_all_forum_topic_messages(message_thread_id=42)
|
assert await chat.unpin_all_forum_topic_messages(message_thread_id=42)
|
||||||
|
|
||||||
|
async def test_unpin_all_general_forum_topic_messages(self, monkeypatch, chat):
|
||||||
|
async def make_assertion(*_, **kwargs):
|
||||||
|
return kwargs["chat_id"] == chat.id
|
||||||
|
|
||||||
|
assert check_shortcut_signature(
|
||||||
|
Chat.unpin_all_general_forum_topic_messages,
|
||||||
|
Bot.unpin_all_general_forum_topic_messages,
|
||||||
|
["chat_id"],
|
||||||
|
[],
|
||||||
|
)
|
||||||
|
assert await check_shortcut_call(
|
||||||
|
chat.unpin_all_general_forum_topic_messages,
|
||||||
|
chat.get_bot(),
|
||||||
|
"unpin_all_general_forum_topic_messages",
|
||||||
|
shortcut_kwargs=["chat_id"],
|
||||||
|
)
|
||||||
|
assert await check_defaults_handling(
|
||||||
|
chat.unpin_all_general_forum_topic_messages, chat.get_bot()
|
||||||
|
)
|
||||||
|
|
||||||
|
monkeypatch.setattr(
|
||||||
|
chat.get_bot(), "unpin_all_general_forum_topic_messages", make_assertion
|
||||||
|
)
|
||||||
|
assert await chat.unpin_all_general_forum_topic_messages()
|
||||||
|
|
||||||
async def test_edit_general_forum_topic(self, monkeypatch, chat):
|
async def test_edit_general_forum_topic(self, monkeypatch, chat):
|
||||||
async def make_assertion(*_, **kwargs):
|
async def make_assertion(*_, **kwargs):
|
||||||
return kwargs["chat_id"] == chat.id and kwargs["name"] == "WhatAName"
|
return kwargs["chat_id"] == chat.id and kwargs["name"] == "WhatAName"
|
||||||
|
|
|
@ -236,6 +236,7 @@ class TestForumMethodsWithRequest:
|
||||||
assert result is True, "Failed to reopen forum topic"
|
assert result is True, "Failed to reopen forum topic"
|
||||||
|
|
||||||
async def test_unpin_all_forum_topic_messages(self, bot, forum_group_id, real_topic):
|
async def test_unpin_all_forum_topic_messages(self, bot, forum_group_id, real_topic):
|
||||||
|
# We need 2 or more pinned msgs for this to work, else we get Chat_not_modified error
|
||||||
message_thread_id = real_topic.message_thread_id
|
message_thread_id = real_topic.message_thread_id
|
||||||
pin_msg_tasks = set()
|
pin_msg_tasks = set()
|
||||||
|
|
||||||
|
@ -249,10 +250,23 @@ class TestForumMethodsWithRequest:
|
||||||
|
|
||||||
assert all([await task for task in pin_msg_tasks]) is True, "Message(s) were not pinned"
|
assert all([await task for task in pin_msg_tasks]) is True, "Message(s) were not pinned"
|
||||||
|
|
||||||
# We need 2 or more pinned msgs for this to work, else we get Chat_not_modified error
|
|
||||||
result = await bot.unpin_all_forum_topic_messages(forum_group_id, message_thread_id)
|
result = await bot.unpin_all_forum_topic_messages(forum_group_id, message_thread_id)
|
||||||
assert result is True, "Failed to unpin all the messages in forum topic"
|
assert result is True, "Failed to unpin all the messages in forum topic"
|
||||||
|
|
||||||
|
async def test_unpin_all_general_forum_topic_messages(self, bot, forum_group_id):
|
||||||
|
# We need 2 or more pinned msgs for this to work, else we get Chat_not_modified error
|
||||||
|
pin_msg_tasks = set()
|
||||||
|
|
||||||
|
awaitables = {bot.send_message(forum_group_id, TEST_MSG_TEXT) for _ in range(2)}
|
||||||
|
for coro in asyncio.as_completed(awaitables):
|
||||||
|
msg = await coro
|
||||||
|
pin_msg_tasks.add(asyncio.create_task(msg.pin()))
|
||||||
|
|
||||||
|
assert all([await task for task in pin_msg_tasks]) is True, "Message(s) were not pinned"
|
||||||
|
|
||||||
|
result = await bot.unpin_all_general_forum_topic_messages(forum_group_id)
|
||||||
|
assert result is True, "Failed to unpin all the messages in forum topic"
|
||||||
|
|
||||||
async def test_edit_general_forum_topic(self, bot, forum_group_id):
|
async def test_edit_general_forum_topic(self, bot, forum_group_id):
|
||||||
result = await bot.edit_general_forum_topic(
|
result = await bot.edit_general_forum_topic(
|
||||||
chat_id=forum_group_id,
|
chat_id=forum_group_id,
|
||||||
|
|
|
@ -42,6 +42,7 @@ from telegram import (
|
||||||
PollOption,
|
PollOption,
|
||||||
ProximityAlertTriggered,
|
ProximityAlertTriggered,
|
||||||
Sticker,
|
Sticker,
|
||||||
|
Story,
|
||||||
SuccessfulPayment,
|
SuccessfulPayment,
|
||||||
Update,
|
Update,
|
||||||
User,
|
User,
|
||||||
|
@ -123,6 +124,7 @@ def message(bot):
|
||||||
},
|
},
|
||||||
{"photo": [PhotoSize("photo_id", "unique_id", 50, 50)], "caption": "photo_file"},
|
{"photo": [PhotoSize("photo_id", "unique_id", 50, 50)], "caption": "photo_file"},
|
||||||
{"sticker": Sticker("sticker_id", "unique_id", 50, 50, True, False, Sticker.REGULAR)},
|
{"sticker": Sticker("sticker_id", "unique_id", 50, 50, True, False, Sticker.REGULAR)},
|
||||||
|
{"story": Story()},
|
||||||
{"video": Video("video_id", "unique_id", 12, 12, 12), "caption": "video_file"},
|
{"video": Video("video_id", "unique_id", 12, 12, 12), "caption": "video_file"},
|
||||||
{"voice": Voice("voice_id", "unique_id", 5)},
|
{"voice": Voice("voice_id", "unique_id", 5)},
|
||||||
{"video_note": VideoNote("video_note_id", "unique_id", 20, 12)},
|
{"video_note": VideoNote("video_note_id", "unique_id", 20, 12)},
|
||||||
|
@ -227,6 +229,7 @@ def message(bot):
|
||||||
"game",
|
"game",
|
||||||
"photo",
|
"photo",
|
||||||
"sticker",
|
"sticker",
|
||||||
|
"story",
|
||||||
"video",
|
"video",
|
||||||
"voice",
|
"voice",
|
||||||
"video_note",
|
"video_note",
|
||||||
|
@ -989,6 +992,7 @@ class TestMessageWithoutRequest(TestMessageBase):
|
||||||
"photo",
|
"photo",
|
||||||
"poll",
|
"poll",
|
||||||
"sticker",
|
"sticker",
|
||||||
|
"story",
|
||||||
"successful_payment",
|
"successful_payment",
|
||||||
"video",
|
"video",
|
||||||
"video_note",
|
"video_note",
|
||||||
|
|
|
@ -19,9 +19,10 @@ from datetime import datetime, timedelta, timezone
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from telegram import MessageEntity, Poll, PollAnswer, PollOption, User
|
from telegram import Chat, MessageEntity, Poll, PollAnswer, PollOption, User
|
||||||
from telegram._utils.datetime import UTC, to_timestamp
|
from telegram._utils.datetime import UTC, to_timestamp
|
||||||
from telegram.constants import PollType
|
from telegram.constants import PollType
|
||||||
|
from telegram.warnings import PTBDeprecationWarning
|
||||||
from tests.auxil.slots import mro_slots
|
from tests.auxil.slots import mro_slots
|
||||||
|
|
||||||
|
|
||||||
|
@ -81,50 +82,58 @@ class TestPollOptionWithoutRequest(TestPollOptionBase):
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
def poll_answer():
|
def poll_answer():
|
||||||
return PollAnswer(
|
return PollAnswer(
|
||||||
TestPollAnswerBase.poll_id, TestPollAnswerBase.user, TestPollAnswerBase.poll_id
|
TestPollAnswerBase.poll_id,
|
||||||
|
TestPollAnswerBase.option_ids,
|
||||||
|
TestPollAnswerBase.user,
|
||||||
|
TestPollAnswerBase.voter_chat,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestPollAnswerBase:
|
class TestPollAnswerBase:
|
||||||
poll_id = "id"
|
poll_id = "id"
|
||||||
user = User(1, "", False)
|
|
||||||
option_ids = [2]
|
option_ids = [2]
|
||||||
|
user = User(1, "", False)
|
||||||
|
voter_chat = Chat(1, "")
|
||||||
|
|
||||||
|
|
||||||
class TestPollAnswerWithoutRequest(TestPollAnswerBase):
|
class TestPollAnswerWithoutRequest(TestPollAnswerBase):
|
||||||
def test_de_json(self):
|
def test_de_json(self):
|
||||||
json_dict = {
|
json_dict = {
|
||||||
"poll_id": self.poll_id,
|
"poll_id": self.poll_id,
|
||||||
"user": self.user.to_dict(),
|
|
||||||
"option_ids": self.option_ids,
|
"option_ids": self.option_ids,
|
||||||
|
"user": self.user.to_dict(),
|
||||||
|
"voter_chat": self.voter_chat.to_dict(),
|
||||||
}
|
}
|
||||||
poll_answer = PollAnswer.de_json(json_dict, None)
|
poll_answer = PollAnswer.de_json(json_dict, None)
|
||||||
assert poll_answer.api_kwargs == {}
|
assert poll_answer.api_kwargs == {}
|
||||||
|
|
||||||
assert poll_answer.poll_id == self.poll_id
|
assert poll_answer.poll_id == self.poll_id
|
||||||
assert poll_answer.user == self.user
|
|
||||||
assert poll_answer.option_ids == tuple(self.option_ids)
|
assert poll_answer.option_ids == tuple(self.option_ids)
|
||||||
|
assert poll_answer.user == self.user
|
||||||
|
assert poll_answer.voter_chat == self.voter_chat
|
||||||
|
|
||||||
def test_to_dict(self, poll_answer):
|
def test_to_dict(self, poll_answer):
|
||||||
poll_answer_dict = poll_answer.to_dict()
|
poll_answer_dict = poll_answer.to_dict()
|
||||||
|
|
||||||
assert isinstance(poll_answer_dict, dict)
|
assert isinstance(poll_answer_dict, dict)
|
||||||
assert poll_answer_dict["poll_id"] == poll_answer.poll_id
|
assert poll_answer_dict["poll_id"] == poll_answer.poll_id
|
||||||
assert poll_answer_dict["user"] == poll_answer.user.to_dict()
|
|
||||||
assert poll_answer_dict["option_ids"] == list(poll_answer.option_ids)
|
assert poll_answer_dict["option_ids"] == list(poll_answer.option_ids)
|
||||||
|
assert poll_answer_dict["user"] == poll_answer.user.to_dict()
|
||||||
|
assert poll_answer_dict["voter_chat"] == poll_answer.voter_chat.to_dict()
|
||||||
|
|
||||||
def test_equality(self):
|
def test_equality(self):
|
||||||
a = PollAnswer(123, self.user, [2])
|
a = PollAnswer(123, [2], self.user, self.voter_chat)
|
||||||
b = PollAnswer(123, User(1, "first", False), [2])
|
b = PollAnswer(123, [2], self.user, Chat(1, ""))
|
||||||
c = PollAnswer(123, self.user, [1, 2])
|
c = PollAnswer(123, [2], User(1, "first", False), self.voter_chat)
|
||||||
d = PollAnswer(456, self.user, [2])
|
d = PollAnswer(123, [1, 2], self.user, self.voter_chat)
|
||||||
e = PollOption("Text", 1)
|
e = PollAnswer(456, [2], self.user, self.voter_chat)
|
||||||
|
f = PollOption("Text", 1)
|
||||||
|
|
||||||
assert a == b
|
assert a == b
|
||||||
assert hash(a) == hash(b)
|
assert hash(a) == hash(b)
|
||||||
|
|
||||||
assert a != c
|
assert a == c
|
||||||
assert hash(a) != hash(c)
|
assert hash(a) == hash(c)
|
||||||
|
|
||||||
assert a != d
|
assert a != d
|
||||||
assert hash(a) != hash(d)
|
assert hash(a) != hash(d)
|
||||||
|
@ -132,6 +141,22 @@ class TestPollAnswerWithoutRequest(TestPollAnswerBase):
|
||||||
assert a != e
|
assert a != e
|
||||||
assert hash(a) != hash(e)
|
assert hash(a) != hash(e)
|
||||||
|
|
||||||
|
assert a != f
|
||||||
|
assert hash(a) != hash(f)
|
||||||
|
|
||||||
|
def test_order_warning(self, recwarn):
|
||||||
|
expected_warning = (
|
||||||
|
"From v20.5 the order of `option_ids` and `user` is changed as the latter one"
|
||||||
|
" became optional. Please update your code to use the new order."
|
||||||
|
)
|
||||||
|
PollAnswer(123, [2], self.user, self.voter_chat)
|
||||||
|
assert len(recwarn) == 0
|
||||||
|
PollAnswer(123, self.user, [2], self.voter_chat)
|
||||||
|
assert len(recwarn) == 1
|
||||||
|
assert str(recwarn[0].message) == expected_warning
|
||||||
|
assert recwarn[0].category is PTBDeprecationWarning
|
||||||
|
assert recwarn[0].filename == __file__, "wrong stacklevel"
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
def poll():
|
def poll():
|
||||||
|
|
45
tests/test_story.py
Normal file
45
tests/test_story.py
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# A library that provides a Python interface to the Telegram Bot API
|
||||||
|
# Copyright (C) 2015-2023
|
||||||
|
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Lesser Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Lesser Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser Public License
|
||||||
|
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from telegram import Story
|
||||||
|
from tests.auxil.slots import mro_slots
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="module")
|
||||||
|
def story():
|
||||||
|
return Story()
|
||||||
|
|
||||||
|
|
||||||
|
class TestStoryWithoutRequest:
|
||||||
|
def test_slot_behaviour(self):
|
||||||
|
story = Story()
|
||||||
|
for attr in story.__slots__:
|
||||||
|
assert getattr(story, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||||
|
assert len(mro_slots(story)) == len(set(mro_slots(story))), "duplicate slot"
|
||||||
|
|
||||||
|
def test_de_json(self):
|
||||||
|
story = Story.de_json({}, None)
|
||||||
|
assert story.api_kwargs == {}
|
||||||
|
assert isinstance(story, Story)
|
||||||
|
|
||||||
|
def test_to_dict(self):
|
||||||
|
story = Story()
|
||||||
|
story_dict = story.to_dict()
|
||||||
|
assert story_dict == {}
|
|
@ -70,7 +70,18 @@ params = [
|
||||||
{"shipping_query": ShippingQuery("id", User(1, "", False), "", None)},
|
{"shipping_query": ShippingQuery("id", User(1, "", False), "", None)},
|
||||||
{"pre_checkout_query": PreCheckoutQuery("id", User(1, "", False), "", 0, "")},
|
{"pre_checkout_query": PreCheckoutQuery("id", User(1, "", False), "", 0, "")},
|
||||||
{"poll": Poll("id", "?", [PollOption(".", 1)], False, False, False, Poll.REGULAR, True)},
|
{"poll": Poll("id", "?", [PollOption(".", 1)], False, False, False, Poll.REGULAR, True)},
|
||||||
{"poll_answer": PollAnswer("id", User(1, "", False), [1])},
|
{
|
||||||
|
"poll_answer": PollAnswer(
|
||||||
|
"id",
|
||||||
|
[1],
|
||||||
|
User(
|
||||||
|
1,
|
||||||
|
"",
|
||||||
|
False,
|
||||||
|
),
|
||||||
|
Chat(1, ""),
|
||||||
|
)
|
||||||
|
},
|
||||||
{"my_chat_member": chat_member_updated},
|
{"my_chat_member": chat_member_updated},
|
||||||
{"chat_member": chat_member_updated},
|
{"chat_member": chat_member_updated},
|
||||||
{"chat_join_request": chat_join_request},
|
{"chat_join_request": chat_join_request},
|
||||||
|
|
Loading…
Reference in a new issue