mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2024-12-23 15:00:40 +01:00
Co-authored-by: Harshil <37377066+harshil21@users.noreply.github.com> Co-authored-by: Abdelrahman Elkheir <90580077+aelkheir@users.noreply.github.com>
This commit is contained in:
parent
f3bd0f1462
commit
805b7bff32
55 changed files with 2742 additions and 171 deletions
|
@ -14,9 +14,9 @@
|
|||
:target: https://pypi.org/project/python-telegram-bot/
|
||||
:alt: Supported Python versions
|
||||
|
||||
.. image:: https://img.shields.io/badge/Bot%20API-7.2-blue?logo=telegram
|
||||
.. image:: https://img.shields.io/badge/Bot%20API-7.3-blue?logo=telegram
|
||||
:target: https://core.telegram.org/bots/api-changelog
|
||||
:alt: Supported Bot API versions
|
||||
:alt: Supported Bot API version
|
||||
|
||||
.. image:: https://img.shields.io/pypi/dm/python-telegram-bot
|
||||
:target: https://pypistats.org/packages/python-telegram-bot
|
||||
|
@ -89,7 +89,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 **7.2** are supported.
|
||||
All types and methods of the Telegram Bot API **7.3** are supported.
|
||||
|
||||
Installing
|
||||
==========
|
||||
|
|
|
@ -14,9 +14,9 @@
|
|||
:target: https://pypi.org/project/python-telegram-bot-raw/
|
||||
:alt: Supported Python versions
|
||||
|
||||
.. image:: https://img.shields.io/badge/Bot%20API-7.2-blue?logo=telegram
|
||||
.. image:: https://img.shields.io/badge/Bot%20API-7.3-blue?logo=telegram
|
||||
:target: https://core.telegram.org/bots/api-changelog
|
||||
:alt: Supported Bot API versions
|
||||
:alt: Supported Bot API version
|
||||
|
||||
.. image:: https://img.shields.io/pypi/dm/python-telegram-bot-raw
|
||||
:target: https://pypistats.org/packages/python-telegram-bot-raw
|
||||
|
@ -85,7 +85,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 **7.2** are supported.
|
||||
All types and methods of the Telegram Bot API **7.3** are supported.
|
||||
|
||||
Installing
|
||||
==========
|
||||
|
|
|
@ -28,6 +28,16 @@ Available Types
|
|||
telegram.callbackquery
|
||||
telegram.chat
|
||||
telegram.chatadministratorrights
|
||||
telegram.chatbackground
|
||||
telegram.backgroundtype
|
||||
telegram.backgroundtypefill
|
||||
telegram.backgroundtypewallpaper
|
||||
telegram.backgroundtypepattern
|
||||
telegram.backgroundtypechattheme
|
||||
telegram.backgroundfill
|
||||
telegram.backgroundfillsolid
|
||||
telegram.backgroundfillgradient
|
||||
telegram.backgroundfillfreeformgradient
|
||||
telegram.chatboost
|
||||
telegram.chatboostadded
|
||||
telegram.chatboostremoved
|
||||
|
@ -36,6 +46,7 @@ Available Types
|
|||
telegram.chatboostsourcegiveaway
|
||||
telegram.chatboostsourcepremium
|
||||
telegram.chatboostupdated
|
||||
telegram.chatfullinfo
|
||||
telegram.chatinvitelink
|
||||
telegram.chatjoinrequest
|
||||
telegram.chatlocation
|
||||
|
@ -77,6 +88,7 @@ Available Types
|
|||
telegram.inputmediadocument
|
||||
telegram.inputmediaphoto
|
||||
telegram.inputmediavideo
|
||||
telegram.inputpolloption
|
||||
telegram.inputsticker
|
||||
telegram.keyboardbutton
|
||||
telegram.keyboardbuttonpolltype
|
||||
|
|
8
docs/source/telegram.backgroundfill.rst
Normal file
8
docs/source/telegram.backgroundfill.rst
Normal file
|
@ -0,0 +1,8 @@
|
|||
BackgroundFill
|
||||
==============
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
.. autoclass:: telegram.BackgroundFill
|
||||
:members:
|
||||
:show-inheritance:
|
8
docs/source/telegram.backgroundfillfreeformgradient.rst
Normal file
8
docs/source/telegram.backgroundfillfreeformgradient.rst
Normal file
|
@ -0,0 +1,8 @@
|
|||
BackgroundFillFreeformGradient
|
||||
==============================
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
.. autoclass:: telegram.BackgroundFillFreeformGradient
|
||||
:members:
|
||||
:show-inheritance:
|
8
docs/source/telegram.backgroundfillgradient.rst
Normal file
8
docs/source/telegram.backgroundfillgradient.rst
Normal file
|
@ -0,0 +1,8 @@
|
|||
BackgroundFillGradient
|
||||
======================
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
.. autoclass:: telegram.BackgroundFillGradient
|
||||
:members:
|
||||
:show-inheritance:
|
8
docs/source/telegram.backgroundfillsolid.rst
Normal file
8
docs/source/telegram.backgroundfillsolid.rst
Normal file
|
@ -0,0 +1,8 @@
|
|||
BackgroundFillSolid
|
||||
===================
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
.. autoclass:: telegram.BackgroundFillSolid
|
||||
:members:
|
||||
:show-inheritance:
|
8
docs/source/telegram.backgroundtype.rst
Normal file
8
docs/source/telegram.backgroundtype.rst
Normal file
|
@ -0,0 +1,8 @@
|
|||
BackgroundType
|
||||
==============
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
.. autoclass:: telegram.BackgroundType
|
||||
:members:
|
||||
:show-inheritance:
|
8
docs/source/telegram.backgroundtypechattheme.rst
Normal file
8
docs/source/telegram.backgroundtypechattheme.rst
Normal file
|
@ -0,0 +1,8 @@
|
|||
BackgroundTypeChatTheme
|
||||
=======================
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
.. autoclass:: telegram.BackgroundTypeChatTheme
|
||||
:members:
|
||||
:show-inheritance:
|
8
docs/source/telegram.backgroundtypefill.rst
Normal file
8
docs/source/telegram.backgroundtypefill.rst
Normal file
|
@ -0,0 +1,8 @@
|
|||
BackgroundTypeFill
|
||||
==================
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
.. autoclass:: telegram.BackgroundTypeFill
|
||||
:members:
|
||||
:show-inheritance:
|
8
docs/source/telegram.backgroundtypepattern.rst
Normal file
8
docs/source/telegram.backgroundtypepattern.rst
Normal file
|
@ -0,0 +1,8 @@
|
|||
BackgroundTypePattern
|
||||
=====================
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
.. autoclass:: telegram.BackgroundTypePattern
|
||||
:members:
|
||||
:show-inheritance:
|
8
docs/source/telegram.backgroundtypewallpaper.rst
Normal file
8
docs/source/telegram.backgroundtypewallpaper.rst
Normal file
|
@ -0,0 +1,8 @@
|
|||
BackgroundTypeWallpaper
|
||||
=======================
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
.. autoclass:: telegram.BackgroundTypeWallpaper
|
||||
:members:
|
||||
:show-inheritance:
|
8
docs/source/telegram.chatbackground.rst
Normal file
8
docs/source/telegram.chatbackground.rst
Normal file
|
@ -0,0 +1,8 @@
|
|||
ChatBackground
|
||||
==============
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
.. autoclass:: telegram.ChatBackground
|
||||
:members:
|
||||
:show-inheritance:
|
6
docs/source/telegram.chatfullinfo.rst
Normal file
6
docs/source/telegram.chatfullinfo.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
ChatFullInfo
|
||||
============
|
||||
|
||||
.. autoclass:: telegram.ChatFullInfo
|
||||
:members:
|
||||
:show-inheritance:
|
6
docs/source/telegram.inputpolloption.rst
Normal file
6
docs/source/telegram.inputpolloption.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
InputPollOption
|
||||
===============
|
||||
|
||||
.. autoclass:: telegram.InputPollOption
|
||||
:members:
|
||||
:show-inheritance:
|
|
@ -22,6 +22,15 @@ __author__ = "devs@python-telegram-bot.org"
|
|||
__all__ = (
|
||||
"Animation",
|
||||
"Audio",
|
||||
"BackgroundFill",
|
||||
"BackgroundFillFreeformGradient",
|
||||
"BackgroundFillGradient",
|
||||
"BackgroundFillSolid",
|
||||
"BackgroundType",
|
||||
"BackgroundTypeChatTheme",
|
||||
"BackgroundTypeFill",
|
||||
"BackgroundTypePattern",
|
||||
"BackgroundTypeWallpaper",
|
||||
"Birthdate",
|
||||
"Bot",
|
||||
"BotCommand",
|
||||
|
@ -46,6 +55,7 @@ __all__ = (
|
|||
"CallbackQuery",
|
||||
"Chat",
|
||||
"ChatAdministratorRights",
|
||||
"ChatBackground",
|
||||
"ChatBoost",
|
||||
"ChatBoostAdded",
|
||||
"ChatBoostRemoved",
|
||||
|
@ -54,6 +64,7 @@ __all__ = (
|
|||
"ChatBoostSourceGiveaway",
|
||||
"ChatBoostSourcePremium",
|
||||
"ChatBoostUpdated",
|
||||
"ChatFullInfo",
|
||||
"ChatInviteLink",
|
||||
"ChatJoinRequest",
|
||||
"ChatLocation",
|
||||
|
@ -131,6 +142,7 @@ __all__ = (
|
|||
"InputMediaPhoto",
|
||||
"InputMediaVideo",
|
||||
"InputMessageContent",
|
||||
"InputPollOption",
|
||||
"InputSticker",
|
||||
"InputTextMessageContent",
|
||||
"InputVenueMessageContent",
|
||||
|
@ -258,6 +270,18 @@ from ._business import (
|
|||
from ._callbackquery import CallbackQuery
|
||||
from ._chat import Chat
|
||||
from ._chatadministratorrights import ChatAdministratorRights
|
||||
from ._chatbackground import (
|
||||
BackgroundFill,
|
||||
BackgroundFillFreeformGradient,
|
||||
BackgroundFillGradient,
|
||||
BackgroundFillSolid,
|
||||
BackgroundType,
|
||||
BackgroundTypeChatTheme,
|
||||
BackgroundTypeFill,
|
||||
BackgroundTypePattern,
|
||||
BackgroundTypeWallpaper,
|
||||
ChatBackground,
|
||||
)
|
||||
from ._chatboost import (
|
||||
ChatBoost,
|
||||
ChatBoostAdded,
|
||||
|
@ -269,6 +293,7 @@ from ._chatboost import (
|
|||
ChatBoostUpdated,
|
||||
UserChatBoosts,
|
||||
)
|
||||
from ._chatfullinfo import ChatFullInfo
|
||||
from ._chatinvitelink import ChatInviteLink
|
||||
from ._chatjoinrequest import ChatJoinRequest
|
||||
from ._chatlocation import ChatLocation
|
||||
|
@ -403,7 +428,7 @@ from ._payment.shippingaddress import ShippingAddress
|
|||
from ._payment.shippingoption import ShippingOption
|
||||
from ._payment.shippingquery import ShippingQuery
|
||||
from ._payment.successfulpayment import SuccessfulPayment
|
||||
from ._poll import Poll, PollAnswer, PollOption
|
||||
from ._poll import InputPollOption, Poll, PollAnswer, PollOption
|
||||
from ._proximityalerttriggered import ProximityAlertTriggered
|
||||
from ._reaction import ReactionCount, ReactionType, ReactionTypeCustomEmoji, ReactionTypeEmoji
|
||||
from ._reply import ExternalReplyInfo, ReplyParameters, TextQuote
|
||||
|
|
|
@ -26,7 +26,7 @@ from telegram._utils.types import JSONDict
|
|||
|
||||
class Birthdate(TelegramObject):
|
||||
"""
|
||||
This object represents a user's birthday.
|
||||
This object describes the birthdate of a user.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`day`, and :attr:`month` are equal.
|
||||
|
|
|
@ -58,9 +58,9 @@ from telegram._botcommandscope import BotCommandScope
|
|||
from telegram._botdescription import BotDescription, BotShortDescription
|
||||
from telegram._botname import BotName
|
||||
from telegram._business import BusinessConnection
|
||||
from telegram._chat import Chat
|
||||
from telegram._chatadministratorrights import ChatAdministratorRights
|
||||
from telegram._chatboost import UserChatBoosts
|
||||
from telegram._chatfullinfo import ChatFullInfo
|
||||
from telegram._chatinvitelink import ChatInviteLink
|
||||
from telegram._chatmember import ChatMember
|
||||
from telegram._chatpermissions import ChatPermissions
|
||||
|
@ -84,7 +84,7 @@ from telegram._inline.inlinequeryresultsbutton import InlineQueryResultsButton
|
|||
from telegram._menubutton import MenuButton
|
||||
from telegram._message import Message
|
||||
from telegram._messageid import MessageId
|
||||
from telegram._poll import Poll
|
||||
from telegram._poll import InputPollOption, Poll
|
||||
from telegram._reaction import ReactionType, ReactionTypeCustomEmoji, ReactionTypeEmoji
|
||||
from telegram._reply import ReplyParameters
|
||||
from telegram._sentwebappmessage import SentWebAppMessage
|
||||
|
@ -2287,9 +2287,10 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
|||
"""
|
||||
Use this method to send audio files, if you want Telegram clients to display the file
|
||||
as a playable voice message. For this to work, your audio must be in an ``.ogg`` file
|
||||
encoded with OPUS (other formats may be sent as Audio or Document). Bots can currently
|
||||
send voice messages of up to :tg-const:`telegram.constants.FileSizeLimit.FILESIZE_UPLOAD`
|
||||
in size, this limit may be changed in the future.
|
||||
encoded with OPUS , or in .MP3 format, or in .M4A format (other formats may be sent as
|
||||
:class:`~telegram.Audio` or :class:`~telegram.Document`). Bots can currently send voice
|
||||
messages of up to :tg-const:`telegram.constants.FileSizeLimit.FILESIZE_UPLOAD` in size,
|
||||
this limit may be changed in the future.
|
||||
|
||||
Note:
|
||||
To use this method, the file must have the type :mimetype:`audio/ogg` and be no more
|
||||
|
@ -2610,7 +2611,9 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
|||
live_period (:obj:`int`, optional): Period in seconds for which the location will be
|
||||
updated, should be between
|
||||
:tg-const:`telegram.constants.LocationLimit.MIN_LIVE_PERIOD` and
|
||||
:tg-const:`telegram.constants.LocationLimit.MAX_LIVE_PERIOD`.
|
||||
:tg-const:`telegram.constants.LocationLimit.MAX_LIVE_PERIOD`, or
|
||||
:tg-const:`telegram.constants.LocationLimit.LIVE_PERIOD_FOREVER` for live
|
||||
locations that can be edited indefinitely.
|
||||
heading (:obj:`int`, optional): For live locations, a direction in which the user is
|
||||
moving, in degrees. Must be between
|
||||
:tg-const:`telegram.constants.LocationLimit.MIN_HEADING` and
|
||||
|
@ -2720,6 +2723,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
|||
horizontal_accuracy: Optional[float] = None,
|
||||
heading: Optional[int] = None,
|
||||
proximity_alert_radius: Optional[int] = None,
|
||||
live_period: Optional[int] = None,
|
||||
*,
|
||||
location: Optional[Location] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
|
@ -2758,6 +2762,15 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
|||
if specified.
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): An object for a new
|
||||
inline keyboard.
|
||||
live_period (:obj:`int`, optional): New period in seconds during which the location
|
||||
can be updated, starting from the message send date. If
|
||||
:tg-const:`telegram.constants.LocationLimit.LIVE_PERIOD_FOREVER` is specified,
|
||||
then the location can be updated forever. Otherwise, the new value must not exceed
|
||||
the current ``live_period`` by more than a day, and the live location expiration
|
||||
date must remain within the next 90 days. If not specified, then ``live_period``
|
||||
remains unchanged
|
||||
|
||||
.. versionadded:: NEXT.VERSION.
|
||||
|
||||
Keyword Args:
|
||||
location (:class:`telegram.Location`, optional): The location to send.
|
||||
|
@ -2790,6 +2803,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
|||
"horizontal_accuracy": horizontal_accuracy,
|
||||
"heading": heading,
|
||||
"proximity_alert_radius": proximity_alert_radius,
|
||||
"live_period": live_period,
|
||||
}
|
||||
|
||||
return await self._send_message(
|
||||
|
@ -4434,16 +4448,19 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
|||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> Chat:
|
||||
) -> ChatFullInfo:
|
||||
"""
|
||||
Use this method to get up to date information about the chat (current name of the user for
|
||||
one-on-one conversations, current username of a user, group or channel, etc.).
|
||||
|
||||
.. versionchanged:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this method now returns a :class:`telegram.ChatFullInfo`.
|
||||
|
||||
Args:
|
||||
chat_id (:obj:`int` | :obj:`str`): |chat_id_channel|
|
||||
|
||||
Returns:
|
||||
:class:`telegram.Chat`
|
||||
:class:`telegram.ChatFullInfo`
|
||||
|
||||
Raises:
|
||||
:class:`telegram.error.TelegramError`
|
||||
|
@ -4461,7 +4478,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
|||
api_kwargs=api_kwargs,
|
||||
)
|
||||
|
||||
return Chat.de_json(result, self) # type: ignore[return-value]
|
||||
return ChatFullInfo.de_json(result, self) # type: ignore[return-value]
|
||||
|
||||
async def get_chat_administrators(
|
||||
self,
|
||||
|
@ -5311,7 +5328,8 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]):
|
|||
|
||||
.. versionadded:: 20.6
|
||||
can_edit_stories (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can
|
||||
edit stories posted by other users.
|
||||
edit stories posted by other users, post stories to the chat page, pin chat
|
||||
stories, and access the chat's story archive
|
||||
|
||||
.. versionadded:: 20.6
|
||||
can_delete_stories (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can
|
||||
|
@ -6798,7 +6816,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
self,
|
||||
chat_id: Union[int, str],
|
||||
question: str,
|
||||
options: Sequence[str],
|
||||
options: Sequence[Union[str, "InputPollOption"]],
|
||||
is_anonymous: Optional[bool] = None,
|
||||
type: Optional[str] = None, # pylint: disable=redefined-builtin
|
||||
allows_multiple_answers: Optional[bool] = None,
|
||||
|
@ -6815,6 +6833,8 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
message_thread_id: Optional[int] = None,
|
||||
reply_parameters: Optional["ReplyParameters"] = None,
|
||||
business_connection_id: Optional[str] = None,
|
||||
question_parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
question_entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
*,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
|
@ -6831,14 +6851,20 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
chat_id (:obj:`int` | :obj:`str`): |chat_id_channel|
|
||||
question (:obj:`str`): Poll question, :tg-const:`telegram.Poll.MIN_QUESTION_LENGTH`-
|
||||
:tg-const:`telegram.Poll.MAX_QUESTION_LENGTH` characters.
|
||||
options (Sequence[:obj:`str`]): Sequence of answer options,
|
||||
options (Sequence[:obj:`str` | :class:`telegram.InputPollOption`]): Sequence of
|
||||
:tg-const:`telegram.Poll.MIN_OPTION_NUMBER`-
|
||||
:tg-const:`telegram.Poll.MAX_OPTION_NUMBER` strings
|
||||
:tg-const:`telegram.Poll.MAX_OPTION_NUMBER` answer options. Each option may either
|
||||
be a string with
|
||||
:tg-const:`telegram.Poll.MIN_OPTION_LENGTH`-
|
||||
:tg-const:`telegram.Poll.MAX_OPTION_LENGTH` characters each.
|
||||
:tg-const:`telegram.Poll.MAX_OPTION_LENGTH` characters or an
|
||||
:class:`~telegram.InputPollOption` object. Strings are converted to
|
||||
:class:`~telegram.InputPollOption` objects automatically.
|
||||
|
||||
.. versionchanged:: 20.0
|
||||
|sequenceargs|
|
||||
|
||||
.. versionchanged:: NEXT.VERSION
|
||||
Bot API 7.3 adds support for :class:`~telegram.InputPollOption` objects.
|
||||
is_anonymous (:obj:`bool`, optional): :obj:`True`, if the poll needs to be anonymous,
|
||||
defaults to :obj:`True`.
|
||||
type (:obj:`str`, optional): Poll type, :tg-const:`telegram.Poll.QUIZ` or
|
||||
|
@ -6893,6 +6919,16 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
business_connection_id (:obj:`str`, optional): |business_id_str|
|
||||
|
||||
.. versionadded:: 21.1
|
||||
question_parse_mode (:obj:`str`, optional): Mode for parsing entities in the question.
|
||||
See the constants in :class:`telegram.constants.ParseMode` for the available modes.
|
||||
Currently, only custom emoji entities are allowed.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
question_entities (Sequence[:class:`telegram.Message`], optional): Special entities
|
||||
that appear in the poll :paramref:`question`. It can be specified instead of
|
||||
:paramref:`question_parse_mode`.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Keyword Args:
|
||||
allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply|
|
||||
|
@ -6924,7 +6960,10 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
data: JSONDict = {
|
||||
"chat_id": chat_id,
|
||||
"question": question,
|
||||
"options": options,
|
||||
"options": [
|
||||
InputPollOption(option) if isinstance(option, str) else option
|
||||
for option in options
|
||||
],
|
||||
"explanation_parse_mode": explanation_parse_mode,
|
||||
"is_anonymous": is_anonymous,
|
||||
"type": type,
|
||||
|
@ -6935,6 +6974,8 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified.
|
|||
"explanation_entities": explanation_entities,
|
||||
"open_period": open_period,
|
||||
"close_date": close_date,
|
||||
"question_parse_mode": question_parse_mode,
|
||||
"question_entities": question_entities,
|
||||
}
|
||||
|
||||
return await self._send_message(
|
||||
|
|
|
@ -189,7 +189,7 @@ class BusinessMessagesDeleted(TelegramObject):
|
|||
|
||||
class BusinessIntro(TelegramObject):
|
||||
"""
|
||||
This object represents the intro of a business account.
|
||||
This object contains information about the start page settings of a Telegram Business account.
|
||||
|
||||
Objects of this class are comparable in terms of equality.
|
||||
Two objects of this class are considered equal, if their
|
||||
|
@ -246,7 +246,7 @@ class BusinessIntro(TelegramObject):
|
|||
|
||||
class BusinessLocation(TelegramObject):
|
||||
"""
|
||||
This object represents the location of a business account.
|
||||
This object contains information about the location of a Telegram Business account.
|
||||
|
||||
Objects of this class are comparable in terms of equality.
|
||||
Two objects of this class are considered equal, if their
|
||||
|
@ -298,7 +298,7 @@ class BusinessLocation(TelegramObject):
|
|||
|
||||
class BusinessOpeningHoursInterval(TelegramObject):
|
||||
"""
|
||||
This object represents the time intervals describing business opening hours.
|
||||
This object describes an interval of time during which a business is open.
|
||||
|
||||
Objects of this class are comparable in terms of equality.
|
||||
Two objects of this class are considered equal, if their
|
||||
|
@ -390,7 +390,7 @@ class BusinessOpeningHoursInterval(TelegramObject):
|
|||
|
||||
class BusinessOpeningHours(TelegramObject):
|
||||
"""
|
||||
This object represents the opening hours of a business account.
|
||||
This object describes the opening hours of a business.
|
||||
|
||||
Objects of this class are comparable in terms of equality.
|
||||
Two objects of this class are considered equal, if their
|
||||
|
|
|
@ -461,6 +461,7 @@ class CallbackQuery(TelegramObject):
|
|||
horizontal_accuracy: Optional[float] = None,
|
||||
heading: Optional[int] = None,
|
||||
proximity_alert_radius: Optional[int] = None,
|
||||
live_period: Optional[int] = None,
|
||||
*,
|
||||
location: Optional[Location] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
|
@ -509,6 +510,7 @@ class CallbackQuery(TelegramObject):
|
|||
horizontal_accuracy=horizontal_accuracy,
|
||||
heading=heading,
|
||||
proximity_alert_radius=proximity_alert_radius,
|
||||
live_period=live_period,
|
||||
chat_id=None,
|
||||
message_id=None,
|
||||
)
|
||||
|
@ -525,6 +527,7 @@ class CallbackQuery(TelegramObject):
|
|||
horizontal_accuracy=horizontal_accuracy,
|
||||
heading=heading,
|
||||
proximity_alert_radius=proximity_alert_radius,
|
||||
live_period=live_period,
|
||||
)
|
||||
|
||||
async def stop_message_live_location(
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
"""This module contains an object that represents a Telegram Chat."""
|
||||
from datetime import datetime
|
||||
from html import escape
|
||||
from typing import TYPE_CHECKING, Final, Optional, Sequence, Tuple, Union
|
||||
from typing import TYPE_CHECKING, Any, Final, Optional, Sequence, Tuple, Union
|
||||
|
||||
from telegram import constants
|
||||
from telegram._birthdate import Birthdate
|
||||
|
@ -36,9 +36,11 @@ 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.types import CorrectOptionID, FileInput, JSONDict, ODVInput, ReplyMarkup
|
||||
from telegram._utils.warnings import warn
|
||||
from telegram.helpers import escape_markdown
|
||||
from telegram.helpers import mention_html as helpers_mention_html
|
||||
from telegram.helpers import mention_markdown as helpers_mention_markdown
|
||||
from telegram.warnings import PTBDeprecationWarning
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import (
|
||||
|
@ -57,6 +59,7 @@ if TYPE_CHECKING:
|
|||
InputMediaDocument,
|
||||
InputMediaPhoto,
|
||||
InputMediaVideo,
|
||||
InputPollOption,
|
||||
LabeledPrice,
|
||||
LinkPreviewOptions,
|
||||
Location,
|
||||
|
@ -74,6 +77,45 @@ if TYPE_CHECKING:
|
|||
)
|
||||
|
||||
|
||||
_deprecated_attrs = (
|
||||
"accent_color_id",
|
||||
"active_usernames",
|
||||
"available_reactions",
|
||||
"background_custom_emoji_id",
|
||||
"bio",
|
||||
"birthdate",
|
||||
"business_intro",
|
||||
"business_location",
|
||||
"business_opening_hours",
|
||||
"can_set_sticker_set",
|
||||
"custom_emoji_sticker_set_name",
|
||||
"description",
|
||||
"emoji_status_custom_emoji_id",
|
||||
"emoji_status_expiration_date",
|
||||
"has_aggressive_anti_spam_enabled",
|
||||
"has_hidden_members",
|
||||
"has_private_forwards",
|
||||
"has_protected_content",
|
||||
"has_restricted_voice_and_video_messages",
|
||||
"has_visible_history",
|
||||
"invite_link",
|
||||
"join_by_request",
|
||||
"join_to_send_messages",
|
||||
"linked_chat_id",
|
||||
"location",
|
||||
"message_auto_delete_time",
|
||||
"permissions",
|
||||
"personal_chat",
|
||||
"photo",
|
||||
"pinned_message",
|
||||
"profile_accent_color_id",
|
||||
"profile_background_custom_emoji_id",
|
||||
"slow_mode_delay",
|
||||
"sticker_set_name",
|
||||
"unrestrict_boost_count",
|
||||
)
|
||||
|
||||
|
||||
class Chat(TelegramObject):
|
||||
"""This object represents a chat.
|
||||
|
||||
|
@ -107,62 +149,134 @@ class Chat(TelegramObject):
|
|||
last_name (:obj:`str`, optional): Last name of the other party in a private chat.
|
||||
photo (:class:`telegram.ChatPhoto`, optional): Chat photo.
|
||||
Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
bio (:obj:`str`, optional): Bio of the other party in a private chat. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
has_private_forwards (:obj:`bool`, optional): :obj:`True`, if privacy settings of the other
|
||||
party in the private chat allows to use ``tg://user?id=<user_id>`` links only in chats
|
||||
with the user. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 13.9
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
description (:obj:`str`, optional): Description, for groups, supergroups and channel chats.
|
||||
Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
invite_link (:obj:`str`, optional): Primary invite link, for groups, supergroups and
|
||||
channel. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
pinned_message (:class:`telegram.Message`, optional): The most recent pinned message
|
||||
(by sending date). Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
permissions (:class:`telegram.ChatPermissions`): Optional. Default chat member permissions,
|
||||
for groups and supergroups. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
slow_mode_delay (:obj:`int`, optional): For supergroups, the minimum allowed delay between
|
||||
consecutive messages sent by each unprivileged user.
|
||||
Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
message_auto_delete_time (:obj:`int`, optional): The time after which all messages sent to
|
||||
the chat will be automatically deleted; in seconds. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
has_protected_content (:obj:`bool`, optional): :obj:`True`, if messages from the chat can't
|
||||
be forwarded to other chats. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 13.9
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
has_visible_history (:obj:`bool`, optional): :obj:`True`, if new chat members will have
|
||||
access to old messages; available only to chat administrators. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 20.8
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
sticker_set_name (:obj:`str`, optional): For supergroups, name of group sticker set.
|
||||
Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
can_set_sticker_set (:obj:`bool`, optional): :obj:`True`, if the bot can change group the
|
||||
sticker set. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
linked_chat_id (:obj:`int`, optional): Unique identifier for the linked chat, i.e. the
|
||||
discussion group identifier for a channel and vice versa; for supergroups and channel
|
||||
chats. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
location (:class:`telegram.ChatLocation`, optional): For supergroups, the location to which
|
||||
the supergroup is connected. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
join_to_send_messages (:obj:`bool`, optional): :obj:`True`, if users need to join the
|
||||
supergroup before they can send messages. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
join_by_request (:obj:`bool`, optional): :obj:`True`, if all users directly joining the
|
||||
supergroup need to be approved by supergroup administrators. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
has_restricted_voice_and_video_messages (:obj:`bool`, optional): :obj:`True`, if the
|
||||
privacy settings of the other party restrict sending voice and video note messages
|
||||
in the private chat. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
is_forum (:obj:`bool`, optional): :obj:`True`, if the supergroup chat is a forum
|
||||
(has topics_ enabled).
|
||||
|
||||
|
@ -173,27 +287,47 @@ class Chat(TelegramObject):
|
|||
only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
business_intro (:class:`telegram.BusinessIntro`, optional): For private chats with
|
||||
business accounts, the intro of the business. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 21.1
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
business_location (:class:`telegram.BusinessLocation`, optional): For private chats with
|
||||
business accounts, the location of the business. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 21.1
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
business_opening_hours (:class:`telegram.BusinessOpeningHours`, optional): For private
|
||||
chats with business accounts, the opening hours of the business. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 21.1
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
available_reactions (Sequence[:class:`telegram.ReactionType`], optional): List of available
|
||||
reactions allowed in the chat. If omitted, then all of
|
||||
:const:`telegram.constants.ReactionEmoji` are allowed. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 20.8
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
accent_color_id (:obj:`int`, optional): Identifier of the
|
||||
:class:`accent color <telegram.constants.AccentColor>` for the chat name and
|
||||
backgrounds of the chat photo, reply header, and link preview. See `accent colors`_
|
||||
|
@ -201,62 +335,110 @@ class Chat(TelegramObject):
|
|||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 20.8
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
background_custom_emoji_id (:obj:`str`, optional): Custom emoji identifier of emoji chosen
|
||||
by the chat for the reply header and link preview background. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 20.8
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
profile_accent_color_id (:obj:`int`, optional): Identifier of the
|
||||
:class:`accent color <telegram.constants.ProfileAccentColor>` for the chat's profile
|
||||
background. See profile `accent colors`_ for more details. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 20.8
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
profile_background_custom_emoji_id (:obj:`str`, optional): Custom emoji identifier of
|
||||
the emoji chosen by the chat for its profile background. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 20.8
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
emoji_status_custom_emoji_id (:obj:`str`, optional): Custom emoji identifier of emoji
|
||||
status of the chat or the other party in a private chat. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
emoji_status_expiration_date (:class:`datetime.datetime`, optional): Expiration date of
|
||||
emoji status of the chat or the other party in a private chat, in seconds. Returned
|
||||
only in :meth:`telegram.Bot.get_chat`.
|
||||
|datetime_localization|
|
||||
|
||||
.. versionadded:: 20.5
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
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
|
||||
administrators. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
has_hidden_members (:obj:`bool`, optional): :obj:`True`, if non-administrators can only
|
||||
get the list of bots and administrators in the chat. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
unrestrict_boost_count (:obj:`int`, optional): For supergroups, the minimum number of
|
||||
boosts that a non-administrator user needs to add in order to ignore slow mode and chat
|
||||
permissions. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 21.0
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
custom_emoji_sticker_set_name (:obj:`str`, optional): For supergroups, the name of the
|
||||
group's custom emoji sticker set. Custom emoji from this set can be used by all users
|
||||
and bots in the group. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 21.0
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
birthdate (:obj:`telegram.Birthdate`, optional): For private chats,
|
||||
the date of birth of the user. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 21.1
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
personal_chat (:obj:`telegram.Chat`, optional): For private chats, the personal channel of
|
||||
the user. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 21.1
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
|
||||
Attributes:
|
||||
id (:obj:`int`): Unique identifier for this chat. This number may be greater than 32 bits
|
||||
and some programming languages may have difficulty/silent defects in interpreting it.
|
||||
|
@ -271,62 +453,134 @@ class Chat(TelegramObject):
|
|||
last_name (:obj:`str`): Optional. Last name of the other party in a private chat.
|
||||
photo (:class:`telegram.ChatPhoto`): Optional. Chat photo.
|
||||
Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
bio (:obj:`str`): Optional. Bio of the other party in a private chat. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
has_private_forwards (:obj:`bool`): Optional. :obj:`True`, if privacy settings of the other
|
||||
party in the private chat allows to use ``tg://user?id=<user_id>`` links only in chats
|
||||
with the user. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 13.9
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
description (:obj:`str`): Optional. Description, for groups, supergroups and channel chats.
|
||||
Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
invite_link (:obj:`str`): Optional. Primary invite link, for groups, supergroups and
|
||||
channel. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
pinned_message (:class:`telegram.Message`): Optional. The most recent pinned message
|
||||
(by sending date). Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
permissions (:class:`telegram.ChatPermissions`): Optional. Default chat member permissions,
|
||||
for groups and supergroups. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
slow_mode_delay (:obj:`int`): Optional. For supergroups, the minimum allowed delay between
|
||||
consecutive messages sent by each unprivileged user. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
message_auto_delete_time (:obj:`int`): Optional. The time after which all messages sent to
|
||||
the chat will be automatically deleted; in seconds. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 13.4
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
has_protected_content (:obj:`bool`): Optional. :obj:`True`, if messages from the chat can't
|
||||
be forwarded to other chats. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 13.9
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
has_visible_history (:obj:`bool`): Optional. :obj:`True`, if new chat members will have
|
||||
access to old messages; available only to chat administrators. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 20.8
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
sticker_set_name (:obj:`str`): Optional. For supergroups, name of Group sticker set.
|
||||
Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
can_set_sticker_set (:obj:`bool`): Optional. :obj:`True`, if the bot can change group the
|
||||
sticker set. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
linked_chat_id (:obj:`int`): Optional. Unique identifier for the linked chat, i.e. the
|
||||
discussion group identifier for a channel and vice versa; for supergroups and channel
|
||||
chats. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
location (:class:`telegram.ChatLocation`): Optional. For supergroups, the location to which
|
||||
the supergroup is connected. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
join_to_send_messages (:obj:`bool`): Optional. :obj:`True`, if users need to join
|
||||
the supergroup before they can send messages. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
join_by_request (:obj:`bool`): Optional. :obj:`True`, if all users directly
|
||||
joining the supergroup need to be approved by supergroup administrators. Returned only
|
||||
in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
has_restricted_voice_and_video_messages (:obj:`bool`): Optional. :obj:`True`, if the
|
||||
privacy settings of the other party restrict sending voice and video note messages
|
||||
in the private chat. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
is_forum (:obj:`bool`): Optional. :obj:`True`, if the supergroup chat is a forum
|
||||
(has topics_ enabled).
|
||||
|
||||
|
@ -339,27 +593,47 @@ class Chat(TelegramObject):
|
|||
obtained via :meth:`~telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
business_intro (:class:`telegram.BusinessIntro`): Optional. For private chats with
|
||||
business accounts, the intro of the business. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 21.1
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
business_location (:class:`telegram.BusinessLocation`): Optional. For private chats with
|
||||
business accounts, the location of the business. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 21.1
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
business_opening_hours (:class:`telegram.BusinessOpeningHours`): Optional. For private
|
||||
chats with business accounts, the opening hours of the business. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 21.1
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
available_reactions (Tuple[:class:`telegram.ReactionType`]): Optional. List of available
|
||||
reactions allowed in the chat. If omitted, then all of
|
||||
:const:`telegram.constants.ReactionEmoji` are allowed. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 20.8
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
accent_color_id (:obj:`int`): Optional. Identifier of the
|
||||
:class:`accent color <telegram.constants.AccentColor>` for the chat name and
|
||||
backgrounds of the chat photo, reply header, and link preview. See `accent colors`_
|
||||
|
@ -367,62 +641,110 @@ class Chat(TelegramObject):
|
|||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 20.8
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
background_custom_emoji_id (:obj:`str`): Optional. Custom emoji identifier of emoji chosen
|
||||
by the chat for the reply header and link preview background. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 20.8
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
profile_accent_color_id (:obj:`int`): Optional. Identifier of the
|
||||
:class:`accent color <telegram.constants.ProfileAccentColor>` for the chat's profile
|
||||
background. See profile `accent colors`_ for more details. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 20.8
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
profile_background_custom_emoji_id (:obj:`str`): Optional. Custom emoji identifier of
|
||||
the emoji chosen by the chat for its profile background. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 20.8
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
emoji_status_custom_emoji_id (:obj:`str`): Optional. Custom emoji identifier of emoji
|
||||
status of the chat or the other party in a private chat. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
emoji_status_expiration_date (:class:`datetime.datetime`): Optional. Expiration date of
|
||||
emoji status of the chat or the other party in a private chat, in seconds. Returned
|
||||
only in :meth:`telegram.Bot.get_chat`.
|
||||
|datetime_localization|
|
||||
|
||||
.. versionadded:: 20.5
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
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
|
||||
administrators. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
has_hidden_members (:obj:`bool`): Optional. :obj:`True`, if non-administrators can only
|
||||
get the list of bots and administrators in the chat. Returned only in
|
||||
:meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
unrestrict_boost_count (:obj:`int`): Optional. For supergroups, the minimum number of
|
||||
boosts that a non-administrator user needs to add in order to ignore slow mode and chat
|
||||
permissions. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 21.0
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
custom_emoji_sticker_set_name (:obj:`str`): Optional. For supergroups, the name of the
|
||||
group's custom emoji sticker set. Custom emoji from this set can be used by all users
|
||||
and bots in the group. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 21.0
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
birthdate (:obj:`telegram.Birthdate`): Optional. For private chats,
|
||||
the date of birth of the user. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 21.1
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
personal_chat (:obj:`telegram.Chat`): Optional. For private chats, the personal channel of
|
||||
the user. Returned only in :meth:`telegram.Bot.get_chat`.
|
||||
|
||||
.. versionadded:: 21.1
|
||||
|
||||
.. deprecated:: NEXT.VERSION
|
||||
In accordance to Bot API 7.3, this attribute will be moved to
|
||||
:class:`telegram.ChatFullInfo`.
|
||||
|
||||
.. _topics: https://telegram.org/blog/topics-in-groups-collectible-usernames#topics-in-groups
|
||||
.. _accent colors: https://core.telegram.org/bots/api#accent-colors
|
||||
"""
|
||||
|
@ -471,7 +793,6 @@ class Chat(TelegramObject):
|
|||
"unrestrict_boost_count",
|
||||
"username",
|
||||
)
|
||||
|
||||
SENDER: Final[str] = constants.ChatType.SENDER
|
||||
""":const:`telegram.constants.ChatType.SENDER`
|
||||
|
||||
|
@ -518,7 +839,7 @@ class Chat(TelegramObject):
|
|||
has_aggressive_anti_spam_enabled: Optional[bool] = None,
|
||||
has_hidden_members: Optional[bool] = None,
|
||||
available_reactions: Optional[Sequence[ReactionType]] = None,
|
||||
accent_color_id: Optional[int] = None,
|
||||
accent_color_id: Optional[int] = None, # required in API 7.3 - Optional for back compat
|
||||
background_custom_emoji_id: Optional[str] = None,
|
||||
profile_accent_color_id: Optional[int] = None,
|
||||
profile_background_custom_emoji_id: Optional[str] = None,
|
||||
|
@ -585,10 +906,30 @@ class Chat(TelegramObject):
|
|||
self.business_location: Optional["BusinessLocation"] = business_location
|
||||
self.business_opening_hours: Optional["BusinessOpeningHours"] = business_opening_hours
|
||||
|
||||
if self.__class__ is Chat:
|
||||
for arg in _deprecated_attrs:
|
||||
if (val := object.__getattribute__(self, arg)) is not None and val != ():
|
||||
warn(
|
||||
f"The argument `{arg}` is deprecated and will only be available via "
|
||||
"`ChatFullInfo` in the future.",
|
||||
stacklevel=2,
|
||||
category=PTBDeprecationWarning,
|
||||
)
|
||||
|
||||
self._id_attrs = (self.id,)
|
||||
|
||||
self._freeze()
|
||||
|
||||
def __getattribute__(self, name: str) -> Any:
|
||||
if name in _deprecated_attrs and self.__class__ is Chat:
|
||||
warn(
|
||||
f"The attribute `{name}` is deprecated and will only be accessible via "
|
||||
"`ChatFullInfo` in the future.",
|
||||
stacklevel=2,
|
||||
category=PTBDeprecationWarning,
|
||||
)
|
||||
return super().__getattribute__(name)
|
||||
|
||||
@property
|
||||
def effective_name(self) -> Optional[str]:
|
||||
"""
|
||||
|
@ -658,7 +999,7 @@ class Chat(TelegramObject):
|
|||
data["location"] = ChatLocation.de_json(data.get("location"), bot)
|
||||
data["available_reactions"] = ReactionType.de_list(data.get("available_reactions"), bot)
|
||||
data["birthdate"] = Birthdate.de_json(data.get("birthdate"), bot)
|
||||
data["personal_chat"] = cls.de_json(data.get("personal_chat"), bot)
|
||||
data["personal_chat"] = Chat.de_json(data.get("personal_chat"), bot)
|
||||
data["business_intro"] = BusinessIntro.de_json(data.get("business_intro"), bot)
|
||||
data["business_location"] = BusinessLocation.de_json(data.get("business_location"), bot)
|
||||
data["business_opening_hours"] = BusinessOpeningHours.de_json(
|
||||
|
@ -2545,7 +2886,7 @@ class Chat(TelegramObject):
|
|||
async def send_poll(
|
||||
self,
|
||||
question: str,
|
||||
options: Sequence[str],
|
||||
options: Sequence[Union[str, "InputPollOption"]],
|
||||
is_anonymous: Optional[bool] = None,
|
||||
type: Optional[str] = None,
|
||||
allows_multiple_answers: Optional[bool] = None,
|
||||
|
@ -2562,6 +2903,8 @@ class Chat(TelegramObject):
|
|||
message_thread_id: Optional[int] = None,
|
||||
reply_parameters: Optional["ReplyParameters"] = None,
|
||||
business_connection_id: Optional[str] = None,
|
||||
question_parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
question_entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
*,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
|
@ -2608,6 +2951,8 @@ class Chat(TelegramObject):
|
|||
protect_content=protect_content,
|
||||
message_thread_id=message_thread_id,
|
||||
business_connection_id=business_connection_id,
|
||||
question_parse_mode=question_parse_mode,
|
||||
question_entities=question_entities,
|
||||
)
|
||||
|
||||
async def send_copy(
|
||||
|
|
|
@ -80,8 +80,9 @@ class ChatAdministratorRights(TelegramObject):
|
|||
.. versionadded:: 20.6
|
||||
.. versionchanged:: 21.0
|
||||
|non_optional_story_argument|
|
||||
can_edit_stories (:obj:`bool`): :obj:`True`, if the administrator can edit
|
||||
stories posted by other users.
|
||||
can_edit_stories (:obj:`bool`): :obj:`True`, if the administrator can edit stories posted
|
||||
by other users, post stories to the chat page, pin chat stories, and access the chat's
|
||||
story archive
|
||||
|
||||
.. versionadded:: 20.6
|
||||
.. versionchanged:: 21.0
|
||||
|
@ -128,8 +129,9 @@ class ChatAdministratorRights(TelegramObject):
|
|||
.. versionadded:: 20.6
|
||||
.. versionchanged:: 21.0
|
||||
|non_optional_story_argument|
|
||||
can_edit_stories (:obj:`bool`): :obj:`True`, if the administrator can edit
|
||||
stories posted by other users.
|
||||
can_edit_stories (:obj:`bool`): :obj:`True`, if the administrator can edit stories posted
|
||||
by other users, post stories to the chat page, pin chat stories, and access the chat's
|
||||
story archive
|
||||
|
||||
.. versionadded:: 20.6
|
||||
.. versionchanged:: 21.0
|
||||
|
|
540
telegram/_chatbackground.py
Normal file
540
telegram/_chatbackground.py
Normal file
|
@ -0,0 +1,540 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2024
|
||||
# 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 chat backgrounds."""
|
||||
from typing import TYPE_CHECKING, Dict, Final, Optional, Sequence, Tuple, Type
|
||||
|
||||
from telegram import constants
|
||||
from telegram._files.document import Document
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils import enum
|
||||
from telegram._utils.argumentparsing import parse_sequence_arg
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Bot
|
||||
|
||||
|
||||
class BackgroundFill(TelegramObject):
|
||||
"""Base class for Telegram BackgroundFill Objects. It can be one of:
|
||||
|
||||
* :class:`telegram.BackgroundFillSolid`
|
||||
* :class:`telegram.BackgroundFillGradient`
|
||||
* :class:`telegram.BackgroundFillFreeformGradient`
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`type` is equal.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Args:
|
||||
type (:obj:`str`): Type of the background fill. Can be one of:
|
||||
:attr:`~telegram.BackgroundFill.SOLID`, :attr:`~telegram.BackgroundFill.GRADIENT`
|
||||
or :attr:`~telegram.BackgroundFill.FREEFORM_GRADIENT`.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): Type of the background fill. Can be one of:
|
||||
:attr:`~telegram.BackgroundFill.SOLID`, :attr:`~telegram.BackgroundFill.GRADIENT`
|
||||
or :attr:`~telegram.BackgroundFill.FREEFORM_GRADIENT`.
|
||||
"""
|
||||
|
||||
__slots__ = ("type",)
|
||||
|
||||
SOLID: Final[constants.BackgroundFillType] = constants.BackgroundFillType.SOLID
|
||||
""":const:`telegram.constants.BackgroundFillType.SOLID`"""
|
||||
GRADIENT: Final[constants.BackgroundFillType] = constants.BackgroundFillType.GRADIENT
|
||||
""":const:`telegram.constants.BackgroundFillType.GRADIENT`"""
|
||||
FREEFORM_GRADIENT: Final[constants.BackgroundFillType] = (
|
||||
constants.BackgroundFillType.FREEFORM_GRADIENT
|
||||
)
|
||||
""":const:`telegram.constants.BackgroundFillType.FREEFORM_GRADIENT`"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
type: str, # pylint: disable=redefined-builtin
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
# Required by all subclasses
|
||||
self.type: str = enum.get_member(constants.BackgroundFillType, type, type)
|
||||
|
||||
self._id_attrs = (self.type,)
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["BackgroundFill"]:
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
_class_mapping: Dict[str, Type[BackgroundFill]] = {
|
||||
cls.SOLID: BackgroundFillSolid,
|
||||
cls.GRADIENT: BackgroundFillGradient,
|
||||
cls.FREEFORM_GRADIENT: BackgroundFillFreeformGradient,
|
||||
}
|
||||
|
||||
if cls is BackgroundFill and data.get("type") in _class_mapping:
|
||||
return _class_mapping[data.pop("type")].de_json(data=data, bot=bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
|
||||
class BackgroundFillSolid(BackgroundFill):
|
||||
"""
|
||||
The background is filled using the selected color.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`color` is equal.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Args:
|
||||
color (:obj:`int`): The color of the background fill in the `RGB24` format.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): Type of the background fill. Always
|
||||
:attr:`~telegram.BackgroundFill.SOLID`.
|
||||
color (:obj:`int`): The color of the background fill in the `RGB24` format.
|
||||
"""
|
||||
|
||||
__slots__ = ("color",)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
color: int,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
super().__init__(type=self.SOLID, api_kwargs=api_kwargs)
|
||||
|
||||
with self._unfrozen():
|
||||
self.color: int = color
|
||||
|
||||
self._id_attrs = (self.color,)
|
||||
|
||||
|
||||
class BackgroundFillGradient(BackgroundFill):
|
||||
"""
|
||||
The background is a gradient fill.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`top_color`, :attr:`bottom_color`
|
||||
and :attr:`rotation_angle` are equal.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Args:
|
||||
top_color (:obj:`int`): Top color of the gradient in the `RGB24` format.
|
||||
bottom_color (:obj:`int`): Bottom color of the gradient in the `RGB24` format.
|
||||
rotation_angle (:obj:`int`): Clockwise rotation angle of the background
|
||||
fill in degrees;
|
||||
0-:tg-const:`telegram.constants.BackgroundFillLimit.MAX_ROTATION_ANGLE`.
|
||||
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): Type of the background fill. Always
|
||||
:attr:`~telegram.BackgroundFill.GRADIENT`.
|
||||
top_color (:obj:`int`): Top color of the gradient in the `RGB24` format.
|
||||
bottom_color (:obj:`int`): Bottom color of the gradient in the `RGB24` format.
|
||||
rotation_angle (:obj:`int`): Clockwise rotation angle of the background
|
||||
fill in degrees;
|
||||
0-:tg-const:`telegram.constants.BackgroundFillLimit.MAX_ROTATION_ANGLE`.
|
||||
"""
|
||||
|
||||
__slots__ = ("bottom_color", "rotation_angle", "top_color")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
top_color: int,
|
||||
bottom_color: int,
|
||||
rotation_angle: int,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
super().__init__(type=self.GRADIENT, api_kwargs=api_kwargs)
|
||||
|
||||
with self._unfrozen():
|
||||
self.top_color: int = top_color
|
||||
self.bottom_color: int = bottom_color
|
||||
self.rotation_angle: int = rotation_angle
|
||||
|
||||
self._id_attrs = (self.top_color, self.bottom_color, self.rotation_angle)
|
||||
|
||||
|
||||
class BackgroundFillFreeformGradient(BackgroundFill):
|
||||
"""
|
||||
The background is a freeform gradient that rotates after every message in the chat.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`colors` is equal.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Args:
|
||||
colors (Sequence[:obj:`int`]): A list of the 3 or 4 base colors that are used to
|
||||
generate the freeform gradient in the `RGB24` format
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): Type of the background fill. Always
|
||||
:attr:`~telegram.BackgroundFill.FREEFORM_GRADIENT`.
|
||||
colors (Sequence[:obj:`int`]): A list of the 3 or 4 base colors that are used to
|
||||
generate the freeform gradient in the `RGB24` format
|
||||
"""
|
||||
|
||||
__slots__ = ("colors",)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
colors: Sequence[int],
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
super().__init__(type=self.FREEFORM_GRADIENT, api_kwargs=api_kwargs)
|
||||
|
||||
with self._unfrozen():
|
||||
self.colors: Tuple[int, ...] = parse_sequence_arg(colors)
|
||||
|
||||
self._id_attrs = (self.colors,)
|
||||
|
||||
|
||||
class BackgroundType(TelegramObject):
|
||||
"""Base class for Telegram BackgroundType Objects. It can be one of:
|
||||
|
||||
* :class:`telegram.BackgroundTypeFill`
|
||||
* :class:`telegram.BackgroundTypeWallpaper`
|
||||
* :class:`telegram.BackgroundTypePattern`
|
||||
* :class:`telegram.BackgroundTypeChatTheme`.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`type` is equal.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Args:
|
||||
type (:obj:`str`): Type of the background. Can be one of:
|
||||
:attr:`~telegram.BackgroundType.FILL`, :attr:`~telegram.BackgroundType.WALLPAPER`
|
||||
:attr:`~telegram.BackgroundType.PATTERN` or
|
||||
:attr:`~telegram.BackgroundType.CHAT_THEME`.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): Type of the background. Can be one of:
|
||||
:attr:`~telegram.BackgroundType.FILL`, :attr:`~telegram.BackgroundType.WALLPAPER`
|
||||
:attr:`~telegram.BackgroundType.PATTERN` or
|
||||
:attr:`~telegram.BackgroundType.CHAT_THEME`.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ("type",)
|
||||
|
||||
FILL: Final[constants.BackgroundTypeType] = constants.BackgroundTypeType.FILL
|
||||
""":const:`telegram.constants.BackgroundTypeType.FILL`"""
|
||||
WALLPAPER: Final[constants.BackgroundTypeType] = constants.BackgroundTypeType.WALLPAPER
|
||||
""":const:`telegram.constants.BackgroundTypeType.WALLPAPER`"""
|
||||
PATTERN: Final[constants.BackgroundTypeType] = constants.BackgroundTypeType.PATTERN
|
||||
""":const:`telegram.constants.BackgroundTypeType.PATTERN`"""
|
||||
CHAT_THEME: Final[constants.BackgroundTypeType] = constants.BackgroundTypeType.CHAT_THEME
|
||||
""":const:`telegram.constants.BackgroundTypeType.CHAT_THEME`"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
type: str, # pylint: disable=redefined-builtin
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
# Required by all subclasses
|
||||
self.type: str = enum.get_member(constants.BackgroundTypeType, type, type)
|
||||
|
||||
self._id_attrs = (self.type,)
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["BackgroundType"]:
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
_class_mapping: Dict[str, Type[BackgroundType]] = {
|
||||
cls.FILL: BackgroundTypeFill,
|
||||
cls.WALLPAPER: BackgroundTypeWallpaper,
|
||||
cls.PATTERN: BackgroundTypePattern,
|
||||
cls.CHAT_THEME: BackgroundTypeChatTheme,
|
||||
}
|
||||
|
||||
if cls is BackgroundType and data.get("type") in _class_mapping:
|
||||
return _class_mapping[data.pop("type")].de_json(data=data, bot=bot)
|
||||
|
||||
if "fill" in data:
|
||||
data["fill"] = BackgroundFill.de_json(data.get("fill"), bot)
|
||||
|
||||
if "document" in data:
|
||||
data["document"] = Document.de_json(data.get("document"), bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
|
||||
class BackgroundTypeFill(BackgroundType):
|
||||
"""
|
||||
The background is automatically filled based on the selected colors.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`fill` and :attr:`dark_theme_dimming` are equal.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Args:
|
||||
fill (:obj:`telegram.BackgroundFill`): The background fill.
|
||||
dark_theme_dimming (:obj:`int`): Dimming of the background in dark themes, as a
|
||||
percentage;
|
||||
0-:tg-const:`telegram.constants.BackgroundTypeLimit.MAX_DIMMING`.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): Type of the background. Always
|
||||
:attr:`~telegram.BackgroundType.FILL`.
|
||||
fill (:obj:`telegram.BackgroundFill`): The background fill.
|
||||
dark_theme_dimming (:obj:`int`): Dimming of the background in dark themes, as a
|
||||
percentage;
|
||||
0-:tg-const:`telegram.constants.BackgroundTypeLimit.MAX_DIMMING`.
|
||||
"""
|
||||
|
||||
__slots__ = ("dark_theme_dimming", "fill")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
fill: BackgroundFill,
|
||||
dark_theme_dimming: int,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
super().__init__(type=self.FILL, api_kwargs=api_kwargs)
|
||||
|
||||
with self._unfrozen():
|
||||
self.fill: BackgroundFill = fill
|
||||
self.dark_theme_dimming: int = dark_theme_dimming
|
||||
|
||||
self._id_attrs = (self.fill, self.dark_theme_dimming)
|
||||
|
||||
|
||||
class BackgroundTypeWallpaper(BackgroundType):
|
||||
"""
|
||||
The background is a wallpaper in the `JPEG` format.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`document` and :attr:`dark_theme_dimming` are equal.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Args:
|
||||
document (:obj:`telegram.Document`): Document with the wallpaper
|
||||
dark_theme_dimming (:obj:`int`): Dimming of the background in dark themes, as a
|
||||
percentage;
|
||||
0-:tg-const:`telegram.constants.BackgroundTypeLimit.MAX_DIMMING`.
|
||||
is_blurred (:obj:`bool`, optional): :obj:`True`, if the wallpaper is downscaled to fit
|
||||
in a 450x450 square and then box-blurred with radius 12
|
||||
is_moving (:obj:`bool`, optional): :obj:`True`, if the background moves slightly
|
||||
when the device is tilted
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): Type of the background. Always
|
||||
:attr:`~telegram.BackgroundType.WALLPAPER`.
|
||||
document (:obj:`telegram.Document`): Document with the wallpaper
|
||||
dark_theme_dimming (:obj:`int`): Dimming of the background in dark themes, as a
|
||||
percentage;
|
||||
0-:tg-const:`telegram.constants.BackgroundTypeLimit.MAX_DIMMING`.
|
||||
is_blurred (:obj:`bool`): Optional. :obj:`True`, if the wallpaper is downscaled to fit
|
||||
in a 450x450 square and then box-blurred with radius 12
|
||||
is_moving (:obj:`bool`): Optional. :obj:`True`, if the background moves slightly
|
||||
when the device is tilted
|
||||
"""
|
||||
|
||||
__slots__ = ("dark_theme_dimming", "document", "is_blurred", "is_moving")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
document: Document,
|
||||
dark_theme_dimming: int,
|
||||
is_blurred: Optional[bool] = None,
|
||||
is_moving: Optional[bool] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
super().__init__(type=self.WALLPAPER, api_kwargs=api_kwargs)
|
||||
|
||||
with self._unfrozen():
|
||||
# Required
|
||||
self.document: Document = document
|
||||
self.dark_theme_dimming: int = dark_theme_dimming
|
||||
# Optionals
|
||||
self.is_blurred: Optional[bool] = is_blurred
|
||||
self.is_moving: Optional[bool] = is_moving
|
||||
|
||||
self._id_attrs = (self.document, self.dark_theme_dimming)
|
||||
|
||||
|
||||
class BackgroundTypePattern(BackgroundType):
|
||||
"""
|
||||
The background is a `PNG` or `TGV` (gzipped subset of `SVG` with `MIME` type
|
||||
`"application/x-tgwallpattern"`) pattern to be combined with the background fill
|
||||
chosen by the user.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`document` and :attr:`fill` and :attr:`intensity` are equal.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Args:
|
||||
document (:obj:`telegram.Document`): Document with the pattern.
|
||||
fill (:obj:`telegram.BackgroundFill`): The background fill that is combined with
|
||||
the pattern.
|
||||
intensity (:obj:`int`): Intensity of the pattern when it is shown above the filled
|
||||
background;
|
||||
0-:tg-const:`telegram.constants.BackgroundTypeLimit.MAX_INTENSITY`.
|
||||
is_inverted (:obj:`int`, optional): :obj:`True`, if the background fill must be applied
|
||||
only to the pattern itself. All other pixels are black in this case. For dark
|
||||
themes only.
|
||||
is_moving (:obj:`bool`, optional): :obj:`True`, if the background moves slightly
|
||||
when the device is tilted.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): Type of the background. Always
|
||||
:attr:`~telegram.BackgroundType.PATTERN`.
|
||||
document (:obj:`telegram.Document`): Document with the pattern.
|
||||
fill (:obj:`telegram.BackgroundFill`): The background fill that is combined with
|
||||
the pattern.
|
||||
intensity (:obj:`int`): Intensity of the pattern when it is shown above the filled
|
||||
background;
|
||||
0-:tg-const:`telegram.constants.BackgroundTypeLimit.MAX_INTENSITY`.
|
||||
is_inverted (:obj:`int`): Optional. :obj:`True`, if the background fill must be applied
|
||||
only to the pattern itself. All other pixels are black in this case. For dark
|
||||
themes only.
|
||||
is_moving (:obj:`bool`): Optional. :obj:`True`, if the background moves slightly
|
||||
when the device is tilted.
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
"document",
|
||||
"fill",
|
||||
"intensity",
|
||||
"is_inverted",
|
||||
"is_moving",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
document: Document,
|
||||
fill: BackgroundFill,
|
||||
intensity: int,
|
||||
is_inverted: Optional[bool] = None,
|
||||
is_moving: Optional[bool] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
super().__init__(type=self.PATTERN, api_kwargs=api_kwargs)
|
||||
|
||||
with self._unfrozen():
|
||||
# Required
|
||||
self.document: Document = document
|
||||
self.fill: BackgroundFill = fill
|
||||
self.intensity: int = intensity
|
||||
# Optionals
|
||||
self.is_inverted: Optional[bool] = is_inverted
|
||||
self.is_moving: Optional[bool] = is_moving
|
||||
|
||||
self._id_attrs = (self.document, self.fill, self.intensity)
|
||||
|
||||
|
||||
class BackgroundTypeChatTheme(BackgroundType):
|
||||
"""
|
||||
The background is taken directly from a built-in chat theme.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`theme_name` is equal.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Args:
|
||||
theme_name (:obj:`str`): Name of the chat theme, which is usually an emoji.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): Type of the background. Always
|
||||
:attr:`~telegram.BackgroundType.CHAT_THEME`.
|
||||
theme_name (:obj:`str`): Name of the chat theme, which is usually an emoji.
|
||||
"""
|
||||
|
||||
__slots__ = ("theme_name",)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
theme_name: str,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
super().__init__(type=self.CHAT_THEME, api_kwargs=api_kwargs)
|
||||
|
||||
with self._unfrozen():
|
||||
self.theme_name: str = theme_name
|
||||
|
||||
self._id_attrs = (self.theme_name,)
|
||||
|
||||
|
||||
class ChatBackground(TelegramObject):
|
||||
"""
|
||||
This object represents a chat background.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`type` is equal.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Args:
|
||||
type (:obj:`telegram.BackgroundType`): Type of the background.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`telegram.BackgroundType`): Type of the background.
|
||||
"""
|
||||
|
||||
__slots__ = ("type",)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
type: BackgroundType, # pylint: disable=redefined-builtin
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
self.type: BackgroundType = type
|
||||
|
||||
self._id_attrs = (self.type,)
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ChatBackground"]:
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["type"] = BackgroundType.de_json(data.get("type"), bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
166
telegram/_chatfullinfo.py
Normal file
166
telegram/_chatfullinfo.py
Normal file
|
@ -0,0 +1,166 @@
|
|||
#!/usr/bin/env python
|
||||
# pylint: disable=redefined-builtin
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2024
|
||||
# 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 ChatFullInfo."""
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING, Optional, Sequence
|
||||
|
||||
from telegram._birthdate import Birthdate
|
||||
from telegram._chat import Chat
|
||||
from telegram._chatlocation import ChatLocation
|
||||
from telegram._chatpermissions import ChatPermissions
|
||||
from telegram._files.chatphoto import ChatPhoto
|
||||
from telegram._reaction import ReactionType
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import BusinessIntro, BusinessLocation, BusinessOpeningHours, Message
|
||||
|
||||
|
||||
class ChatFullInfo(Chat):
|
||||
"""
|
||||
This object contains full information about a chat.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`~telegram.Chat.id` is equal.
|
||||
|
||||
Caution:
|
||||
This class is a subclass of :class:`telegram.Chat` and inherits all attributes and methods
|
||||
for backwards compatibility. In the future, this class will *NOT* inherit from
|
||||
:class:`telegram.Chat`.
|
||||
|
||||
.. seealso::
|
||||
All arguments and attributes can be found in :class:`telegram.Chat`.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Args:
|
||||
max_reaction_count (:obj:`int`): The maximum number of reactions that can be set on a
|
||||
message in the chat.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Attributes:
|
||||
max_reaction_count (:obj:`int`): The maximum number of reactions that can be set on a
|
||||
message in the chat.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
"""
|
||||
|
||||
__slots__ = ("max_reaction_count",)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
id: int,
|
||||
type: str,
|
||||
accent_color_id: int, # API 7.3 made this argument required
|
||||
max_reaction_count: int, # NEW arg in api 7.3 and is required
|
||||
title: Optional[str] = None,
|
||||
username: Optional[str] = None,
|
||||
first_name: Optional[str] = None,
|
||||
last_name: Optional[str] = None,
|
||||
is_forum: Optional[bool] = None,
|
||||
photo: Optional[ChatPhoto] = None,
|
||||
active_usernames: Optional[Sequence[str]] = None,
|
||||
birthdate: Optional[Birthdate] = None,
|
||||
business_intro: Optional["BusinessIntro"] = None,
|
||||
business_location: Optional["BusinessLocation"] = None,
|
||||
business_opening_hours: Optional["BusinessOpeningHours"] = None,
|
||||
personal_chat: Optional["Chat"] = None,
|
||||
available_reactions: Optional[Sequence[ReactionType]] = None,
|
||||
background_custom_emoji_id: Optional[str] = None,
|
||||
profile_accent_color_id: Optional[int] = None,
|
||||
profile_background_custom_emoji_id: Optional[str] = None,
|
||||
emoji_status_custom_emoji_id: Optional[str] = None,
|
||||
emoji_status_expiration_date: Optional[datetime] = None,
|
||||
bio: Optional[str] = None,
|
||||
has_private_forwards: Optional[bool] = None,
|
||||
has_restricted_voice_and_video_messages: Optional[bool] = None,
|
||||
join_to_send_messages: Optional[bool] = None,
|
||||
join_by_request: Optional[bool] = None,
|
||||
description: Optional[str] = None,
|
||||
invite_link: Optional[str] = None,
|
||||
pinned_message: Optional["Message"] = None,
|
||||
permissions: Optional[ChatPermissions] = None,
|
||||
slow_mode_delay: Optional[int] = None,
|
||||
unrestrict_boost_count: Optional[int] = None,
|
||||
message_auto_delete_time: Optional[int] = None,
|
||||
has_aggressive_anti_spam_enabled: Optional[bool] = None,
|
||||
has_hidden_members: Optional[bool] = None,
|
||||
has_protected_content: Optional[bool] = None,
|
||||
has_visible_history: Optional[bool] = None,
|
||||
sticker_set_name: Optional[str] = None,
|
||||
can_set_sticker_set: Optional[bool] = None,
|
||||
custom_emoji_sticker_set_name: Optional[str] = None,
|
||||
linked_chat_id: Optional[int] = None,
|
||||
location: Optional[ChatLocation] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
super().__init__(
|
||||
id=id,
|
||||
type=type,
|
||||
title=title,
|
||||
username=username,
|
||||
first_name=first_name,
|
||||
last_name=last_name,
|
||||
photo=photo,
|
||||
description=description,
|
||||
invite_link=invite_link,
|
||||
pinned_message=pinned_message,
|
||||
permissions=permissions,
|
||||
sticker_set_name=sticker_set_name,
|
||||
can_set_sticker_set=can_set_sticker_set,
|
||||
slow_mode_delay=slow_mode_delay,
|
||||
bio=bio,
|
||||
linked_chat_id=linked_chat_id,
|
||||
location=location,
|
||||
message_auto_delete_time=message_auto_delete_time,
|
||||
has_private_forwards=has_private_forwards,
|
||||
has_protected_content=has_protected_content,
|
||||
join_to_send_messages=join_to_send_messages,
|
||||
join_by_request=join_by_request,
|
||||
has_restricted_voice_and_video_messages=has_restricted_voice_and_video_messages,
|
||||
is_forum=is_forum,
|
||||
active_usernames=active_usernames,
|
||||
emoji_status_custom_emoji_id=emoji_status_custom_emoji_id,
|
||||
emoji_status_expiration_date=emoji_status_expiration_date,
|
||||
has_aggressive_anti_spam_enabled=has_aggressive_anti_spam_enabled,
|
||||
has_hidden_members=has_hidden_members,
|
||||
available_reactions=available_reactions,
|
||||
accent_color_id=accent_color_id,
|
||||
background_custom_emoji_id=background_custom_emoji_id,
|
||||
profile_accent_color_id=profile_accent_color_id,
|
||||
profile_background_custom_emoji_id=profile_background_custom_emoji_id,
|
||||
has_visible_history=has_visible_history,
|
||||
unrestrict_boost_count=unrestrict_boost_count,
|
||||
custom_emoji_sticker_set_name=custom_emoji_sticker_set_name,
|
||||
birthdate=birthdate,
|
||||
personal_chat=personal_chat,
|
||||
business_intro=business_intro,
|
||||
business_location=business_location,
|
||||
business_opening_hours=business_opening_hours,
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
|
||||
# Required and unique to this class-
|
||||
with self._unfrozen():
|
||||
self.max_reaction_count: int = max_reaction_count
|
||||
|
||||
self._freeze()
|
|
@ -235,8 +235,9 @@ class ChatMemberAdministrator(ChatMember):
|
|||
.. versionadded:: 20.6
|
||||
.. versionchanged:: 21.0
|
||||
|non_optional_story_argument|
|
||||
can_edit_stories (:obj:`bool`): :obj:`True`, if the administrator can edit
|
||||
stories posted by other users.
|
||||
can_edit_stories (:obj:`bool`): :obj:`True`, if the administrator can edit stories posted
|
||||
by other users, post stories to the chat page, pin chat stories, and access the chat's
|
||||
story archive
|
||||
|
||||
.. versionadded:: 20.6
|
||||
.. versionchanged:: 21.0
|
||||
|
@ -294,8 +295,9 @@ class ChatMemberAdministrator(ChatMember):
|
|||
.. versionadded:: 20.6
|
||||
.. versionchanged:: 21.0
|
||||
|non_optional_story_argument|
|
||||
can_edit_stories (:obj:`bool`): :obj:`True`, if the administrator can edit
|
||||
stories posted by other users.
|
||||
can_edit_stories (:obj:`bool`): :obj:`True`, if the administrator can edit stories posted
|
||||
by other users, post stories to the chat page, pin chat stories, and access the chat's
|
||||
story archive
|
||||
|
||||
.. versionadded:: 20.6
|
||||
.. versionchanged:: 21.0
|
||||
|
|
|
@ -63,6 +63,11 @@ class ChatMemberUpdated(TelegramObject):
|
|||
chat via a chat folder invite link
|
||||
|
||||
.. versionadded:: 20.3
|
||||
via_join_request (:obj:`bool`, optional): :obj:`True`, if the user joined the chat after
|
||||
sending a direct join request without using an invite link and being approved by
|
||||
an administrator
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Attributes:
|
||||
chat (:class:`telegram.Chat`): Chat the user belongs to.
|
||||
|
@ -80,6 +85,11 @@ class ChatMemberUpdated(TelegramObject):
|
|||
chat via a chat folder invite link
|
||||
|
||||
.. versionadded:: 20.3
|
||||
via_join_request (:obj:`bool`): Optional. :obj:`True`, if the user joined the chat after
|
||||
sending a direct join request without using an invite link and being approved
|
||||
by an administrator
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
"""
|
||||
|
||||
|
@ -91,6 +101,7 @@ class ChatMemberUpdated(TelegramObject):
|
|||
"new_chat_member",
|
||||
"old_chat_member",
|
||||
"via_chat_folder_invite_link",
|
||||
"via_join_request",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
|
@ -102,6 +113,7 @@ class ChatMemberUpdated(TelegramObject):
|
|||
new_chat_member: ChatMember,
|
||||
invite_link: Optional[ChatInviteLink] = None,
|
||||
via_chat_folder_invite_link: Optional[bool] = None,
|
||||
via_join_request: Optional[bool] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
|
@ -116,6 +128,7 @@ class ChatMemberUpdated(TelegramObject):
|
|||
|
||||
# Optionals
|
||||
self.invite_link: Optional[ChatInviteLink] = invite_link
|
||||
self.via_join_request: Optional[bool] = via_join_request
|
||||
|
||||
self._id_attrs = (
|
||||
self.chat,
|
||||
|
|
|
@ -30,7 +30,8 @@ class ForceReply(TelegramObject):
|
|||
Upon receiving a message with this object, Telegram clients will display a reply interface to
|
||||
the user (act as if the user has selected the bot's message and tapped 'Reply'). This can be
|
||||
extremely useful if you want to create user-friendly step-by-step interfaces without having
|
||||
to sacrifice privacy mode.
|
||||
to sacrifice `privacy mode <https://core.telegram.org/bots/features#privacy-mode>`_. Not
|
||||
supported in channels and for messages sent on behalf of a Telegram Business account.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`selective` is equal.
|
||||
|
|
|
@ -91,6 +91,7 @@ class InlineKeyboardButton(TelegramObject):
|
|||
to the bot when button is pressed, UTF-8
|
||||
:tg-const:`telegram.InlineKeyboardButton.MIN_CALLBACK_DATA`-
|
||||
:tg-const:`telegram.InlineKeyboardButton.MAX_CALLBACK_DATA` bytes.
|
||||
Not supported for messages sent on behalf of a Telegram Business account.
|
||||
If the bot instance allows arbitrary callback data, anything can be passed.
|
||||
|
||||
Tip:
|
||||
|
@ -102,25 +103,25 @@ class InlineKeyboardButton(TelegramObject):
|
|||
<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.
|
||||
private chats between a user and the bot. Not supported for messages sent on behalf of
|
||||
a Telegram Business account.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
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
|
||||
username will be inserted. This offers an easy way for users to start using your bot
|
||||
in inline mode when they are currently in a private chat with it. Especially useful
|
||||
when combined with ``switch_pm*`` actions - in this case the user will be automatically
|
||||
returned to the chat they switched from, skipping the chat selection screen.
|
||||
switch_inline_query (:obj:`str`, optional): If set, pressing the button will insert the
|
||||
bot's username and the specified inline query in the current chat's input field. May be
|
||||
empty, in which case only the bot's username will be inserted.
|
||||
|
||||
This offers a quick way for the user to open your bot in inline mode in the same chat -
|
||||
good for selecting something from multiple options. Not supported in channels and for
|
||||
messages sent on behalf of a Telegram Business account.
|
||||
|
||||
Tip:
|
||||
This is similar to the new parameter :paramref:`switch_inline_query_chosen_chat`,
|
||||
but gives no control over which chats can be selected.
|
||||
switch_inline_query_current_chat (:obj:`str`, optional): If set, pressing the button will
|
||||
insert the bot's username and the specified inline query in the current chat's input
|
||||
field. Can be empty, in which case only the bot's username will be inserted. This
|
||||
offers a quick way for the user to open your bot in inline mode in the same chat - good
|
||||
for selecting something from multiple options.
|
||||
prompt the user to select one of their chats of the specified type, open that chat and
|
||||
insert the bot's username and the specified inline query in the input field. Not
|
||||
supported for messages sent on behalf of a Telegram Business account.
|
||||
callback_game (:class:`telegram.CallbackGame`, optional): Description of the game that will
|
||||
be launched when the user presses the button. This type of button **must** always be
|
||||
the **first** button in the first row.
|
||||
|
@ -130,7 +131,8 @@ class InlineKeyboardButton(TelegramObject):
|
|||
switch_inline_query_chosen_chat (:obj:`telegram.SwitchInlineQueryChosenChat`, optional):
|
||||
If set, pressing the button will prompt the user to select one of their chats of the
|
||||
specified type, open that chat and insert the bot's username and the specified inline
|
||||
query in the input field.
|
||||
query in the input field. Not supported for messages sent on behalf of a Telegram
|
||||
Business account.
|
||||
|
||||
.. versionadded:: 20.3
|
||||
|
||||
|
@ -159,29 +161,30 @@ class InlineKeyboardButton(TelegramObject):
|
|||
to the bot when button is pressed, UTF-8
|
||||
:tg-const:`telegram.InlineKeyboardButton.MIN_CALLBACK_DATA`-
|
||||
:tg-const:`telegram.InlineKeyboardButton.MAX_CALLBACK_DATA` bytes.
|
||||
Not supported for messages sent on behalf of a Telegram Business account.
|
||||
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.
|
||||
private chats between a user and the bot. Not supported for messages sent on behalf of
|
||||
a Telegram Business account.
|
||||
|
||||
.. versionadded:: 20.0
|
||||
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
|
||||
username will be inserted. This offers an easy way for users to start using your bot
|
||||
in inline mode when they are currently in a private chat with it. Especially useful
|
||||
when combined with ``switch_pm*`` actions - in this case the user will be automatically
|
||||
returned to the chat they switched from, skipping the chat selection screen.
|
||||
switch_inline_query (:obj:`str`): Optional. If set, pressing the button will insert the
|
||||
bot's username and the specified inline query in the current chat's input field. May be
|
||||
empty, in which case only the bot's username will be inserted.
|
||||
|
||||
This offers a quick way for the user to open your bot in inline mode in the same chat -
|
||||
good for selecting something from multiple options. Not supported in channels and for
|
||||
messages sent on behalf of a Telegram Business account.
|
||||
|
||||
Tip:
|
||||
This is similar to the new parameter :paramref:`switch_inline_query_chosen_chat`,
|
||||
but gives no control over which chats can be selected.
|
||||
switch_inline_query_current_chat (:obj:`str`): Optional. If set, pressing the button will
|
||||
insert the bot's username and the specified inline query in the current chat's input
|
||||
field. Can be empty, in which case only the bot's username will be inserted. This
|
||||
offers a quick way for the user to open your bot in inline mode in the same chat - good
|
||||
for selecting something from multiple options.
|
||||
prompt the user to select one of their chats of the specified type, open that chat and
|
||||
insert the bot's username and the specified inline query in the input field. Not
|
||||
supported for messages sent on behalf of a Telegram Business account.
|
||||
callback_game (:class:`telegram.CallbackGame`): Optional. Description of the game that will
|
||||
be launched when the user presses the button. This type of button **must** always be
|
||||
the **first** button in the first row.
|
||||
|
@ -191,7 +194,8 @@ class InlineKeyboardButton(TelegramObject):
|
|||
switch_inline_query_chosen_chat (:obj:`telegram.SwitchInlineQueryChosenChat`): Optional.
|
||||
If set, pressing the button will prompt the user to select one of their chats of the
|
||||
specified type, open that chat and insert the bot's username and the specified inline
|
||||
query in the input field.
|
||||
query in the input field. Not supported for messages sent on behalf of a Telegram
|
||||
Business account.
|
||||
|
||||
.. versionadded:: 20.3
|
||||
|
||||
|
|
|
@ -89,7 +89,9 @@ class InlineQueryResultLocation(InlineQueryResult):
|
|||
live_period (:obj:`int`): Optional. Period in seconds for which the location will be
|
||||
updated, should be between
|
||||
:tg-const:`telegram.InlineQueryResultLocation.MIN_LIVE_PERIOD` and
|
||||
:tg-const:`telegram.InlineQueryResultLocation.MAX_LIVE_PERIOD`.
|
||||
:tg-const:`telegram.InlineQueryResultLocation.MAX_LIVE_PERIOD` or
|
||||
:tg-const:`telegram.constants.LocationLimit.LIVE_PERIOD_FOREVER` for live
|
||||
locations that can be edited indefinitely.
|
||||
heading (:obj:`int`): Optional. For live locations, a direction in which the user is
|
||||
moving, in degrees. Must be between
|
||||
:tg-const:`telegram.InlineQueryResultLocation.MIN_HEADING` and
|
||||
|
|
|
@ -42,7 +42,9 @@ class InputLocationMessageContent(InputMessageContent):
|
|||
live_period (:obj:`int`, optional): Period in seconds for which the location will be
|
||||
updated, should be between
|
||||
:tg-const:`telegram.InputLocationMessageContent.MIN_LIVE_PERIOD` and
|
||||
:tg-const:`telegram.InputLocationMessageContent.MAX_LIVE_PERIOD`.
|
||||
:tg-const:`telegram.InputLocationMessageContent.MAX_LIVE_PERIOD` or
|
||||
:tg-const:`telegram.constants.LocationLimit.LIVE_PERIOD_FOREVER` for live
|
||||
locations that can be edited indefinitely.
|
||||
heading (:obj:`int`, optional): For live locations, a direction in which the user is
|
||||
moving, in degrees. Must be between
|
||||
:tg-const:`telegram.InputLocationMessageContent.MIN_HEADING` and
|
||||
|
|
|
@ -25,6 +25,7 @@ from html import escape
|
|||
from typing import TYPE_CHECKING, Dict, List, Optional, Sequence, Tuple, TypedDict, Union
|
||||
|
||||
from telegram._chat import Chat
|
||||
from telegram._chatbackground import ChatBackground
|
||||
from telegram._chatboost import ChatBoostAdded
|
||||
from telegram._dice import Dice
|
||||
from telegram._files.animation import Animation
|
||||
|
@ -64,6 +65,7 @@ from telegram._user import User
|
|||
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, DefaultValue
|
||||
from telegram._utils.entities import parse_message_entities, parse_message_entity
|
||||
from telegram._utils.types import (
|
||||
CorrectOptionID,
|
||||
FileInput,
|
||||
|
@ -99,6 +101,7 @@ if TYPE_CHECKING:
|
|||
InputMediaDocument,
|
||||
InputMediaPhoto,
|
||||
InputMediaVideo,
|
||||
InputPollOption,
|
||||
LabeledPrice,
|
||||
MessageId,
|
||||
MessageOrigin,
|
||||
|
@ -553,6 +556,11 @@ class Message(MaybeInaccessibleMessage):
|
|||
|
||||
.. versionadded:: 21.1
|
||||
|
||||
chat_background_set (:obj:`telegram.ChatBackground`, optional): Service message: chat
|
||||
background set.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Attributes:
|
||||
message_id (:obj:`int`): Unique message identifier inside this chat.
|
||||
from_user (:class:`telegram.User`): Optional. Sender of the message; empty for messages
|
||||
|
@ -853,6 +861,11 @@ class Message(MaybeInaccessibleMessage):
|
|||
|
||||
.. versionadded:: 21.1
|
||||
|
||||
chat_background_set (:obj:`telegram.ChatBackground`): Optional. Service message: chat
|
||||
background set
|
||||
|
||||
.. versionadded:: Next.Version
|
||||
|
||||
.. |custom_emoji_no_md1_support| replace:: Since custom emoji entities are not supported by
|
||||
:attr:`~telegram.constants.ParseMode.MARKDOWN`, this method now raises a
|
||||
:exc:`ValueError` when encountering a custom emoji.
|
||||
|
@ -876,6 +889,7 @@ class Message(MaybeInaccessibleMessage):
|
|||
"caption",
|
||||
"caption_entities",
|
||||
"channel_chat_created",
|
||||
"chat_background_set",
|
||||
"chat_shared",
|
||||
"connected_website",
|
||||
"contact",
|
||||
|
@ -1029,6 +1043,7 @@ class Message(MaybeInaccessibleMessage):
|
|||
business_connection_id: Optional[str] = None,
|
||||
sender_business_bot: Optional[User] = None,
|
||||
is_from_offline: Optional[bool] = None,
|
||||
chat_background_set: Optional[ChatBackground] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
|
@ -1127,6 +1142,7 @@ class Message(MaybeInaccessibleMessage):
|
|||
self.business_connection_id: Optional[str] = business_connection_id
|
||||
self.sender_business_bot: Optional[User] = sender_business_bot
|
||||
self.is_from_offline: Optional[bool] = is_from_offline
|
||||
self.chat_background_set: Optional[ChatBackground] = chat_background_set
|
||||
|
||||
self._effective_attachment = DEFAULT_NONE
|
||||
|
||||
|
@ -1241,6 +1257,7 @@ class Message(MaybeInaccessibleMessage):
|
|||
)
|
||||
data["users_shared"] = UsersShared.de_json(data.get("users_shared"), bot)
|
||||
data["chat_shared"] = ChatShared.de_json(data.get("chat_shared"), bot)
|
||||
data["chat_background_set"] = ChatBackground.de_json(data.get("chat_background_set"), bot)
|
||||
|
||||
# Unfortunately, this needs to be here due to cyclic imports
|
||||
from telegram._giveaway import ( # pylint: disable=import-outside-toplevel
|
||||
|
@ -2890,7 +2907,7 @@ class Message(MaybeInaccessibleMessage):
|
|||
async def reply_poll(
|
||||
self,
|
||||
question: str,
|
||||
options: Sequence[str],
|
||||
options: Sequence[Union[str, "InputPollOption"]],
|
||||
is_anonymous: Optional[bool] = None,
|
||||
type: Optional[str] = None, # pylint: disable=redefined-builtin
|
||||
allows_multiple_answers: Optional[bool] = None,
|
||||
|
@ -2906,6 +2923,8 @@ class Message(MaybeInaccessibleMessage):
|
|||
protect_content: ODVInput[bool] = DEFAULT_NONE,
|
||||
message_thread_id: ODVInput[int] = DEFAULT_NONE,
|
||||
reply_parameters: Optional["ReplyParameters"] = None,
|
||||
question_parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
question_entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
*,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
|
@ -2976,6 +2995,8 @@ class Message(MaybeInaccessibleMessage):
|
|||
protect_content=protect_content,
|
||||
message_thread_id=message_thread_id,
|
||||
business_connection_id=self.business_connection_id,
|
||||
question_parse_mode=question_parse_mode,
|
||||
question_entities=question_entities,
|
||||
)
|
||||
|
||||
async def reply_dice(
|
||||
|
@ -3653,6 +3674,7 @@ class Message(MaybeInaccessibleMessage):
|
|||
horizontal_accuracy: Optional[float] = None,
|
||||
heading: Optional[int] = None,
|
||||
proximity_alert_radius: Optional[int] = None,
|
||||
live_period: Optional[int] = None,
|
||||
*,
|
||||
location: Optional[Location] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
|
@ -3694,6 +3716,7 @@ class Message(MaybeInaccessibleMessage):
|
|||
horizontal_accuracy=horizontal_accuracy,
|
||||
heading=heading,
|
||||
proximity_alert_radius=proximity_alert_radius,
|
||||
live_period=live_period,
|
||||
inline_message_id=None,
|
||||
)
|
||||
|
||||
|
@ -4184,9 +4207,7 @@ class Message(MaybeInaccessibleMessage):
|
|||
if not self.text:
|
||||
raise RuntimeError("This Message has no 'text'.")
|
||||
|
||||
entity_text = self.text.encode("utf-16-le")
|
||||
entity_text = entity_text[entity.offset * 2 : (entity.offset + entity.length) * 2]
|
||||
return entity_text.decode("utf-16-le")
|
||||
return parse_message_entity(self.text, entity)
|
||||
|
||||
def parse_caption_entity(self, entity: MessageEntity) -> str:
|
||||
"""Returns the text from a given :class:`telegram.MessageEntity`.
|
||||
|
@ -4210,9 +4231,7 @@ class Message(MaybeInaccessibleMessage):
|
|||
if not self.caption:
|
||||
raise RuntimeError("This Message has no 'caption'.")
|
||||
|
||||
entity_text = self.caption.encode("utf-16-le")
|
||||
entity_text = entity_text[entity.offset * 2 : (entity.offset + entity.length) * 2]
|
||||
return entity_text.decode("utf-16-le")
|
||||
return parse_message_entity(self.caption, entity)
|
||||
|
||||
def parse_entities(self, types: Optional[List[str]] = None) -> Dict[MessageEntity, str]:
|
||||
"""
|
||||
|
@ -4237,12 +4256,7 @@ class Message(MaybeInaccessibleMessage):
|
|||
the text that belongs to them, calculated based on UTF-16 codepoints.
|
||||
|
||||
"""
|
||||
if types is None:
|
||||
types = MessageEntity.ALL_TYPES
|
||||
|
||||
return {
|
||||
entity: self.parse_entity(entity) for entity in self.entities if entity.type in types
|
||||
}
|
||||
return parse_message_entities(self.text, self.entities, types=types)
|
||||
|
||||
def parse_caption_entities(
|
||||
self, types: Optional[List[str]] = None
|
||||
|
@ -4269,14 +4283,7 @@ class Message(MaybeInaccessibleMessage):
|
|||
the text that belongs to them, calculated based on UTF-16 codepoints.
|
||||
|
||||
"""
|
||||
if types is None:
|
||||
types = MessageEntity.ALL_TYPES
|
||||
|
||||
return {
|
||||
entity: self.parse_caption_entity(entity)
|
||||
for entity in self.caption_entities
|
||||
if entity.type in types
|
||||
}
|
||||
return parse_message_entities(self.caption, self.caption_entities, types=types)
|
||||
|
||||
@classmethod
|
||||
def _parse_html(
|
||||
|
|
|
@ -28,12 +28,80 @@ from telegram._user import User
|
|||
from telegram._utils import enum
|
||||
from telegram._utils.argumentparsing import parse_sequence_arg
|
||||
from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp
|
||||
from telegram._utils.types import JSONDict
|
||||
from telegram._utils.defaultvalue import DEFAULT_NONE
|
||||
from telegram._utils.entities import parse_message_entities, parse_message_entity
|
||||
from telegram._utils.types import JSONDict, ODVInput
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Bot
|
||||
|
||||
|
||||
class InputPollOption(TelegramObject):
|
||||
"""
|
||||
This object contains information about one answer option in a poll to send.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`text` is equal.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Args:
|
||||
text (:obj:`str`): Option text,
|
||||
:tg-const:`telegram.PollOption.MIN_LENGTH`-:tg-const:`telegram.PollOption.MAX_LENGTH`
|
||||
characters.
|
||||
text_parse_mode (:obj:`str`, optional): |parse_mode|
|
||||
Currently, only custom emoji entities are allowed.
|
||||
text_entities (Sequence[:class:`telegram.MessageEntity`], optional): Special entities
|
||||
that appear in the option :paramref:`text`. It can be specified instead of
|
||||
:paramref:`text_parse_mode`.
|
||||
Currently, only custom emoji entities are allowed.
|
||||
This list is empty if the text does not contain entities.
|
||||
|
||||
Attributes:
|
||||
text (:obj:`str`): Option text,
|
||||
:tg-const:`telegram.PollOption.MIN_LENGTH`-:tg-const:`telegram.PollOption.MAX_LENGTH`
|
||||
characters.
|
||||
text_parse_mode (:obj:`str`): Optional. |parse_mode|
|
||||
Currently, only custom emoji entities are allowed.
|
||||
text_entities (Sequence[:class:`telegram.MessageEntity`]): Special entities
|
||||
that appear in the option :paramref:`text`. It can be specified instead of
|
||||
:paramref:`text_parse_mode`.
|
||||
Currently, only custom emoji entities are allowed.
|
||||
This list is empty if the text does not contain entities.
|
||||
"""
|
||||
|
||||
__slots__ = ("text", "text_entities", "text_parse_mode")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
text: str,
|
||||
text_parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
text_entities: Optional[Sequence[MessageEntity]] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
self.text: str = text
|
||||
self.text_parse_mode: ODVInput[str] = text_parse_mode
|
||||
self.text_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(text_entities)
|
||||
|
||||
self._id_attrs = (self.text,)
|
||||
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["InputPollOption"]:
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["text_entities"] = MessageEntity.de_list(data.get("text_entities"), bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
|
||||
class PollOption(TelegramObject):
|
||||
"""
|
||||
This object contains information about one answer option in a poll.
|
||||
|
@ -46,26 +114,101 @@ class PollOption(TelegramObject):
|
|||
:tg-const:`telegram.PollOption.MIN_LENGTH`-:tg-const:`telegram.PollOption.MAX_LENGTH`
|
||||
characters.
|
||||
voter_count (:obj:`int`): Number of users that voted for this option.
|
||||
text_entities (Sequence[:class:`telegram.MessageEntity`], optional): Special entities
|
||||
that appear in the option text. Currently, only custom emoji entities are allowed in
|
||||
poll option texts.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Attributes:
|
||||
text (:obj:`str`): Option text,
|
||||
:tg-const:`telegram.PollOption.MIN_LENGTH`-:tg-const:`telegram.PollOption.MAX_LENGTH`
|
||||
characters.
|
||||
voter_count (:obj:`int`): Number of users that voted for this option.
|
||||
text_entities (Tuple[:class:`telegram.MessageEntity`]): Special entities
|
||||
that appear in the option text. Currently, only custom emoji entities are allowed in
|
||||
poll option texts.
|
||||
This list is empty if the question does not contain entities.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ("text", "voter_count")
|
||||
__slots__ = ("text", "text_entities", "voter_count")
|
||||
|
||||
def __init__(self, text: str, voter_count: int, *, api_kwargs: Optional[JSONDict] = None):
|
||||
def __init__(
|
||||
self,
|
||||
text: str,
|
||||
voter_count: int,
|
||||
text_entities: Optional[Sequence[MessageEntity]] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
self.text: str = text
|
||||
self.voter_count: int = voter_count
|
||||
self.text_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(text_entities)
|
||||
|
||||
self._id_attrs = (self.text, self.voter_count)
|
||||
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["PollOption"]:
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
||||
data["text_entities"] = MessageEntity.de_list(data.get("text_entities"), bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
def parse_entity(self, entity: MessageEntity) -> str:
|
||||
"""Returns the text in :attr:`text`
|
||||
from a given :class:`telegram.MessageEntity` of :attr:`text_entities`.
|
||||
|
||||
Note:
|
||||
This method is present because Telegram calculates the offset and length in
|
||||
UTF-16 codepoint pairs, which some versions of Python don't handle automatically.
|
||||
(That is, you can't just slice ``Message.text`` with the offset and length.)
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Args:
|
||||
entity (:class:`telegram.MessageEntity`): The entity to extract the text from. It must
|
||||
be an entity that belongs to :attr:`text_entities`.
|
||||
|
||||
Returns:
|
||||
:obj:`str`: The text of the given entity.
|
||||
"""
|
||||
return parse_message_entity(self.text, entity)
|
||||
|
||||
def parse_entities(self, types: Optional[List[str]] = None) -> Dict[MessageEntity, str]:
|
||||
"""
|
||||
Returns a :obj:`dict` that maps :class:`telegram.MessageEntity` to :obj:`str`.
|
||||
It contains entities from this polls question filtered by their ``type`` attribute as
|
||||
the key, and the text that each entity belongs to as the value of the :obj:`dict`.
|
||||
|
||||
Note:
|
||||
This method should always be used instead of the :attr:`text_entities`
|
||||
attribute, since it calculates the correct substring from the message text based on
|
||||
UTF-16 codepoints. See :attr:`parse_entity` for more info.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Args:
|
||||
types (List[:obj:`str`], optional): List of ``MessageEntity`` types as strings. If the
|
||||
``type`` attribute of an entity is contained in this list, it will be returned.
|
||||
Defaults to :attr:`telegram.MessageEntity.ALL_TYPES`.
|
||||
|
||||
Returns:
|
||||
Dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to
|
||||
the text that belongs to them, calculated based on UTF-16 codepoints.
|
||||
"""
|
||||
return parse_message_entities(self.text, self.text_entities, types)
|
||||
|
||||
MIN_LENGTH: Final[int] = constants.PollLimit.MIN_OPTION_LENGTH
|
||||
""":const:`telegram.constants.PollLimit.MIN_OPTION_LENGTH`
|
||||
|
||||
|
@ -215,6 +358,11 @@ class Poll(TelegramObject):
|
|||
|
||||
.. versionchanged:: 20.3
|
||||
|datetime_localization|
|
||||
question_entities (Sequence[:class:`telegram.MessageEntity`], optional): Special entities
|
||||
that appear in the :attr:`question`. Currently, only custom emoji entities are allowed
|
||||
in poll questions.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Attributes:
|
||||
id (:obj:`str`): Unique poll identifier.
|
||||
|
@ -251,6 +399,12 @@ class Poll(TelegramObject):
|
|||
|
||||
.. versionchanged:: 20.3
|
||||
|datetime_localization|
|
||||
question_entities (Tuple[:class:`telegram.MessageEntity`]): Special entities
|
||||
that appear in the :attr:`question`. Currently, only custom emoji entities are allowed
|
||||
in poll questions.
|
||||
This list is empty if the question does not contain entities.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
"""
|
||||
|
||||
|
@ -266,6 +420,7 @@ class Poll(TelegramObject):
|
|||
"open_period",
|
||||
"options",
|
||||
"question",
|
||||
"question_entities",
|
||||
"total_voter_count",
|
||||
"type",
|
||||
)
|
||||
|
@ -285,6 +440,7 @@ class Poll(TelegramObject):
|
|||
explanation_entities: Optional[Sequence[MessageEntity]] = None,
|
||||
open_period: Optional[int] = None,
|
||||
close_date: Optional[datetime.datetime] = None,
|
||||
question_entities: Optional[Sequence[MessageEntity]] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
|
@ -304,6 +460,7 @@ class Poll(TelegramObject):
|
|||
)
|
||||
self.open_period: Optional[int] = open_period
|
||||
self.close_date: Optional[datetime.datetime] = close_date
|
||||
self.question_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(question_entities)
|
||||
|
||||
self._id_attrs = (self.id,)
|
||||
|
||||
|
@ -323,11 +480,13 @@ class Poll(TelegramObject):
|
|||
data["options"] = [PollOption.de_json(option, bot) for option in data["options"]]
|
||||
data["explanation_entities"] = MessageEntity.de_list(data.get("explanation_entities"), bot)
|
||||
data["close_date"] = from_timestamp(data.get("close_date"), tzinfo=loc_tzinfo)
|
||||
data["question_entities"] = MessageEntity.de_list(data.get("question_entities"), bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
def parse_explanation_entity(self, entity: MessageEntity) -> str:
|
||||
"""Returns the text from a given :class:`telegram.MessageEntity`.
|
||||
"""Returns the text in :attr:`explanation` from a given :class:`telegram.MessageEntity` of
|
||||
:attr:`explanation_entities`.
|
||||
|
||||
Note:
|
||||
This method is present because Telegram calculates the offset and length in
|
||||
|
@ -336,7 +495,7 @@ class Poll(TelegramObject):
|
|||
|
||||
Args:
|
||||
entity (:class:`telegram.MessageEntity`): The entity to extract the text from. It must
|
||||
be an entity that belongs to this message.
|
||||
be an entity that belongs to :attr:`explanation_entities`.
|
||||
|
||||
Returns:
|
||||
:obj:`str`: The text of the given entity.
|
||||
|
@ -348,10 +507,7 @@ class Poll(TelegramObject):
|
|||
if not self.explanation:
|
||||
raise RuntimeError("This Poll has no 'explanation'.")
|
||||
|
||||
entity_text = self.explanation.encode("utf-16-le")
|
||||
entity_text = entity_text[entity.offset * 2 : (entity.offset + entity.length) * 2]
|
||||
|
||||
return entity_text.decode("utf-16-le")
|
||||
return parse_message_entity(self.explanation, entity)
|
||||
|
||||
def parse_explanation_entities(
|
||||
self, types: Optional[List[str]] = None
|
||||
|
@ -375,15 +531,61 @@ class Poll(TelegramObject):
|
|||
Dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to
|
||||
the text that belongs to them, calculated based on UTF-16 codepoints.
|
||||
|
||||
"""
|
||||
if types is None:
|
||||
types = MessageEntity.ALL_TYPES
|
||||
Raises:
|
||||
RuntimeError: If the poll has no explanation.
|
||||
|
||||
return {
|
||||
entity: self.parse_explanation_entity(entity)
|
||||
for entity in self.explanation_entities
|
||||
if entity.type in types
|
||||
}
|
||||
"""
|
||||
if not self.explanation:
|
||||
raise RuntimeError("This Poll has no 'explanation'.")
|
||||
|
||||
return parse_message_entities(self.explanation, self.explanation_entities, types)
|
||||
|
||||
def parse_question_entity(self, entity: MessageEntity) -> str:
|
||||
"""Returns the text in :attr:`question` from a given :class:`telegram.MessageEntity` of
|
||||
:attr:`question_entities`.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Note:
|
||||
This method is present because Telegram calculates the offset and length in
|
||||
UTF-16 codepoint pairs, which some versions of Python don't handle automatically.
|
||||
(That is, you can't just slice ``Message.text`` with the offset and length.)
|
||||
|
||||
Args:
|
||||
entity (:class:`telegram.MessageEntity`): The entity to extract the text from. It must
|
||||
be an entity that belongs to :attr:`question_entities`.
|
||||
|
||||
Returns:
|
||||
:obj:`str`: The text of the given entity.
|
||||
"""
|
||||
return parse_message_entity(self.question, entity)
|
||||
|
||||
def parse_question_entities(
|
||||
self, types: Optional[List[str]] = None
|
||||
) -> Dict[MessageEntity, str]:
|
||||
"""
|
||||
Returns a :obj:`dict` that maps :class:`telegram.MessageEntity` to :obj:`str`.
|
||||
It contains entities from this polls question filtered by their ``type`` attribute as
|
||||
the key, and the text that each entity belongs to as the value of the :obj:`dict`.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
|
||||
Note:
|
||||
This method should always be used instead of the :attr:`question_entities`
|
||||
attribute, since it calculates the correct substring from the message text based on
|
||||
UTF-16 codepoints. See :attr:`parse_question_entity` for more info.
|
||||
|
||||
Args:
|
||||
types (List[:obj:`str`], optional): List of ``MessageEntity`` types as strings. If the
|
||||
``type`` attribute of an entity is contained in this list, it will be returned.
|
||||
Defaults to :attr:`telegram.MessageEntity.ALL_TYPES`.
|
||||
|
||||
Returns:
|
||||
Dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to
|
||||
the text that belongs to them, calculated based on UTF-16 codepoints.
|
||||
|
||||
"""
|
||||
return parse_message_entities(self.question, self.question_entities, types)
|
||||
|
||||
REGULAR: Final[str] = constants.PollType.REGULAR
|
||||
""":const:`telegram.constants.PollType.REGULAR`"""
|
||||
|
|
|
@ -355,6 +355,7 @@ class ReplyParameters(TelegramObject):
|
|||
chat, or in the chat :paramref:`chat_id` if it is specified.
|
||||
chat_id (:obj:`int` | :obj:`str`, optional): If the message to be replied to is from a
|
||||
different chat, |chat_id_channel|
|
||||
Not supported for messages sent on behalf of a business account.
|
||||
allow_sending_without_reply (:obj:`bool`, optional): |allow_sending_without_reply| Can be
|
||||
used only for replies in the same chat and forum topic.
|
||||
quote (:obj:`str`, optional): Quoted part of the message to be replied to; 0-1024
|
||||
|
@ -376,6 +377,7 @@ class ReplyParameters(TelegramObject):
|
|||
chat, or in the chat :paramref:`chat_id` if it is specified.
|
||||
chat_id (:obj:`int` | :obj:`str`): Optional. If the message to be replied to is from a
|
||||
different chat, |chat_id_channel|
|
||||
Not supported for messages sent on behalf of a business account.
|
||||
allow_sending_without_reply (:obj:`bool`): Optional. |allow_sending_without_reply| Can be
|
||||
used only for replies in the same chat and forum topic.
|
||||
quote (:obj:`str`): Optional. Quoted part of the message to be replied to; 0-1024
|
||||
|
|
|
@ -28,7 +28,8 @@ from telegram._utils.types import JSONDict
|
|||
|
||||
|
||||
class ReplyKeyboardMarkup(TelegramObject):
|
||||
"""This object represents a custom keyboard with reply options.
|
||||
"""This object represents a custom keyboard with reply options. Not supported in channels and
|
||||
for messages sent on behalf of a Telegram Business account.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their size of :attr:`keyboard` and all the buttons are equal.
|
||||
|
|
|
@ -29,6 +29,7 @@ class ReplyKeyboardRemove(TelegramObject):
|
|||
keyboard and display the default letter-keyboard. By default, custom keyboards are displayed
|
||||
until a new keyboard is sent by a bot. An exception is made for one-time keyboards that are
|
||||
hidden immediately after the user presses a button (see :class:`telegram.ReplyKeyboardMarkup`).
|
||||
Not supported in channels and for messages sent on behalf of a Telegram Business account.
|
||||
|
||||
Note:
|
||||
User will not be able to summon this keyboard; if you want to hide the keyboard from
|
||||
|
|
|
@ -40,6 +40,7 @@ if TYPE_CHECKING:
|
|||
InputMediaDocument,
|
||||
InputMediaPhoto,
|
||||
InputMediaVideo,
|
||||
InputPollOption,
|
||||
LabeledPrice,
|
||||
LinkPreviewOptions,
|
||||
Location,
|
||||
|
@ -1482,7 +1483,7 @@ class User(TelegramObject):
|
|||
async def send_poll(
|
||||
self,
|
||||
question: str,
|
||||
options: Sequence[str],
|
||||
options: Sequence[Union[str, "InputPollOption"]],
|
||||
is_anonymous: Optional[bool] = None,
|
||||
type: Optional[str] = None,
|
||||
allows_multiple_answers: Optional[bool] = None,
|
||||
|
@ -1499,6 +1500,8 @@ class User(TelegramObject):
|
|||
message_thread_id: Optional[int] = None,
|
||||
reply_parameters: Optional["ReplyParameters"] = None,
|
||||
business_connection_id: Optional[str] = None,
|
||||
question_parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
question_entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
*,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
|
@ -1548,6 +1551,8 @@ class User(TelegramObject):
|
|||
protect_content=protect_content,
|
||||
message_thread_id=message_thread_id,
|
||||
business_connection_id=business_connection_id,
|
||||
question_parse_mode=question_parse_mode,
|
||||
question_entities=question_entities,
|
||||
)
|
||||
|
||||
async def send_copy(
|
||||
|
|
71
telegram/_utils/entities.py
Normal file
71
telegram/_utils/entities.py
Normal file
|
@ -0,0 +1,71 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2024
|
||||
# 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 auxiliary functionality for parsing MessageEntity objects.
|
||||
|
||||
Warning:
|
||||
Contents of this module are intended to be used internally by the library and *not* by the
|
||||
user. Changes to this module are not considered breaking changes and may not be documented in
|
||||
the changelog.
|
||||
"""
|
||||
from typing import Dict, Optional, Sequence
|
||||
|
||||
from telegram._messageentity import MessageEntity
|
||||
|
||||
|
||||
def parse_message_entity(text: str, entity: MessageEntity) -> str:
|
||||
"""Returns the text from a given :class:`telegram.MessageEntity`.
|
||||
|
||||
Args:
|
||||
text (:obj:`str`): The text to extract the entity from.
|
||||
entity (:class:`telegram.MessageEntity`): The entity to extract the text from.
|
||||
|
||||
Returns:
|
||||
:obj:`str`: The text of the given entity.
|
||||
"""
|
||||
entity_text = text.encode("utf-16-le")
|
||||
entity_text = entity_text[entity.offset * 2 : (entity.offset + entity.length) * 2]
|
||||
|
||||
return entity_text.decode("utf-16-le")
|
||||
|
||||
|
||||
def parse_message_entities(
|
||||
text: str, entities: Sequence[MessageEntity], types: Optional[Sequence[str]] = None
|
||||
) -> Dict[MessageEntity, str]:
|
||||
"""
|
||||
Returns a :obj:`dict` that maps :class:`telegram.MessageEntity` to :obj:`str`.
|
||||
It contains entities filtered by their ``type`` attribute as
|
||||
the key, and the text that each entity belongs to as the value of the :obj:`dict`.
|
||||
|
||||
Args:
|
||||
text (:obj:`str`): The text to extract the entity from.
|
||||
entities (List[:class:`telegram.MessageEntity`]): The entities to extract the text from.
|
||||
types (List[:obj:`str`], optional): List of ``MessageEntity`` types as strings. If the
|
||||
``type`` attribute of an entity is contained in this list, it will be returned.
|
||||
Defaults to :attr:`telegram.MessageEntity.ALL_TYPES`.
|
||||
|
||||
Returns:
|
||||
Dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to
|
||||
the text that belongs to them, calculated based on UTF-16 codepoints.
|
||||
"""
|
||||
if types is None:
|
||||
types = MessageEntity.ALL_TYPES
|
||||
|
||||
return {
|
||||
entity: parse_message_entity(text, entity) for entity in entities if entity.type in types
|
||||
}
|
|
@ -37,6 +37,10 @@ __all__ = [
|
|||
"SUPPORTED_WEBHOOK_PORTS",
|
||||
"ZERO_DATE",
|
||||
"AccentColor",
|
||||
"BackgroundFillLimit",
|
||||
"BackgroundFillType",
|
||||
"BackgroundTypeLimit",
|
||||
"BackgroundTypeType",
|
||||
"BotCommandLimit",
|
||||
"BotCommandScopeType",
|
||||
"BotDescriptionLimit",
|
||||
|
@ -142,7 +146,7 @@ class _AccentColor(NamedTuple):
|
|||
#: :data:`telegram.__bot_api_version_info__`.
|
||||
#:
|
||||
#: .. versionadded:: 20.0
|
||||
BOT_API_VERSION_INFO: Final[_BotAPIVersion] = _BotAPIVersion(major=7, minor=2)
|
||||
BOT_API_VERSION_INFO: Final[_BotAPIVersion] = _BotAPIVersion(major=7, minor=3)
|
||||
#: :obj:`str`: Telegram Bot API
|
||||
#: version supported by this version of `python-telegram-bot`. Also available as
|
||||
#: :data:`telegram.__bot_api_version__`.
|
||||
|
@ -822,6 +826,46 @@ class ChatLimit(IntEnum):
|
|||
"""
|
||||
|
||||
|
||||
class BackgroundTypeLimit(IntEnum):
|
||||
"""This enum contains limitations for :class:`telegram.BackgroundTypeFill`,
|
||||
:class:`telegram.BackgroundTypeWallpaper` and :class:`telegram.BackgroundTypePattern`.
|
||||
The enum members of this enumeration are instances of :class:`int` and can be treated as such.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
MAX_DIMMING = 100
|
||||
""":obj:`int`: Maximum value allowed for:
|
||||
|
||||
* :paramref:`~telegram.BackgroundTypeFill.dark_theme_dimming` parameter of
|
||||
:class:`telegram.BackgroundTypeFill`
|
||||
* :paramref:`~telegram.BackgroundTypeWallpaper.dark_theme_dimming` parameter of
|
||||
:class:`telegram.BackgroundTypeWallpaper`
|
||||
"""
|
||||
MAX_INTENSITY = 100
|
||||
""":obj:`int`: Maximum value allowed for :paramref:`~telegram.BackgroundTypePattern.intensity`
|
||||
parameter of :class:`telegram.BackgroundTypePattern`
|
||||
"""
|
||||
|
||||
|
||||
class BackgroundFillLimit(IntEnum):
|
||||
"""This enum contains limitations for :class:`telegram.BackgroundFillGradient`.
|
||||
The enum members of this enumeration are instances of :class:`int` and can be treated as such.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
MAX_ROTATION_ANGLE = 359
|
||||
""":obj:`int`: Maximum value allowed for:
|
||||
:paramref:`~telegram.BackgroundFillGradient.rotation_angle` parameter of
|
||||
:class:`telegram.BackgroundFillGradient`
|
||||
"""
|
||||
|
||||
|
||||
class ChatMemberStatus(StringEnum):
|
||||
"""This enum contains the available states for :class:`telegram.ChatMember`. The enum
|
||||
members of this enumeration are instances of :class:`str` and can be treated as such.
|
||||
|
@ -1427,6 +1471,21 @@ class LocationLimit(IntEnum):
|
|||
:meth:`telegram.Bot.send_location`
|
||||
"""
|
||||
|
||||
LIVE_PERIOD_FOREVER = int(hex(0x7FFFFFFF), 16)
|
||||
""":obj:`int`: Value for live locations that can be edited indefinitely. Passed in:
|
||||
|
||||
* :paramref:`~telegram.InlineQueryResultLocation.live_period` parameter of
|
||||
:class:`telegram.InlineQueryResultLocation`
|
||||
* :paramref:`~telegram.InputLocationMessageContent.live_period` parameter of
|
||||
:class:`telegram.InputLocationMessageContent`
|
||||
* :paramref:`~telegram.Bot.edit_message_live_location.live_period` parameter of
|
||||
:meth:`telegram.Bot.edit_message_live_location`
|
||||
* :paramref:`~telegram.Bot.send_location.live_period` parameter of
|
||||
:meth:`telegram.Bot.send_location`
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
"""
|
||||
|
||||
MIN_PROXIMITY_ALERT_RADIUS = 1
|
||||
""":obj:`int`: Minimum value allowed for:
|
||||
|
||||
|
@ -1726,6 +1785,11 @@ class MessageType(StringEnum):
|
|||
|
||||
.. versionadded:: 20.8
|
||||
"""
|
||||
CHAT_BACKGROUND_SET = "chat_background_set"
|
||||
""":obj:`str`: Messages with :attr:`telegram.Message.chat_background_set`.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
"""
|
||||
CONNECTED_WEBSITE = "connected_website"
|
||||
""":obj:`str`: Messages with :attr:`telegram.Message.connected_website`."""
|
||||
CONTACT = "contact"
|
||||
|
@ -2878,3 +2942,39 @@ class ReactionEmoji(StringEnum):
|
|||
""":obj:`str`: Woman Shrugging"""
|
||||
POUTING_FACE = "😡"
|
||||
""":obj:`str`: Pouting face"""
|
||||
|
||||
|
||||
class BackgroundTypeType(StringEnum):
|
||||
"""This enum contains the available types of :class:`telegram.BackgroundType`. The enum
|
||||
members of this enumeration are instances of :class:`str` and can be treated as such.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
FILL = "fill"
|
||||
""":obj:`str`: A :class:`telegram.BackgroundType` with fill background."""
|
||||
WALLPAPER = "wallpaper"
|
||||
""":obj:`str`: A :class:`telegram.BackgroundType` with wallpaper background."""
|
||||
PATTERN = "pattern"
|
||||
""":obj:`str`: A :class:`telegram.BackgroundType` with pattern background."""
|
||||
CHAT_THEME = "chat_theme"
|
||||
""":obj:`str`: A :class:`telegram.BackgroundType` with chat_theme background."""
|
||||
|
||||
|
||||
class BackgroundFillType(StringEnum):
|
||||
"""This enum contains the available types of :class:`telegram.BackgroundFill`. The enum
|
||||
members of this enumeration are instances of :class:`str` and can be treated as such.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
SOLID = "solid"
|
||||
""":obj:`str`: A :class:`telegram.BackgroundFill` with solid fill."""
|
||||
GRADIENT = "gradient"
|
||||
""":obj:`str`: A :class:`telegram.BackgroundFill` with gradient fill."""
|
||||
FREEFORM_GRADIENT = "freeform_gradient"
|
||||
""":obj:`str`: A :class:`telegram.BackgroundFill` with freeform_gradient fill."""
|
||||
|
|
|
@ -179,13 +179,14 @@ class Defaults:
|
|||
# Gather all defaults that actually have a default value
|
||||
self._api_defaults = {}
|
||||
for kwarg in (
|
||||
"parse_mode",
|
||||
"explanation_parse_mode",
|
||||
"disable_notification",
|
||||
"allow_sending_without_reply",
|
||||
"protect_content",
|
||||
"link_preview_options",
|
||||
"disable_notification",
|
||||
"do_quote",
|
||||
"explanation_parse_mode",
|
||||
"link_preview_options",
|
||||
"parse_mode",
|
||||
"protect_content",
|
||||
"question_parse_mode",
|
||||
):
|
||||
value = getattr(self, kwarg)
|
||||
if value is not None:
|
||||
|
@ -264,6 +265,36 @@ class Defaults:
|
|||
"You can not assign a new value to quote_parse_mode after initialization."
|
||||
)
|
||||
|
||||
@property
|
||||
def text_parse_mode(self) -> Optional[str]:
|
||||
""":obj:`str`: Optional. Alias for :attr:`parse_mode`, used for
|
||||
the corresponding parameter of :class:`telegram.InputPollOption`.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
"""
|
||||
return self._parse_mode
|
||||
|
||||
@text_parse_mode.setter
|
||||
def text_parse_mode(self, _: object) -> NoReturn:
|
||||
raise AttributeError(
|
||||
"You can not assign a new value to text_parse_mode after initialization."
|
||||
)
|
||||
|
||||
@property
|
||||
def question_parse_mode(self) -> Optional[str]:
|
||||
""":obj:`str`: Optional. Alias for :attr:`parse_mode`, used for
|
||||
the corresponding parameter of :meth:`telegram.Bot.send_poll`.
|
||||
|
||||
.. versionadded:: NEXT.VERSION
|
||||
"""
|
||||
return self._parse_mode
|
||||
|
||||
@question_parse_mode.setter
|
||||
def question_parse_mode(self, _: object) -> NoReturn:
|
||||
raise AttributeError(
|
||||
"You can not assign a new value to question_parse_mode after initialization."
|
||||
)
|
||||
|
||||
@property
|
||||
def disable_notification(self) -> Optional[bool]:
|
||||
""":obj:`bool`: Optional. Sends the message silently. Users will
|
||||
|
|
|
@ -50,8 +50,8 @@ from telegram import (
|
|||
BotShortDescription,
|
||||
BusinessConnection,
|
||||
CallbackQuery,
|
||||
Chat,
|
||||
ChatAdministratorRights,
|
||||
ChatFullInfo,
|
||||
ChatInviteLink,
|
||||
ChatMember,
|
||||
ChatPermissions,
|
||||
|
@ -64,6 +64,7 @@ from telegram import (
|
|||
InlineKeyboardMarkup,
|
||||
InlineQueryResultsButton,
|
||||
InputMedia,
|
||||
InputPollOption,
|
||||
LinkPreviewOptions,
|
||||
Location,
|
||||
MaskPosition,
|
||||
|
@ -113,7 +114,7 @@ if TYPE_CHECKING:
|
|||
)
|
||||
from telegram.ext import BaseRateLimiter, Defaults
|
||||
|
||||
HandledTypes = TypeVar("HandledTypes", bound=Union[Message, CallbackQuery, Chat])
|
||||
HandledTypes = TypeVar("HandledTypes", bound=Union[Message, CallbackQuery, ChatFullInfo])
|
||||
KT = TypeVar("KT", bound=ReplyMarkup)
|
||||
|
||||
|
||||
|
@ -436,6 +437,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
|||
# 3) set the correct parse_mode for all InputMedia objects
|
||||
# 4) handle the LinkPreviewOptions case (see below)
|
||||
# 5) handle the ReplyParameters case (see below)
|
||||
# 6) handle text_parse_mode in InputPollOption
|
||||
for key, val in data.items():
|
||||
# 1)
|
||||
if isinstance(val, DefaultValue):
|
||||
|
@ -487,6 +489,21 @@ class ExtBot(Bot, Generic[RLARGS]):
|
|||
|
||||
data[key] = new_value
|
||||
|
||||
# 6)
|
||||
elif isinstance(val, Sequence) and all(
|
||||
isinstance(obj, InputPollOption) for obj in val
|
||||
):
|
||||
new_val = []
|
||||
for option in val:
|
||||
if not isinstance(option.text_parse_mode, DefaultValue):
|
||||
new_val.append(option)
|
||||
else:
|
||||
new_option = copy(option)
|
||||
with new_option._unfrozen():
|
||||
new_option.text_parse_mode = self.defaults.text_parse_mode
|
||||
new_val.append(new_option)
|
||||
data[key] = new_val
|
||||
|
||||
def _replace_keyboard(self, reply_markup: Optional[KT]) -> Optional[KT]:
|
||||
# If the reply_markup is an inline keyboard and we allow arbitrary callback data, let the
|
||||
# CallbackDataCache build a new keyboard with the data replaced. Otherwise return the input
|
||||
|
@ -554,7 +571,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
|||
self.callback_data_cache.process_message(message=obj)
|
||||
return obj # type: ignore[return-value]
|
||||
|
||||
if isinstance(obj, Chat) and obj.pinned_message:
|
||||
if isinstance(obj, ChatFullInfo) and obj.pinned_message:
|
||||
self.callback_data_cache.process_message(obj.pinned_message)
|
||||
|
||||
return obj
|
||||
|
@ -853,7 +870,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
|||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
rate_limit_args: Optional[RLARGS] = None,
|
||||
) -> Chat:
|
||||
) -> ChatFullInfo:
|
||||
# We override this method to call self._insert_callback_data
|
||||
result = await super().get_chat(
|
||||
chat_id=chat_id,
|
||||
|
@ -1520,6 +1537,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
|||
horizontal_accuracy: Optional[float] = None,
|
||||
heading: Optional[int] = None,
|
||||
proximity_alert_radius: Optional[int] = None,
|
||||
live_period: Optional[int] = None,
|
||||
*,
|
||||
location: Optional[Location] = None,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
|
@ -1539,6 +1557,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
|||
horizontal_accuracy=horizontal_accuracy,
|
||||
heading=heading,
|
||||
proximity_alert_radius=proximity_alert_radius,
|
||||
live_period=live_period,
|
||||
location=location,
|
||||
read_timeout=read_timeout,
|
||||
write_timeout=write_timeout,
|
||||
|
@ -2915,7 +2934,7 @@ class ExtBot(Bot, Generic[RLARGS]):
|
|||
self,
|
||||
chat_id: Union[int, str],
|
||||
question: str,
|
||||
options: Sequence[str],
|
||||
options: Sequence[Union[str, "InputPollOption"]],
|
||||
is_anonymous: Optional[bool] = None,
|
||||
type: Optional[str] = None, # pylint: disable=redefined-builtin
|
||||
allows_multiple_answers: Optional[bool] = None,
|
||||
|
@ -2932,6 +2951,8 @@ class ExtBot(Bot, Generic[RLARGS]):
|
|||
message_thread_id: Optional[int] = None,
|
||||
reply_parameters: Optional["ReplyParameters"] = None,
|
||||
business_connection_id: Optional[str] = None,
|
||||
question_parse_mode: ODVInput[str] = DEFAULT_NONE,
|
||||
question_entities: Optional[Sequence["MessageEntity"]] = None,
|
||||
*,
|
||||
reply_to_message_id: Optional[int] = None,
|
||||
allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE,
|
||||
|
@ -2969,6 +2990,8 @@ class ExtBot(Bot, Generic[RLARGS]):
|
|||
connect_timeout=connect_timeout,
|
||||
pool_timeout=pool_timeout,
|
||||
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
|
||||
question_parse_mode=question_parse_mode,
|
||||
question_entities=question_entities,
|
||||
)
|
||||
|
||||
async def send_sticker(
|
||||
|
|
|
@ -1909,7 +1909,8 @@ class StatusUpdate:
|
|||
def filter(self, update: Update) -> bool:
|
||||
return bool(
|
||||
# keep this alphabetically sorted for easier maintenance
|
||||
StatusUpdate.CHAT_CREATED.check_update(update)
|
||||
StatusUpdate.CHAT_BACKGROUND_SET.check_update(update)
|
||||
or StatusUpdate.CHAT_CREATED.check_update(update)
|
||||
or StatusUpdate.CHAT_SHARED.check_update(update)
|
||||
or StatusUpdate.CONNECTED_WEBSITE.check_update(update)
|
||||
or StatusUpdate.DELETE_CHAT_PHOTO.check_update(update)
|
||||
|
@ -1942,6 +1943,15 @@ class StatusUpdate:
|
|||
ALL = _All(name="filters.StatusUpdate.ALL")
|
||||
"""Messages that contain any of the below."""
|
||||
|
||||
class _ChatBackgroundSet(MessageFilter):
|
||||
__slots__ = ()
|
||||
|
||||
def filter(self, message: Message) -> bool:
|
||||
return bool(message.chat_background_set)
|
||||
|
||||
CHAT_BACKGROUND_SET = _ChatBackgroundSet(name="filters.StatusUpdate.CHAT_BACKGROUND_SET")
|
||||
"""Messages that contain :attr:`telegram.Message.chat_background_set`."""
|
||||
|
||||
class _ChatCreated(MessageFilter):
|
||||
__slots__ = ()
|
||||
|
||||
|
|
|
@ -124,7 +124,8 @@ class TestLocationWithoutRequest(TestLocationBase):
|
|||
ha = data["horizontal_accuracy"] == "50"
|
||||
heading = data["heading"] == "90"
|
||||
prox_alert = data["proximity_alert_radius"] == "1000"
|
||||
return lat and lon and id_ and ha and heading and prox_alert
|
||||
live = data["live_period"] == "900"
|
||||
return lat and lon and id_ and ha and heading and prox_alert and live
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
assert await bot.edit_message_live_location(
|
||||
|
@ -133,6 +134,7 @@ class TestLocationWithoutRequest(TestLocationBase):
|
|||
horizontal_accuracy=50,
|
||||
heading=90,
|
||||
proximity_alert_radius=1000,
|
||||
live_period=900,
|
||||
)
|
||||
|
||||
# TODO: Needs improvement with in inline sent live location.
|
||||
|
@ -262,6 +264,7 @@ class TestLocationWithRequest:
|
|||
horizontal_accuracy=30,
|
||||
heading=10,
|
||||
proximity_alert_radius=500,
|
||||
live_period=200,
|
||||
)
|
||||
|
||||
assert pytest.approx(message2.location.latitude, rel=1e-5) == 52.223098
|
||||
|
@ -269,6 +272,7 @@ class TestLocationWithRequest:
|
|||
assert message2.location.horizontal_accuracy == 30
|
||||
assert message2.location.heading == 10
|
||||
assert message2.location.proximity_alert_radius == 500
|
||||
assert message2.location.live_period == 200
|
||||
|
||||
await bot.stop_message_live_location(message.chat_id, message.message_id)
|
||||
with pytest.raises(BadRequest, match="Message can't be edited"):
|
||||
|
|
|
@ -314,6 +314,8 @@ def build_kwargs(
|
|||
elif name == "ok":
|
||||
kws["ok"] = False
|
||||
kws["error_message"] = "error"
|
||||
elif name == "options":
|
||||
kws[name] = ["option1", "option2"]
|
||||
else:
|
||||
kws[name] = True
|
||||
|
||||
|
|
|
@ -1090,6 +1090,11 @@ class TestFilters:
|
|||
assert filters.StatusUpdate.GIVEAWAY_COMPLETED.check_update(update)
|
||||
update.message.giveaway_completed = None
|
||||
|
||||
update.message.chat_background_set = "test_background"
|
||||
assert filters.StatusUpdate.ALL.check_update(update)
|
||||
assert filters.StatusUpdate.CHAT_BACKGROUND_SET.check_update(update)
|
||||
update.message.chat_background_set = None
|
||||
|
||||
def test_filters_forwarded(self, update, message_origin_user):
|
||||
assert filters.FORWARDED.check_update(update)
|
||||
update.message.forward_origin = MessageOriginHiddenUser(datetime.datetime.utcnow(), 1)
|
||||
|
|
|
@ -43,6 +43,7 @@ from telegram import (
|
|||
CallbackQuery,
|
||||
Chat,
|
||||
ChatAdministratorRights,
|
||||
ChatFullInfo,
|
||||
ChatPermissions,
|
||||
Dice,
|
||||
InlineKeyboardButton,
|
||||
|
@ -55,6 +56,7 @@ from telegram import (
|
|||
InputMediaDocument,
|
||||
InputMediaPhoto,
|
||||
InputMessageContent,
|
||||
InputPollOption,
|
||||
InputTextMessageContent,
|
||||
LabeledPrice,
|
||||
LinkPreviewOptions,
|
||||
|
@ -1937,6 +1939,59 @@ class TestBotWithoutRequest:
|
|||
chat_id, message, reply_parameters=ReplyParameters(**kwargs)
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("default_bot", "custom"),
|
||||
[
|
||||
({"parse_mode": ParseMode.HTML}, "NOTHING"),
|
||||
({"parse_mode": ParseMode.HTML}, None),
|
||||
({"parse_mode": ParseMode.HTML}, ParseMode.MARKDOWN_V2),
|
||||
({"parse_mode": None}, ParseMode.MARKDOWN_V2),
|
||||
],
|
||||
indirect=["default_bot"],
|
||||
)
|
||||
async def test_send_poll_default_text_question_parse_mode(
|
||||
self, default_bot, raw_bot, chat_id, custom, monkeypatch
|
||||
):
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
expected = default_bot.defaults.text_parse_mode if custom == "NOTHING" else custom
|
||||
|
||||
option_1 = request_data.parameters["options"][0]
|
||||
option_2 = request_data.parameters["options"][1]
|
||||
assert option_1.get("text_parse_mode") == (default_bot.defaults.text_parse_mode)
|
||||
assert option_2.get("text_parse_mode") == expected
|
||||
assert request_data.parameters.get("question_parse_mode") == expected
|
||||
|
||||
return make_message("dummy reply").to_dict()
|
||||
|
||||
async def make_raw_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
expected = None if custom == "NOTHING" else custom
|
||||
|
||||
option_1 = request_data.parameters["options"][0]
|
||||
option_2 = request_data.parameters["options"][1]
|
||||
assert option_1.get("text_parse_mode") is None
|
||||
assert option_2.get("text_parse_mode") == expected
|
||||
|
||||
assert request_data.parameters.get("question_parse_mode") == expected
|
||||
|
||||
return make_message("dummy reply").to_dict()
|
||||
|
||||
if custom == "NOTHING":
|
||||
option_2 = InputPollOption("option2")
|
||||
kwargs = {}
|
||||
else:
|
||||
option_2 = InputPollOption("option2", text_parse_mode=custom)
|
||||
kwargs = {"question_parse_mode": custom}
|
||||
|
||||
monkeypatch.setattr(default_bot.request, "post", make_assertion)
|
||||
await default_bot.send_poll(
|
||||
chat_id, question="question", options=["option1", option_2], **kwargs
|
||||
)
|
||||
|
||||
monkeypatch.setattr(raw_bot.request, "post", make_raw_assertion)
|
||||
await raw_bot.send_poll(
|
||||
chat_id, question="question", options=["option1", option_2], **kwargs
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("default_bot", "custom"),
|
||||
[
|
||||
|
@ -1967,6 +2022,30 @@ class TestBotWithoutRequest:
|
|||
reply_parameters=ReplyParameters(**kwargs),
|
||||
)
|
||||
|
||||
async def test_send_poll_question_parse_mode_entities(self, bot, monkeypatch):
|
||||
# Currently only custom emoji are supported as entities which we can't test
|
||||
# We just test that the correct data is passed for now
|
||||
|
||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||
assert request_data.parameters["question_entities"] == [
|
||||
{"type": "custom_emoji", "offset": 0, "length": 1},
|
||||
{"type": "custom_emoji", "offset": 2, "length": 1},
|
||||
]
|
||||
assert request_data.parameters["question_parse_mode"] == ParseMode.MARKDOWN_V2
|
||||
return make_message("dummy reply").to_dict()
|
||||
|
||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||
await bot.send_poll(
|
||||
1,
|
||||
question="😀😃",
|
||||
options=["option1", "option2"],
|
||||
question_entities=[
|
||||
MessageEntity(MessageEntity.CUSTOM_EMOJI, 0, 1),
|
||||
MessageEntity(MessageEntity.CUSTOM_EMOJI, 2, 1),
|
||||
],
|
||||
question_parse_mode=ParseMode.MARKDOWN_V2,
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("default_bot", "custom"),
|
||||
[
|
||||
|
@ -2326,7 +2405,7 @@ class TestBotWithRequest:
|
|||
)
|
||||
async def test_send_and_stop_poll(self, bot, super_group_id, reply_markup):
|
||||
question = "Is this a test?"
|
||||
answers = ["Yes", "No", "Maybe"]
|
||||
answers = ["Yes", InputPollOption("No"), "Maybe"]
|
||||
explanation = "[Here is a link](https://google.com)"
|
||||
explanation_entities = [
|
||||
MessageEntity(MessageEntity.TEXT_LINK, 0, 14, url="https://google.com")
|
||||
|
@ -2360,7 +2439,7 @@ class TestBotWithRequest:
|
|||
assert message.poll
|
||||
assert message.poll.question == question
|
||||
assert message.poll.options[0].text == answers[0]
|
||||
assert message.poll.options[1].text == answers[1]
|
||||
assert message.poll.options[1].text == answers[1].text
|
||||
assert message.poll.options[2].text == answers[2]
|
||||
assert not message.poll.is_anonymous
|
||||
assert message.poll.allows_multiple_answers
|
||||
|
@ -2380,7 +2459,7 @@ class TestBotWithRequest:
|
|||
assert poll.is_closed
|
||||
assert poll.options[0].text == answers[0]
|
||||
assert poll.options[0].voter_count == 0
|
||||
assert poll.options[1].text == answers[1]
|
||||
assert poll.options[1].text == answers[1].text
|
||||
assert poll.options[1].voter_count == 0
|
||||
assert poll.options[2].text == answers[2]
|
||||
assert poll.options[2].voter_count == 0
|
||||
|
@ -2921,10 +3000,10 @@ class TestBotWithRequest:
|
|||
await bot.leave_chat(-123456)
|
||||
|
||||
async def test_get_chat(self, bot, super_group_id):
|
||||
chat = await bot.get_chat(super_group_id)
|
||||
assert chat.type == "supergroup"
|
||||
assert chat.title == f">>> telegram.Bot(test) @{bot.username}"
|
||||
assert chat.id == int(super_group_id)
|
||||
cfi = await bot.get_chat(super_group_id)
|
||||
assert cfi.type == "supergroup"
|
||||
assert cfi.title == f">>> telegram.Bot(test) @{bot.username}"
|
||||
assert cfi.id == int(super_group_id)
|
||||
|
||||
async def test_get_chat_administrators(self, bot, channel_id):
|
||||
admins = await bot.get_chat_administrators(channel_id)
|
||||
|
@ -3900,9 +3979,9 @@ class TestBotWithRequest:
|
|||
)
|
||||
assert data == "callback_data"
|
||||
|
||||
chat = await bot.get_chat(channel_id)
|
||||
assert chat.pinned_message == message
|
||||
assert chat.pinned_message.reply_markup == reply_markup
|
||||
cfi = await bot.get_chat(channel_id)
|
||||
assert cfi.pinned_message == message
|
||||
assert cfi.pinned_message.reply_markup == reply_markup
|
||||
assert await message.unpin() # (not placed in finally block since msg can be unbound)
|
||||
finally:
|
||||
bot.callback_data_cache.clear_callback_data()
|
||||
|
@ -3915,11 +3994,11 @@ class TestBotWithRequest:
|
|||
await bot.unpin_all_chat_messages(super_group_id)
|
||||
|
||||
try:
|
||||
chat = await bot.get_chat(super_group_id)
|
||||
cfi = await bot.get_chat(super_group_id)
|
||||
|
||||
assert isinstance(chat, Chat)
|
||||
assert int(chat.id) == int(super_group_id)
|
||||
assert chat.pinned_message is None
|
||||
assert isinstance(cfi, ChatFullInfo)
|
||||
assert int(cfi.id) == int(super_group_id)
|
||||
assert cfi.pinned_message is None
|
||||
finally:
|
||||
bot.callback_data_cache.clear_callback_data()
|
||||
bot.callback_data_cache.clear_callback_queries()
|
||||
|
|
|
@ -301,8 +301,9 @@ class TestCallbackQueryWithoutRequest(TestCallbackQueryBase):
|
|||
async def make_assertion(*_, **kwargs):
|
||||
latitude = kwargs.get("latitude") == 1
|
||||
longitude = kwargs.get("longitude") == 2
|
||||
live = kwargs.get("live_period") == 900
|
||||
ids = self.check_passed_ids(callback_query, kwargs)
|
||||
return ids and latitude and longitude
|
||||
return ids and latitude and longitude and live
|
||||
|
||||
assert check_shortcut_signature(
|
||||
CallbackQuery.edit_message_live_location,
|
||||
|
@ -322,8 +323,10 @@ class TestCallbackQueryWithoutRequest(TestCallbackQueryBase):
|
|||
)
|
||||
|
||||
monkeypatch.setattr(callback_query.get_bot(), "edit_message_live_location", make_assertion)
|
||||
assert await callback_query.edit_message_live_location(latitude=1, longitude=2)
|
||||
assert await callback_query.edit_message_live_location(1, 2)
|
||||
assert await callback_query.edit_message_live_location(
|
||||
latitude=1, longitude=2, live_period=900
|
||||
)
|
||||
assert await callback_query.edit_message_live_location(1, 2, live_period=900)
|
||||
|
||||
async def test_stop_message_live_location(self, monkeypatch, callback_query):
|
||||
if isinstance(callback_query.message, InaccessibleMessage):
|
||||
|
|
|
@ -16,7 +16,9 @@
|
|||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
import datetime
|
||||
import warnings
|
||||
|
||||
import pytest
|
||||
|
||||
|
@ -35,9 +37,11 @@ from telegram import (
|
|||
ReactionTypeEmoji,
|
||||
User,
|
||||
)
|
||||
from telegram._chat import _deprecated_attrs
|
||||
from telegram._utils.datetime import UTC, to_timestamp
|
||||
from telegram.constants import ChatAction, ChatType, ReactionEmoji
|
||||
from telegram.helpers import escape_markdown
|
||||
from telegram.warnings import PTBDeprecationWarning
|
||||
from tests.auxil.bot_method_checks import (
|
||||
check_defaults_handling,
|
||||
check_shortcut_call,
|
||||
|
@ -84,6 +88,8 @@ def chat(bot):
|
|||
business_opening_hours=TestChatBase.business_opening_hours,
|
||||
birthdate=Birthdate(1, 1),
|
||||
personal_chat=TestChatBase.personal_chat,
|
||||
first_name=TestChatBase.first_name,
|
||||
last_name=TestChatBase.last_name,
|
||||
)
|
||||
chat.set_bot(bot)
|
||||
chat._unfreeze()
|
||||
|
@ -137,6 +143,8 @@ class TestChatBase:
|
|||
custom_emoji_sticker_set_name = "custom_emoji_sticker_set_name"
|
||||
birthdate = Birthdate(1, 1)
|
||||
personal_chat = Chat(3, "private", "private")
|
||||
first_name = "first"
|
||||
last_name = "last"
|
||||
|
||||
|
||||
class TestChatWithoutRequest(TestChatBase):
|
||||
|
@ -185,6 +193,8 @@ class TestChatWithoutRequest(TestChatBase):
|
|||
"custom_emoji_sticker_set_name": self.custom_emoji_sticker_set_name,
|
||||
"birthdate": self.birthdate.to_dict(),
|
||||
"personal_chat": self.personal_chat.to_dict(),
|
||||
"first_name": self.first_name,
|
||||
"last_name": self.last_name,
|
||||
}
|
||||
chat = Chat.de_json(json_dict, bot)
|
||||
|
||||
|
@ -230,6 +240,8 @@ class TestChatWithoutRequest(TestChatBase):
|
|||
assert chat.custom_emoji_sticker_set_name == self.custom_emoji_sticker_set_name
|
||||
assert chat.birthdate == self.birthdate
|
||||
assert chat.personal_chat == self.personal_chat
|
||||
assert chat.first_name == self.first_name
|
||||
assert chat.last_name == self.last_name
|
||||
|
||||
def test_de_json_localization(self, bot, raw_bot, tz_bot):
|
||||
json_dict = {
|
||||
|
@ -251,6 +263,15 @@ class TestChatWithoutRequest(TestChatBase):
|
|||
assert chat_bot_raw.emoji_status_expiration_date.tzinfo == UTC
|
||||
assert emoji_expire_offset_tz == emoji_expire_offset
|
||||
|
||||
def test_always_tuples_attributes(self):
|
||||
chat = Chat(
|
||||
id=123,
|
||||
title="title",
|
||||
type=Chat.PRIVATE,
|
||||
)
|
||||
assert isinstance(chat.active_usernames, tuple)
|
||||
assert chat.active_usernames == ()
|
||||
|
||||
def test_to_dict(self, chat):
|
||||
chat_dict = chat.to_dict()
|
||||
|
||||
|
@ -300,15 +321,25 @@ class TestChatWithoutRequest(TestChatBase):
|
|||
assert chat_dict["unrestrict_boost_count"] == chat.unrestrict_boost_count
|
||||
assert chat_dict["birthdate"] == chat.birthdate.to_dict()
|
||||
assert chat_dict["personal_chat"] == chat.personal_chat.to_dict()
|
||||
assert chat_dict["first_name"] == chat.first_name
|
||||
assert chat_dict["last_name"] == chat.last_name
|
||||
|
||||
def test_always_tuples_attributes(self):
|
||||
chat = Chat(
|
||||
id=123,
|
||||
title="title",
|
||||
type=Chat.PRIVATE,
|
||||
)
|
||||
assert isinstance(chat.active_usernames, tuple)
|
||||
assert chat.active_usernames == ()
|
||||
def test_deprecated_attributes(self, chat):
|
||||
for depr_attr in _deprecated_attrs:
|
||||
with pytest.warns(PTBDeprecationWarning, match="deprecated and will only be accessib"):
|
||||
getattr(chat, depr_attr)
|
||||
with warnings.catch_warnings(): # No warning should be raised
|
||||
warnings.simplefilter("error")
|
||||
chat.id
|
||||
chat.first_name
|
||||
|
||||
def test_deprecated_arguments(self):
|
||||
for depr_attr in _deprecated_attrs:
|
||||
with pytest.warns(PTBDeprecationWarning, match="deprecated and will only be availabl"):
|
||||
Chat(1, "type", **{depr_attr: "1"})
|
||||
with warnings.catch_warnings(): # No warning should be raised
|
||||
warnings.simplefilter("error")
|
||||
Chat(1, "type", first_name="first_name")
|
||||
|
||||
def test_enum_init(self):
|
||||
chat = Chat(id=1, type="foo")
|
||||
|
@ -348,10 +379,7 @@ class TestChatWithoutRequest(TestChatBase):
|
|||
assert chat.full_name == "first\u2022name last\u2022name"
|
||||
chat = Chat(id=1, type=Chat.PRIVATE, first_name="first\u2022name")
|
||||
assert chat.full_name == "first\u2022name"
|
||||
chat = Chat(
|
||||
id=1,
|
||||
type=Chat.PRIVATE,
|
||||
)
|
||||
chat = Chat(id=1, type=Chat.PRIVATE)
|
||||
assert chat.full_name is None
|
||||
|
||||
def test_effective_name(self):
|
||||
|
@ -588,7 +616,7 @@ class TestChatWithoutRequest(TestChatBase):
|
|||
async def test_set_permissions(self, monkeypatch, chat):
|
||||
async def make_assertion(*_, **kwargs):
|
||||
chat_id = kwargs["chat_id"] == chat.id
|
||||
permissions = kwargs["permissions"] == self.permissions
|
||||
permissions = kwargs["permissions"] == ChatPermissions.all_permissions()
|
||||
return chat_id and permissions
|
||||
|
||||
assert check_shortcut_signature(
|
||||
|
@ -600,7 +628,7 @@ class TestChatWithoutRequest(TestChatBase):
|
|||
assert await check_defaults_handling(chat.set_permissions, chat.get_bot())
|
||||
|
||||
monkeypatch.setattr(chat.get_bot(), "set_chat_permissions", make_assertion)
|
||||
assert await chat.set_permissions(permissions=self.permissions)
|
||||
assert await chat.set_permissions(permissions=ChatPermissions.all_permissions())
|
||||
|
||||
async def test_set_administrator_custom_title(self, monkeypatch, chat):
|
||||
async def make_assertion(*_, **kwargs):
|
||||
|
|
361
tests/test_chatbackground.py
Normal file
361
tests/test_chatbackground.py
Normal file
|
@ -0,0 +1,361 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2024
|
||||
# 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 inspect
|
||||
from copy import deepcopy
|
||||
from typing import Union
|
||||
|
||||
import pytest
|
||||
|
||||
from telegram import (
|
||||
BackgroundFill,
|
||||
BackgroundFillFreeformGradient,
|
||||
BackgroundFillGradient,
|
||||
BackgroundFillSolid,
|
||||
BackgroundType,
|
||||
BackgroundTypeChatTheme,
|
||||
BackgroundTypeFill,
|
||||
BackgroundTypePattern,
|
||||
BackgroundTypeWallpaper,
|
||||
Dice,
|
||||
Document,
|
||||
)
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
ignored = ["self", "api_kwargs"]
|
||||
|
||||
|
||||
class BFDefaults:
|
||||
color = 0
|
||||
top_color = 1
|
||||
bottom_color = 2
|
||||
rotation_angle = 45
|
||||
colors = [0, 1, 2]
|
||||
|
||||
|
||||
def background_fill_solid():
|
||||
return BackgroundFillSolid(BFDefaults.color)
|
||||
|
||||
|
||||
def background_fill_gradient():
|
||||
return BackgroundFillGradient(
|
||||
BFDefaults.top_color, BFDefaults.bottom_color, BFDefaults.rotation_angle
|
||||
)
|
||||
|
||||
|
||||
def background_fill_freeform_gradient():
|
||||
return BackgroundFillFreeformGradient(BFDefaults.colors)
|
||||
|
||||
|
||||
class BTDefaults:
|
||||
document = Document(1, 2)
|
||||
fill = BackgroundFillSolid(color=0)
|
||||
dark_theme_dimming = 20
|
||||
is_blurred = True
|
||||
is_moving = False
|
||||
intensity = 90
|
||||
is_inverted = False
|
||||
theme_name = "ice"
|
||||
|
||||
|
||||
def background_type_fill():
|
||||
return BackgroundTypeFill(BTDefaults.fill, BTDefaults.dark_theme_dimming)
|
||||
|
||||
|
||||
def background_type_wallpaper():
|
||||
return BackgroundTypeWallpaper(
|
||||
BTDefaults.document,
|
||||
BTDefaults.dark_theme_dimming,
|
||||
BTDefaults.is_blurred,
|
||||
BTDefaults.is_moving,
|
||||
)
|
||||
|
||||
|
||||
def background_type_pattern():
|
||||
return BackgroundTypePattern(
|
||||
BTDefaults.document,
|
||||
BTDefaults.fill,
|
||||
BTDefaults.intensity,
|
||||
BTDefaults.is_inverted,
|
||||
BTDefaults.is_moving,
|
||||
)
|
||||
|
||||
|
||||
def background_type_chat_theme():
|
||||
return BackgroundTypeChatTheme(BTDefaults.theme_name)
|
||||
|
||||
|
||||
def make_json_dict(
|
||||
instance: Union[BackgroundType, BackgroundFill], include_optional_args: bool = False
|
||||
) -> dict:
|
||||
"""Used to make the json dict which we use for testing de_json. Similar to iter_args()"""
|
||||
json_dict = {"type": instance.type}
|
||||
sig = inspect.signature(instance.__class__.__init__)
|
||||
|
||||
for param in sig.parameters.values():
|
||||
if param.name in ignored: # ignore irrelevant params
|
||||
continue
|
||||
|
||||
val = getattr(instance, param.name)
|
||||
# Compulsory args-
|
||||
if param.default is inspect.Parameter.empty:
|
||||
if hasattr(val, "to_dict"): # convert the user object or any future ones to dict.
|
||||
val = val.to_dict()
|
||||
json_dict[param.name] = val
|
||||
|
||||
# If we want to test all args (for de_json)-
|
||||
elif param.default is not inspect.Parameter.empty and include_optional_args:
|
||||
json_dict[param.name] = val
|
||||
return json_dict
|
||||
|
||||
|
||||
def iter_args(
|
||||
instance: Union[BackgroundType, BackgroundFill],
|
||||
de_json_inst: Union[BackgroundType, BackgroundFill],
|
||||
include_optional: bool = False,
|
||||
):
|
||||
"""
|
||||
We accept both the regular instance and de_json created instance and iterate over them for
|
||||
easy one line testing later one.
|
||||
"""
|
||||
yield instance.type, de_json_inst.type # yield this here cause it's not available in sig.
|
||||
|
||||
sig = inspect.signature(instance.__class__.__init__)
|
||||
for param in sig.parameters.values():
|
||||
if param.name in ignored:
|
||||
continue
|
||||
inst_at, json_at = getattr(instance, param.name), getattr(de_json_inst, param.name)
|
||||
if (
|
||||
param.default is not inspect.Parameter.empty and include_optional
|
||||
) or param.default is inspect.Parameter.empty:
|
||||
yield inst_at, json_at
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def background_type(request):
|
||||
return request.param()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"background_type",
|
||||
[
|
||||
background_type_fill,
|
||||
background_type_wallpaper,
|
||||
background_type_pattern,
|
||||
background_type_chat_theme,
|
||||
],
|
||||
indirect=True,
|
||||
)
|
||||
class TestBackgroundTypeWithoutRequest:
|
||||
def test_slot_behaviour(self, background_type):
|
||||
inst = background_type
|
||||
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_required_args(self, bot, background_type):
|
||||
cls = background_type.__class__
|
||||
assert cls.de_json({}, bot) is None
|
||||
|
||||
json_dict = make_json_dict(background_type)
|
||||
const_background_type = BackgroundType.de_json(json_dict, bot)
|
||||
assert const_background_type.api_kwargs == {}
|
||||
|
||||
assert isinstance(const_background_type, BackgroundType)
|
||||
assert isinstance(const_background_type, cls)
|
||||
for bg_type_at, const_bg_type_at in iter_args(background_type, const_background_type):
|
||||
assert bg_type_at == const_bg_type_at
|
||||
|
||||
def test_de_json_all_args(self, bot, background_type):
|
||||
json_dict = make_json_dict(background_type, include_optional_args=True)
|
||||
const_background_type = BackgroundType.de_json(json_dict, bot)
|
||||
|
||||
assert const_background_type.api_kwargs == {}
|
||||
|
||||
assert isinstance(const_background_type, BackgroundType)
|
||||
assert isinstance(const_background_type, background_type.__class__)
|
||||
for bg_type_at, const_bg_type_at in iter_args(
|
||||
background_type, const_background_type, True
|
||||
):
|
||||
assert bg_type_at == const_bg_type_at
|
||||
|
||||
def test_de_json_invalid_type(self, background_type, bot):
|
||||
json_dict = {"type": "invalid", "theme_name": BTDefaults.theme_name}
|
||||
background_type = BackgroundType.de_json(json_dict, bot)
|
||||
|
||||
assert type(background_type) is BackgroundType
|
||||
assert background_type.type == "invalid"
|
||||
|
||||
def test_de_json_subclass(self, background_type, bot, chat_id):
|
||||
"""This makes sure that e.g. BackgroundTypeFill(data, bot) never returns a
|
||||
BackgroundTypeWallpaper instance."""
|
||||
cls = background_type.__class__
|
||||
json_dict = make_json_dict(background_type, True)
|
||||
assert type(cls.de_json(json_dict, bot)) is cls
|
||||
|
||||
def test_to_dict(self, background_type):
|
||||
bg_type_dict = background_type.to_dict()
|
||||
|
||||
assert isinstance(bg_type_dict, dict)
|
||||
assert bg_type_dict["type"] == background_type.type
|
||||
|
||||
for slot in background_type.__slots__: # additional verification for the optional args
|
||||
if slot in ("fill", "document"):
|
||||
assert (getattr(background_type, slot)).to_dict() == bg_type_dict[slot]
|
||||
continue
|
||||
assert getattr(background_type, slot) == bg_type_dict[slot]
|
||||
|
||||
def test_equality(self, background_type):
|
||||
a = BackgroundType(type="type")
|
||||
b = BackgroundType(type="type")
|
||||
c = background_type
|
||||
d = deepcopy(background_type)
|
||||
e = Dice(4, "emoji")
|
||||
sig = inspect.signature(background_type.__class__.__init__)
|
||||
params = [
|
||||
"random" for param in sig.parameters.values() if param.name not in [*ignored, "type"]
|
||||
]
|
||||
f = background_type.__class__(*params)
|
||||
|
||||
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)
|
||||
|
||||
assert f != c
|
||||
assert hash(f) != hash(c)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def background_fill(request):
|
||||
return request.param()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"background_fill",
|
||||
[
|
||||
background_fill_solid,
|
||||
background_fill_gradient,
|
||||
background_fill_freeform_gradient,
|
||||
],
|
||||
indirect=True,
|
||||
)
|
||||
class TestBackgroundFillWithoutRequest:
|
||||
def test_slot_behaviour(self, background_fill):
|
||||
inst = background_fill
|
||||
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_required_args(self, bot, background_fill):
|
||||
cls = background_fill.__class__
|
||||
assert cls.de_json({}, bot) is None
|
||||
|
||||
json_dict = make_json_dict(background_fill)
|
||||
const_background_fill = BackgroundFill.de_json(json_dict, bot)
|
||||
assert const_background_fill.api_kwargs == {}
|
||||
|
||||
assert isinstance(const_background_fill, BackgroundFill)
|
||||
assert isinstance(const_background_fill, cls)
|
||||
for bg_fill_at, const_bg_fill_at in iter_args(background_fill, const_background_fill):
|
||||
assert bg_fill_at == const_bg_fill_at
|
||||
|
||||
def test_de_json_all_args(self, bot, background_fill):
|
||||
json_dict = make_json_dict(background_fill, include_optional_args=True)
|
||||
const_background_fill = BackgroundFill.de_json(json_dict, bot)
|
||||
|
||||
assert const_background_fill.api_kwargs == {}
|
||||
|
||||
assert isinstance(const_background_fill, BackgroundFill)
|
||||
assert isinstance(const_background_fill, background_fill.__class__)
|
||||
for bg_fill_at, const_bg_fill_at in iter_args(
|
||||
background_fill, const_background_fill, True
|
||||
):
|
||||
assert bg_fill_at == const_bg_fill_at
|
||||
|
||||
def test_de_json_invalid_type(self, background_fill, bot):
|
||||
json_dict = {"type": "invalid", "theme_name": BTDefaults.theme_name}
|
||||
background_fill = BackgroundFill.de_json(json_dict, bot)
|
||||
|
||||
assert type(background_fill) is BackgroundFill
|
||||
assert background_fill.type == "invalid"
|
||||
|
||||
def test_de_json_subclass(self, background_fill, bot):
|
||||
"""This makes sure that e.g. BackgroundFillSolid(data, bot) never returns a
|
||||
BackgroundFillGradient instance."""
|
||||
cls = background_fill.__class__
|
||||
json_dict = make_json_dict(background_fill, True)
|
||||
assert type(cls.de_json(json_dict, bot)) is cls
|
||||
|
||||
def test_to_dict(self, background_fill):
|
||||
bg_fill_dict = background_fill.to_dict()
|
||||
|
||||
assert isinstance(bg_fill_dict, dict)
|
||||
assert bg_fill_dict["type"] == background_fill.type
|
||||
|
||||
for slot in background_fill.__slots__: # additional verification for the optional args
|
||||
if slot == "colors":
|
||||
assert getattr(background_fill, slot) == tuple(bg_fill_dict[slot])
|
||||
continue
|
||||
assert getattr(background_fill, slot) == bg_fill_dict[slot]
|
||||
|
||||
def test_equality(self, background_fill):
|
||||
a = BackgroundFill(type="type")
|
||||
b = BackgroundFill(type="type")
|
||||
c = background_fill
|
||||
d = deepcopy(background_fill)
|
||||
e = Dice(4, "emoji")
|
||||
sig = inspect.signature(background_fill.__class__.__init__)
|
||||
params = [
|
||||
"random" for param in sig.parameters.values() if param.name not in [*ignored, "type"]
|
||||
]
|
||||
f = background_fill.__class__(*params)
|
||||
|
||||
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)
|
||||
|
||||
assert f != c
|
||||
assert hash(f) != hash(c)
|
209
tests/test_chatfullinfo.py
Normal file
209
tests/test_chatfullinfo.py
Normal file
|
@ -0,0 +1,209 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2024
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
import datetime
|
||||
import warnings
|
||||
|
||||
import pytest
|
||||
|
||||
from telegram import (
|
||||
Birthdate,
|
||||
BusinessIntro,
|
||||
BusinessLocation,
|
||||
BusinessOpeningHours,
|
||||
BusinessOpeningHoursInterval,
|
||||
Chat,
|
||||
ChatFullInfo,
|
||||
ChatLocation,
|
||||
ChatPermissions,
|
||||
Location,
|
||||
ReactionTypeCustomEmoji,
|
||||
ReactionTypeEmoji,
|
||||
)
|
||||
from telegram._chat import _deprecated_attrs
|
||||
from telegram._utils.datetime import UTC, to_timestamp
|
||||
from telegram.constants import ReactionEmoji
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def chat_full_info(bot):
|
||||
chat = ChatFullInfo(
|
||||
TestChatInfoBase.id_,
|
||||
type=TestChatInfoBase.type_,
|
||||
accent_color_id=TestChatInfoBase.accent_color_id,
|
||||
max_reaction_count=TestChatInfoBase.max_reaction_count,
|
||||
title=TestChatInfoBase.title,
|
||||
username=TestChatInfoBase.username,
|
||||
sticker_set_name=TestChatInfoBase.sticker_set_name,
|
||||
can_set_sticker_set=TestChatInfoBase.can_set_sticker_set,
|
||||
permissions=TestChatInfoBase.permissions,
|
||||
slow_mode_delay=TestChatInfoBase.slow_mode_delay,
|
||||
bio=TestChatInfoBase.bio,
|
||||
linked_chat_id=TestChatInfoBase.linked_chat_id,
|
||||
location=TestChatInfoBase.location,
|
||||
has_private_forwards=True,
|
||||
has_protected_content=True,
|
||||
has_visible_history=True,
|
||||
join_to_send_messages=True,
|
||||
join_by_request=True,
|
||||
has_restricted_voice_and_video_messages=True,
|
||||
is_forum=True,
|
||||
active_usernames=TestChatInfoBase.active_usernames,
|
||||
emoji_status_custom_emoji_id=TestChatInfoBase.emoji_status_custom_emoji_id,
|
||||
emoji_status_expiration_date=TestChatInfoBase.emoji_status_expiration_date,
|
||||
has_aggressive_anti_spam_enabled=TestChatInfoBase.has_aggressive_anti_spam_enabled,
|
||||
has_hidden_members=TestChatInfoBase.has_hidden_members,
|
||||
available_reactions=TestChatInfoBase.available_reactions,
|
||||
background_custom_emoji_id=TestChatInfoBase.background_custom_emoji_id,
|
||||
profile_accent_color_id=TestChatInfoBase.profile_accent_color_id,
|
||||
profile_background_custom_emoji_id=TestChatInfoBase.profile_background_custom_emoji_id,
|
||||
unrestrict_boost_count=TestChatInfoBase.unrestrict_boost_count,
|
||||
custom_emoji_sticker_set_name=TestChatInfoBase.custom_emoji_sticker_set_name,
|
||||
business_intro=TestChatInfoBase.business_intro,
|
||||
business_location=TestChatInfoBase.business_location,
|
||||
business_opening_hours=TestChatInfoBase.business_opening_hours,
|
||||
birthdate=Birthdate(1, 1),
|
||||
personal_chat=TestChatInfoBase.personal_chat,
|
||||
)
|
||||
chat.set_bot(bot)
|
||||
chat._unfreeze()
|
||||
return chat
|
||||
|
||||
|
||||
class TestChatInfoBase:
|
||||
id_ = -28767330
|
||||
max_reaction_count = 2
|
||||
title = "ToledosPalaceBot - Group"
|
||||
type_ = "group"
|
||||
username = "username"
|
||||
all_members_are_administrators = False
|
||||
sticker_set_name = "stickers"
|
||||
can_set_sticker_set = False
|
||||
permissions = ChatPermissions(
|
||||
can_send_messages=True,
|
||||
can_change_info=False,
|
||||
can_invite_users=True,
|
||||
)
|
||||
slow_mode_delay = 30
|
||||
bio = "I'm a Barbie Girl in a Barbie World"
|
||||
linked_chat_id = 11880
|
||||
location = ChatLocation(Location(123, 456), "Barbie World")
|
||||
has_protected_content = True
|
||||
has_visible_history = True
|
||||
has_private_forwards = True
|
||||
join_to_send_messages = True
|
||||
join_by_request = True
|
||||
has_restricted_voice_and_video_messages = True
|
||||
is_forum = True
|
||||
active_usernames = ["These", "Are", "Usernames!"]
|
||||
emoji_status_custom_emoji_id = "VeryUniqueCustomEmojiID"
|
||||
emoji_status_expiration_date = datetime.datetime.now(tz=UTC).replace(microsecond=0)
|
||||
has_aggressive_anti_spam_enabled = True
|
||||
has_hidden_members = True
|
||||
available_reactions = [
|
||||
ReactionTypeEmoji(ReactionEmoji.THUMBS_DOWN),
|
||||
ReactionTypeCustomEmoji("custom_emoji_id"),
|
||||
]
|
||||
business_intro = BusinessIntro("Title", "Description", None)
|
||||
business_location = BusinessLocation("Address", Location(123, 456))
|
||||
business_opening_hours = BusinessOpeningHours(
|
||||
"Country/City",
|
||||
[BusinessOpeningHoursInterval(opening, opening + 60) for opening in (0, 24 * 60)],
|
||||
)
|
||||
accent_color_id = 1
|
||||
background_custom_emoji_id = "background_custom_emoji_id"
|
||||
profile_accent_color_id = 2
|
||||
profile_background_custom_emoji_id = "profile_background_custom_emoji_id"
|
||||
unrestrict_boost_count = 100
|
||||
custom_emoji_sticker_set_name = "custom_emoji_sticker_set_name"
|
||||
birthdate = Birthdate(1, 1)
|
||||
personal_chat = Chat(3, "private", "private")
|
||||
|
||||
|
||||
class TestChatWithoutRequest(TestChatInfoBase):
|
||||
def test_slot_behaviour(self, chat_full_info):
|
||||
cfi = chat_full_info
|
||||
for attr in cfi.__slots__:
|
||||
assert getattr(cfi, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(cfi)) == len(set(mro_slots(cfi))), "duplicate slot"
|
||||
|
||||
def test_de_json(self, bot):
|
||||
json_dict = {
|
||||
"id": self.id_,
|
||||
"title": self.title,
|
||||
"type": self.type_,
|
||||
"accent_color_id": self.accent_color_id,
|
||||
"max_reaction_count": self.max_reaction_count,
|
||||
"username": self.username,
|
||||
"all_members_are_administrators": self.all_members_are_administrators,
|
||||
"sticker_set_name": self.sticker_set_name,
|
||||
"can_set_sticker_set": self.can_set_sticker_set,
|
||||
"permissions": self.permissions.to_dict(),
|
||||
"slow_mode_delay": self.slow_mode_delay,
|
||||
"bio": self.bio,
|
||||
"business_intro": self.business_intro.to_dict(),
|
||||
"business_location": self.business_location.to_dict(),
|
||||
"business_opening_hours": self.business_opening_hours.to_dict(),
|
||||
"has_protected_content": self.has_protected_content,
|
||||
"has_visible_history": self.has_visible_history,
|
||||
"has_private_forwards": self.has_private_forwards,
|
||||
"linked_chat_id": self.linked_chat_id,
|
||||
"location": self.location.to_dict(),
|
||||
"join_to_send_messages": self.join_to_send_messages,
|
||||
"join_by_request": self.join_by_request,
|
||||
"has_restricted_voice_and_video_messages": (
|
||||
self.has_restricted_voice_and_video_messages
|
||||
),
|
||||
"is_forum": self.is_forum,
|
||||
"active_usernames": self.active_usernames,
|
||||
"emoji_status_custom_emoji_id": self.emoji_status_custom_emoji_id,
|
||||
"emoji_status_expiration_date": to_timestamp(self.emoji_status_expiration_date),
|
||||
"has_aggressive_anti_spam_enabled": self.has_aggressive_anti_spam_enabled,
|
||||
"has_hidden_members": self.has_hidden_members,
|
||||
"available_reactions": [reaction.to_dict() for reaction in self.available_reactions],
|
||||
"background_custom_emoji_id": self.background_custom_emoji_id,
|
||||
"profile_accent_color_id": self.profile_accent_color_id,
|
||||
"profile_background_custom_emoji_id": self.profile_background_custom_emoji_id,
|
||||
"unrestrict_boost_count": self.unrestrict_boost_count,
|
||||
"custom_emoji_sticker_set_name": self.custom_emoji_sticker_set_name,
|
||||
"birthdate": self.birthdate.to_dict(),
|
||||
"personal_chat": self.personal_chat.to_dict(),
|
||||
}
|
||||
cfi = ChatFullInfo.de_json(json_dict, bot)
|
||||
assert cfi.max_reaction_count == self.max_reaction_count
|
||||
|
||||
def test_to_dict(self, chat_full_info):
|
||||
cfi = chat_full_info
|
||||
cfi_dict = cfi.to_dict()
|
||||
|
||||
assert isinstance(cfi_dict, dict)
|
||||
assert cfi_dict["max_reaction_count"] == cfi.max_reaction_count
|
||||
|
||||
def test_attr_access_no_warning(self, chat_full_info):
|
||||
cfi = chat_full_info
|
||||
for depr_attr in _deprecated_attrs:
|
||||
with warnings.catch_warnings(): # No warning should be raised
|
||||
warnings.simplefilter("error")
|
||||
getattr(cfi, depr_attr)
|
||||
|
||||
def test_cfi_creation_no_warning(self, chat_full_info):
|
||||
cfi = chat_full_info
|
||||
with warnings.catch_warnings():
|
||||
dict = cfi.to_dict()
|
||||
ChatFullInfo(**dict)
|
|
@ -82,7 +82,9 @@ def invite_link(user):
|
|||
|
||||
@pytest.fixture(scope="module")
|
||||
def chat_member_updated(user, chat, old_chat_member, new_chat_member, invite_link, time):
|
||||
return ChatMemberUpdated(chat, user, time, old_chat_member, new_chat_member, invite_link, True)
|
||||
return ChatMemberUpdated(
|
||||
chat, user, time, old_chat_member, new_chat_member, invite_link, True, True
|
||||
)
|
||||
|
||||
|
||||
class TestChatMemberUpdatedBase:
|
||||
|
@ -129,6 +131,7 @@ class TestChatMemberUpdatedWithoutRequest(TestChatMemberUpdatedBase):
|
|||
"new_chat_member": new_chat_member.to_dict(),
|
||||
"invite_link": invite_link.to_dict(),
|
||||
"via_chat_folder_invite_link": True,
|
||||
"via_join_request": True,
|
||||
}
|
||||
|
||||
chat_member_updated = ChatMemberUpdated.de_json(json_dict, bot)
|
||||
|
@ -142,6 +145,7 @@ class TestChatMemberUpdatedWithoutRequest(TestChatMemberUpdatedBase):
|
|||
assert chat_member_updated.new_chat_member == new_chat_member
|
||||
assert chat_member_updated.invite_link == invite_link
|
||||
assert chat_member_updated.via_chat_folder_invite_link is True
|
||||
assert chat_member_updated.via_join_request is True
|
||||
|
||||
def test_de_json_localization(
|
||||
self, bot, raw_bot, tz_bot, user, chat, old_chat_member, new_chat_member, time, invite_link
|
||||
|
@ -188,6 +192,7 @@ class TestChatMemberUpdatedWithoutRequest(TestChatMemberUpdatedBase):
|
|||
chat_member_updated_dict["via_chat_folder_invite_link"]
|
||||
== chat_member_updated.via_chat_folder_invite_link
|
||||
)
|
||||
assert chat_member_updated_dict["via_join_request"] == chat_member_updated.via_join_request
|
||||
|
||||
def test_equality(self, time, old_chat_member, new_chat_member, invite_link):
|
||||
a = ChatMemberUpdated(
|
||||
|
|
|
@ -27,7 +27,10 @@ exclude_dirs = {
|
|||
/ "_passport",
|
||||
}
|
||||
|
||||
exclude_patterns = {re.compile(re.escape("self.type: ReactionType = type"))}
|
||||
exclude_patterns = {
|
||||
re.compile(re.escape("self.type: ReactionType = type")),
|
||||
re.compile(re.escape("self.type: BackgroundType = type")),
|
||||
}
|
||||
|
||||
|
||||
def test_types_are_converted_to_enum():
|
||||
|
|
|
@ -24,8 +24,10 @@ import pytest
|
|||
from telegram import (
|
||||
Animation,
|
||||
Audio,
|
||||
BackgroundTypeChatTheme,
|
||||
Bot,
|
||||
Chat,
|
||||
ChatBackground,
|
||||
ChatBoostAdded,
|
||||
ChatShared,
|
||||
Contact,
|
||||
|
@ -270,6 +272,7 @@ def message(bot):
|
|||
{"is_from_offline": True},
|
||||
{"sender_business_bot": User(1, "BusinessBot", True)},
|
||||
{"business_connection_id": "123456789"},
|
||||
{"chat_background_set": ChatBackground(type=BackgroundTypeChatTheme("ice"))},
|
||||
],
|
||||
ids=[
|
||||
"reply",
|
||||
|
@ -338,6 +341,7 @@ def message(bot):
|
|||
"sender_business_bot",
|
||||
"business_connection_id",
|
||||
"is_from_offline",
|
||||
"chat_background_set",
|
||||
],
|
||||
)
|
||||
def message_params(bot, request):
|
||||
|
@ -2414,7 +2418,8 @@ class TestMessageWithoutRequest(TestMessageBase):
|
|||
message_id = kwargs["message_id"] == message.message_id
|
||||
latitude = kwargs["latitude"] == 1
|
||||
longitude = kwargs["longitude"] == 2
|
||||
return chat_id and message_id and longitude and latitude
|
||||
live = kwargs["live_period"] == 900
|
||||
return chat_id and message_id and longitude and latitude and live
|
||||
|
||||
assert check_shortcut_signature(
|
||||
Message.edit_live_location,
|
||||
|
@ -2432,7 +2437,7 @@ class TestMessageWithoutRequest(TestMessageBase):
|
|||
assert await check_defaults_handling(message.edit_live_location, message.get_bot())
|
||||
|
||||
monkeypatch.setattr(message.get_bot(), "edit_message_live_location", make_assertion)
|
||||
assert await message.edit_live_location(latitude=1, longitude=2)
|
||||
assert await message.edit_live_location(latitude=1, longitude=2, live_period=900)
|
||||
|
||||
async def test_stop_live_location(self, monkeypatch, message):
|
||||
async def make_assertion(*_, **kwargs):
|
||||
|
|
|
@ -148,8 +148,11 @@ def check_param_type(
|
|||
# Now let's do the checking, starting with "Array of ..." types.
|
||||
if "Array of " in tg_param_type:
|
||||
# For exceptions just check if they contain the annotation
|
||||
if ptb_param.name in PTCE.ARRAY_OF_EXCEPTIONS:
|
||||
return PTCE.ARRAY_OF_EXCEPTIONS[ptb_param.name] in str(ptb_annotation), Sequence
|
||||
if any(ptb_param.name in key for key in PTCE.ARRAY_OF_EXCEPTIONS):
|
||||
for (p_name, is_expected_class), exception_type in PTCE.ARRAY_OF_EXCEPTIONS.items():
|
||||
if ptb_param.name == p_name and is_class is is_expected_class:
|
||||
log("Checking that `%s` is an exception!\n", ptb_param.name)
|
||||
return exception_type in str(ptb_annotation), Sequence
|
||||
|
||||
obj_match: re.Match | None = re.search(ARRAY_OF_PATTERN, tg_param_type)
|
||||
if obj_match is None:
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
|
||||
from telegram import Animation, Audio, Document, PhotoSize, Sticker, Video, VideoNote, Voice
|
||||
from telegram._chat import _deprecated_attrs
|
||||
from tests.test_official.helpers import _get_params_base
|
||||
|
||||
IGNORED_OBJECTS = ("ResponseParameters",)
|
||||
|
@ -47,15 +48,17 @@ class ParamTypeCheckingExceptions:
|
|||
"sticker": Sticker,
|
||||
}
|
||||
|
||||
# TODO: Look into merging this with COMPLEX_TYPES
|
||||
# Exceptions to the "Array of" types, where we accept more types than the official API
|
||||
# key: parameter name, value: type which must be present in the annotation
|
||||
# key: (parameter name, is_class), value: type which must be present in the annotation
|
||||
ARRAY_OF_EXCEPTIONS = {
|
||||
"results": "InlineQueryResult", # + Callable
|
||||
"commands": "BotCommand", # + tuple[str, str]
|
||||
"keyboard": "KeyboardButton", # + sequence[sequence[str]]
|
||||
"reaction": "ReactionType", # + str
|
||||
("results", False): "InlineQueryResult", # + Callable
|
||||
("commands", False): "BotCommand", # + tuple[str, str]
|
||||
("keyboard", True): "KeyboardButton", # + sequence[sequence[str]]
|
||||
("reaction", False): "ReactionType", # + str
|
||||
("options", False): "InputPollOption", # + str
|
||||
# TODO: Deprecated and will be corrected (and removed) in next major PTB version:
|
||||
"file_hashes": "List[str]",
|
||||
("file_hashes", True): "List[str]",
|
||||
}
|
||||
|
||||
# Special cases for other parameters that accept more types than the official API, and are
|
||||
|
@ -120,6 +123,8 @@ PTB_EXTRA_PARAMS = {
|
|||
"ChatBoostSource": {"source"}, # attributes common to all subclasses
|
||||
"MessageOrigin": {"type", "date"}, # attributes common to all subclasses
|
||||
"ReactionType": {"type"}, # attributes common to all subclasses
|
||||
"BackgroundType": {"type"}, # attributes common to all subclasses
|
||||
"BackgroundFill": {"type"}, # attributes common to all subclasses
|
||||
"InputTextMessageContent": {"disable_web_page_preview"}, # convenience arg, here for bw compat
|
||||
}
|
||||
|
||||
|
@ -143,6 +148,8 @@ PTB_IGNORED_PARAMS = {
|
|||
r"MessageOrigin\w+": {"type"},
|
||||
r"ChatBoostSource\w+": {"source"},
|
||||
r"ReactionType\w+": {"type"},
|
||||
r"BackgroundType\w+": {"type"},
|
||||
r"BackgroundFill\w+": {"type"},
|
||||
}
|
||||
|
||||
|
||||
|
@ -166,7 +173,9 @@ def ignored_param_requirements(object_name: str) -> set[str]:
|
|||
|
||||
|
||||
# Arguments that are optional arguments for now for backwards compatibility
|
||||
BACKWARDS_COMPAT_KWARGS: dict[str, set[str]] = {}
|
||||
BACKWARDS_COMPAT_KWARGS: dict[str, set[str]] = {
|
||||
"Chat": set(_deprecated_attrs), # removed by bot api 7.3
|
||||
}
|
||||
|
||||
|
||||
def backwards_compat_kwargs(object_name: str) -> set[str]:
|
||||
|
|
|
@ -19,15 +19,97 @@ from datetime import datetime, timedelta, timezone
|
|||
|
||||
import pytest
|
||||
|
||||
from telegram import Chat, MessageEntity, Poll, PollAnswer, PollOption, User
|
||||
from telegram import Chat, InputPollOption, MessageEntity, Poll, PollAnswer, PollOption, User
|
||||
from telegram._utils.datetime import UTC, to_timestamp
|
||||
from telegram.constants import PollType
|
||||
from tests.auxil.slots import mro_slots
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def input_poll_option():
|
||||
out = InputPollOption(
|
||||
text=TestInputPollOptionBase.text,
|
||||
text_parse_mode=TestInputPollOptionBase.text_parse_mode,
|
||||
text_entities=TestInputPollOptionBase.text_entities,
|
||||
)
|
||||
out._unfreeze()
|
||||
return out
|
||||
|
||||
|
||||
class TestInputPollOptionBase:
|
||||
text = "test option"
|
||||
text_parse_mode = "MarkdownV2"
|
||||
text_entities = [
|
||||
MessageEntity(0, 4, MessageEntity.BOLD),
|
||||
MessageEntity(5, 7, MessageEntity.ITALIC),
|
||||
]
|
||||
|
||||
|
||||
class TestInputPollOptionWithoutRequest(TestInputPollOptionBase):
|
||||
def test_slot_behaviour(self, input_poll_option):
|
||||
for attr in input_poll_option.__slots__:
|
||||
assert getattr(input_poll_option, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(input_poll_option)) == len(
|
||||
set(mro_slots(input_poll_option))
|
||||
), "duplicate slot"
|
||||
|
||||
def test_de_json(self):
|
||||
assert InputPollOption.de_json({}, None) is None
|
||||
|
||||
json_dict = {
|
||||
"text": self.text,
|
||||
"text_parse_mode": self.text_parse_mode,
|
||||
"text_entities": [e.to_dict() for e in self.text_entities],
|
||||
}
|
||||
input_poll_option = InputPollOption.de_json(json_dict, None)
|
||||
assert input_poll_option.api_kwargs == {}
|
||||
|
||||
assert input_poll_option.text == self.text
|
||||
assert input_poll_option.text_parse_mode == self.text_parse_mode
|
||||
assert input_poll_option.text_entities == tuple(self.text_entities)
|
||||
|
||||
def test_to_dict(self, input_poll_option):
|
||||
input_poll_option_dict = input_poll_option.to_dict()
|
||||
|
||||
assert isinstance(input_poll_option_dict, dict)
|
||||
assert input_poll_option_dict["text"] == input_poll_option.text
|
||||
assert input_poll_option_dict["text_parse_mode"] == input_poll_option.text_parse_mode
|
||||
assert input_poll_option_dict["text_entities"] == [
|
||||
e.to_dict() for e in input_poll_option.text_entities
|
||||
]
|
||||
|
||||
# Test that the default-value parameter is handled correctly
|
||||
input_poll_option = InputPollOption("text")
|
||||
input_poll_option_dict = input_poll_option.to_dict()
|
||||
assert "text_parse_mode" not in input_poll_option_dict
|
||||
|
||||
def test_equality(self):
|
||||
a = InputPollOption("text")
|
||||
b = InputPollOption("text", self.text_parse_mode)
|
||||
c = InputPollOption("text", text_entities=self.text_entities)
|
||||
d = InputPollOption("different_text")
|
||||
e = Poll(123, "question", ["O1", "O2"], 1, False, True, Poll.REGULAR, True)
|
||||
|
||||
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)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def poll_option():
|
||||
out = PollOption(text=TestPollOptionBase.text, voter_count=TestPollOptionBase.voter_count)
|
||||
out = PollOption(
|
||||
text=TestPollOptionBase.text,
|
||||
voter_count=TestPollOptionBase.voter_count,
|
||||
text_entities=TestPollOptionBase.text_entities,
|
||||
)
|
||||
out._unfreeze()
|
||||
return out
|
||||
|
||||
|
@ -35,6 +117,10 @@ def poll_option():
|
|||
class TestPollOptionBase:
|
||||
text = "test option"
|
||||
voter_count = 3
|
||||
text_entities = [
|
||||
MessageEntity(MessageEntity.BOLD, 0, 4),
|
||||
MessageEntity(MessageEntity.ITALIC, 5, 6),
|
||||
]
|
||||
|
||||
|
||||
class TestPollOptionWithoutRequest(TestPollOptionBase):
|
||||
|
@ -51,12 +137,43 @@ class TestPollOptionWithoutRequest(TestPollOptionBase):
|
|||
assert poll_option.text == self.text
|
||||
assert poll_option.voter_count == self.voter_count
|
||||
|
||||
def test_de_json_all(self):
|
||||
json_dict = {
|
||||
"text": self.text,
|
||||
"voter_count": self.voter_count,
|
||||
"text_entities": [e.to_dict() for e in self.text_entities],
|
||||
}
|
||||
poll_option = PollOption.de_json(json_dict, None)
|
||||
assert PollOption.de_json(None, None) is None
|
||||
assert poll_option.api_kwargs == {}
|
||||
|
||||
assert poll_option.text == self.text
|
||||
assert poll_option.voter_count == self.voter_count
|
||||
assert poll_option.text_entities == tuple(self.text_entities)
|
||||
|
||||
def test_to_dict(self, poll_option):
|
||||
poll_option_dict = poll_option.to_dict()
|
||||
|
||||
assert isinstance(poll_option_dict, dict)
|
||||
assert poll_option_dict["text"] == poll_option.text
|
||||
assert poll_option_dict["voter_count"] == poll_option.voter_count
|
||||
assert poll_option_dict["text_entities"] == [
|
||||
e.to_dict() for e in poll_option.text_entities
|
||||
]
|
||||
|
||||
def test_parse_entity(self, poll_option):
|
||||
entity = MessageEntity(MessageEntity.BOLD, 0, 4)
|
||||
poll_option.text_entities = [entity]
|
||||
|
||||
assert poll_option.parse_entity(entity) == "test"
|
||||
|
||||
def test_parse_entities(self, poll_option):
|
||||
entity = MessageEntity(MessageEntity.BOLD, 0, 4)
|
||||
entity_2 = MessageEntity(MessageEntity.ITALIC, 5, 6)
|
||||
poll_option.text_entities = [entity, entity_2]
|
||||
|
||||
assert poll_option.parse_entities(MessageEntity.BOLD) == {entity: "test"}
|
||||
assert poll_option.parse_entities() == {entity: "test", entity_2: "option"}
|
||||
|
||||
def test_equality(self):
|
||||
a = PollOption("text", 1)
|
||||
|
@ -159,6 +276,7 @@ def poll():
|
|||
explanation_entities=TestPollBase.explanation_entities,
|
||||
open_period=TestPollBase.open_period,
|
||||
close_date=TestPollBase.close_date,
|
||||
question_entities=TestPollBase.question_entities,
|
||||
)
|
||||
poll._unfreeze()
|
||||
return poll
|
||||
|
@ -166,7 +284,7 @@ def poll():
|
|||
|
||||
class TestPollBase:
|
||||
id_ = "id"
|
||||
question = "Test?"
|
||||
question = "Test Question?"
|
||||
options = [PollOption("test", 10), PollOption("test2", 11)]
|
||||
total_voter_count = 0
|
||||
is_closed = True
|
||||
|
@ -180,6 +298,10 @@ class TestPollBase:
|
|||
explanation_entities = [MessageEntity(13, 17, MessageEntity.URL)]
|
||||
open_period = 42
|
||||
close_date = datetime.now(timezone.utc)
|
||||
question_entities = [
|
||||
MessageEntity(MessageEntity.BOLD, 0, 4),
|
||||
MessageEntity(MessageEntity.ITALIC, 5, 8),
|
||||
]
|
||||
|
||||
|
||||
class TestPollWithoutRequest(TestPollBase):
|
||||
|
@ -197,6 +319,7 @@ class TestPollWithoutRequest(TestPollBase):
|
|||
"explanation_entities": [self.explanation_entities[0].to_dict()],
|
||||
"open_period": self.open_period,
|
||||
"close_date": to_timestamp(self.close_date),
|
||||
"question_entities": [e.to_dict() for e in self.question_entities],
|
||||
}
|
||||
poll = Poll.de_json(json_dict, bot)
|
||||
assert poll.api_kwargs == {}
|
||||
|
@ -218,6 +341,7 @@ class TestPollWithoutRequest(TestPollBase):
|
|||
assert poll.open_period == self.open_period
|
||||
assert abs(poll.close_date - self.close_date) < timedelta(seconds=1)
|
||||
assert to_timestamp(poll.close_date) == to_timestamp(self.close_date)
|
||||
assert poll.question_entities == tuple(self.question_entities)
|
||||
|
||||
def test_de_json_localization(self, tz_bot, bot, raw_bot):
|
||||
json_dict = {
|
||||
|
@ -233,6 +357,7 @@ class TestPollWithoutRequest(TestPollBase):
|
|||
"explanation_entities": [self.explanation_entities[0].to_dict()],
|
||||
"open_period": self.open_period,
|
||||
"close_date": to_timestamp(self.close_date),
|
||||
"question_entities": [e.to_dict() for e in self.question_entities],
|
||||
}
|
||||
|
||||
poll_raw = Poll.de_json(json_dict, raw_bot)
|
||||
|
@ -265,6 +390,7 @@ class TestPollWithoutRequest(TestPollBase):
|
|||
assert poll_dict["explanation_entities"] == [poll.explanation_entities[0].to_dict()]
|
||||
assert poll_dict["open_period"] == poll.open_period
|
||||
assert poll_dict["close_date"] == to_timestamp(poll.close_date)
|
||||
assert poll_dict["question_entities"] == [e.to_dict() for e in poll.question_entities]
|
||||
|
||||
def test_equality(self):
|
||||
a = Poll(123, "question", ["O1", "O2"], 1, False, True, Poll.REGULAR, True)
|
||||
|
@ -305,7 +431,7 @@ class TestPollWithoutRequest(TestPollBase):
|
|||
)
|
||||
assert poll.type is PollType.QUIZ
|
||||
|
||||
def test_parse_entity(self, poll):
|
||||
def test_parse_explanation_entity(self, poll):
|
||||
entity = MessageEntity(type=MessageEntity.URL, offset=13, length=17)
|
||||
poll.explanation_entities = [entity]
|
||||
|
||||
|
@ -323,10 +449,36 @@ class TestPollWithoutRequest(TestPollBase):
|
|||
allows_multiple_answers=False,
|
||||
).parse_explanation_entity(entity)
|
||||
|
||||
def test_parse_entities(self, poll):
|
||||
def test_parse_explanation_entities(self, poll):
|
||||
entity = MessageEntity(type=MessageEntity.URL, offset=13, length=17)
|
||||
entity_2 = MessageEntity(type=MessageEntity.BOLD, offset=13, length=1)
|
||||
poll.explanation_entities = [entity_2, entity]
|
||||
|
||||
assert poll.parse_explanation_entities(MessageEntity.URL) == {entity: "http://google.com"}
|
||||
assert poll.parse_explanation_entities() == {entity: "http://google.com", entity_2: "h"}
|
||||
|
||||
with pytest.raises(RuntimeError, match="Poll has no"):
|
||||
Poll(
|
||||
"id",
|
||||
"question",
|
||||
[PollOption("text", voter_count=0)],
|
||||
total_voter_count=0,
|
||||
is_closed=False,
|
||||
is_anonymous=False,
|
||||
type=Poll.QUIZ,
|
||||
allows_multiple_answers=False,
|
||||
).parse_explanation_entities()
|
||||
|
||||
def test_parse_question_entity(self, poll):
|
||||
entity = MessageEntity(MessageEntity.ITALIC, 5, 8)
|
||||
poll.question_entities = [entity]
|
||||
|
||||
assert poll.parse_question_entity(entity) == "Question"
|
||||
|
||||
def test_parse_question_entities(self, poll):
|
||||
entity = MessageEntity(MessageEntity.ITALIC, 5, 8)
|
||||
entity_2 = MessageEntity(MessageEntity.BOLD, 0, 4)
|
||||
poll.question_entities = [entity_2, entity]
|
||||
|
||||
assert poll.parse_question_entities(MessageEntity.ITALIC) == {entity: "Question"}
|
||||
assert poll.parse_question_entities() == {entity: "Question", entity_2: "Test"}
|
||||
|
|
Loading…
Reference in a new issue