Files
Abdelrahman Elkheir 0fb5678180 Full Support for Bot API 10.0 (#5229)
Co-authored-by: Harshil <37377066+harshil21@users.noreply.github.com>
Co-authored-by: poolitzer <github@poolitzer.eu>
Co-authored-by: Phil Bazun <Phil9lne@gmail.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-06-09 11:49:15 -04:00

261 lines
9.8 KiB
Python

#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2026
# 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 exceptions to our API compared to the official API."""
from collections.abc import Sequence
from telegram import (
Animation,
Audio,
Document,
Gift,
LivePhoto,
PhotoSize,
Sticker,
Video,
VideoNote,
Voice,
)
from tests.test_official.helpers import _get_params_base
IGNORED_OBJECTS = ("ResponseParameters",)
GLOBALLY_IGNORED_PARAMETERS = {
"self",
"read_timeout",
"write_timeout",
"connect_timeout",
"pool_timeout",
"bot",
"api_kwargs",
}
class ParamTypeCheckingExceptions:
# Types for certain parameters accepted by PTB but not in the official API
# structure: method/class_name/regex: {param_name/regex: type}
ADDITIONAL_TYPES = {
r"send_\w*": {
"photo$": PhotoSize,
"live_photo": LivePhoto,
"video$": Video,
"video_note": VideoNote,
"audio": Audio,
"document": Document,
"animation": Animation,
"voice": Voice,
"sticker": Sticker,
"gift_id": Gift,
},
"(delete|set)_sticker.*": {
"sticker$": Sticker,
},
"replace_sticker_in_set": {
"old_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, is_class), value: type which must be present in the annotation
ARRAY_OF_EXCEPTIONS = {
("results", False): "InlineQueryResult", # + Callable
("commands", False): "BotCommand", # + tuple[str, str]
("keyboard", True): "KeyboardButton", # + sequence[sequence[str]]
("reaction", False): "ReactionType", # + str
("options", False): "InputPollOption", # + str
("correct_option_ids", False): "Sequence[typing.Literal[",
}
# Special cases for other parameters that accept more types than the official API, and are
# too complex to compare/predict with official API
# structure: class/method_name: {param_name: reduced form of annotation}
COMPLEX_TYPES = {
"send_poll": {
# "correct_option_id": int,
"correct_option_ids": Sequence[int]
},
"get_file": {
"file_id": str, # actual: Union[str, objs_with_file_id_attr]
},
r"\w+invite_link": {
"invite_link": str, # actual: Union[str, ChatInviteLink]
},
"send_invoice|create_invoice_link": {
"provider_data": str, # actual: Union[str, obj]
},
"InlineKeyboardButton": {
"callback_data": str, # actual: Union[str, obj]
},
"Input(Paid)?Media.*": {
"media": str, # actual: Union[str, InputMedia*, FileInput]
"photo": str, # actual: Union[str, FileInput]
# see also https://github.com/tdlib/telegram-bot-api/issues/707
"thumbnail": str, # actual: Union[str, FileInput]
"cover": str, # actual: Union[str, FileInput]
},
"InputProfilePhotoStatic": {
"photo": str, # actual: Union[str, FileInput]
},
"InputProfilePhotoAnimated": {
"animation": str, # actual: Union[str, FileInput]
},
"InputSticker": {
"sticker": str, # actual: Union[str, FileInput]
},
"InputStoryContent.*": {
"photo": str, # actual: Union[str, FileInput]
"video": str, # actual: Union[str, FileInput]
},
"EncryptedPassportElement": {
"data": str, # actual: Union[IdDocumentData, PersonalDetails, ResidentialAddress]
},
}
# param names ignored in the param type checking in classes for the `tg.Defaults` case.
IGNORED_DEFAULTS_PARAM_NAMES = {
"quote",
"link_preview_options",
}
# These classes' params are all ODVInput, so we ignore them in the defaults type checking.
IGNORED_DEFAULTS_CLASSES = {"LinkPreviewOptions"}
# Arguments *added* to the official API
PTB_EXTRA_PARAMS = {
"send_contact": {"contact"},
"send_location": {"location"},
"(send_message|edit_message_text)": { # convenience parameters
"disable_web_page_preview",
},
r"(send|copy)_\w+": { # convenience parameters
"reply_to_message_id",
"allow_sending_without_reply",
},
"edit_message_live_location": {"location"},
"send_venue": {"venue"},
"answer_inline_query": {"current_offset"},
"send_media_group": {"caption", "parse_mode", "caption_entities"},
"send_(animation|audio|document|photo|video(_note)?|voice|live_photo)": {"filename"},
"InlineQueryResult": {"id", "type"}, # attributes common to all subclasses
"ChatMember": {"user", "status"}, # attributes common to all subclasses
"BotCommandScope": {"type"}, # attributes common to all subclasses
"MenuButton": {"type"}, # attributes common to all subclasses
"PassportFile": {"credentials"},
"EncryptedPassportElement": {"credentials"},
"PassportElementError": {"source", "type", "message"},
"InputPoll(Option)?Media": {"media_type"},
"InputMedia": {"caption", "caption_entities", "media", "media_type", "parse_mode"},
"InputMedia(Animation|Audio|Document|Photo|Sticker|Video|VideoNote|Voice)": {
"filename",
# tags: deprecated NEXT.VERSION
"filename_depr",
},
"InputFile": {"attach", "filename", "obj", "read_file_handle"},
"MaybeInaccessibleMessage": {"date", "message_id", "chat"}, # attributes common to all subcls
"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
"OwnedGift": {"type"}, # attributes common to all subclasses
"InputTextMessageContent": {"disable_web_page_preview"}, # convenience arg, here for bw compat
"RevenueWithdrawalState": {"type"}, # attributes common to all subclasses
"TransactionPartner": {"type"}, # attributes common to all subclasses
"PaidMedia": {"type"}, # attributes common to all subclasses
"InputPaidMedia": {"type", "media"}, # attributes common to all subclasses
"InputStoryContent": {"type"}, # attributes common to all subclasses
"StoryAreaType": {"type"}, # attributes common to all subclasses
"InputProfilePhoto": {"type"}, # attributes common to all subclasses
"InputPollOptionMedia": {"args", "kwargs"}, # UnionType's __init__ signature
"InputPollMedia": {"args", "kwargs"}, # UnionType's __init__ signature
# backwards compatibility for api 10.0 changes
# tags: deprecated NEXT.VERSION, bot api 10.0
"Poll": {"correct_option_id"},
"send_poll": {"correct_option_id"},
}
def ptb_extra_params(object_name: str) -> set[str]:
return _get_params_base(object_name, PTB_EXTRA_PARAMS)
# Arguments *removed* from the official API
# Mostly due to the value being fixed anyway
PTB_IGNORED_PARAMS = {
r"InlineQueryResult\w+": {"type"},
r"ChatMember\w+": {"status"},
r"PassportElementError\w+": {"source"},
"ForceReply": {"force_reply"},
"ReplyKeyboardRemove": {"remove_keyboard"},
r"BotCommandScope\w+": {"type"},
r"MenuButton\w+": {"type"},
r"InputMedia\w+": {"type"},
"InaccessibleMessage": {"date"},
r"MessageOrigin\w+": {"type"},
r"ChatBoostSource\w+": {"source"},
r"ReactionType\w+": {"type"},
r"BackgroundType\w+": {"type"},
r"BackgroundFill\w+": {"type"},
r"RevenueWithdrawalState\w+": {"type"},
r"TransactionPartner\w+": {"type"},
r"PaidMedia\w+": {"type"},
r"InputPaidMedia\w+": {"type"},
r"InputProfilePhoto\w+": {"type"},
r"OwnedGift\w+": {"type"},
r"InputStoryContent\w+": {"type"},
r"StoryAreaType\w+": {"type"},
}
def ptb_ignored_params(object_name: str) -> set[str]:
return _get_params_base(object_name, PTB_IGNORED_PARAMS)
IGNORED_PARAM_REQUIREMENTS = {
# Ignore these since there's convenience params in them (eg. Venue)
# <----
"send_location": {"latitude", "longitude"},
"edit_message_live_location": {"latitude", "longitude"},
"send_venue": {"latitude", "longitude", "title", "address"},
"send_contact": {"phone_number", "first_name"},
# ---->
}
def ignored_param_requirements(object_name: str) -> set[str]:
return _get_params_base(object_name, IGNORED_PARAM_REQUIREMENTS)
# Arguments that are optional arguments for now for backwards compatibility
BACKWARDS_COMPAT_KWARGS: dict[str, set[str]] = {
"PollOption": {"persistent_id"},
"PollAnswer": {"option_persistent_ids"},
"Poll": {"allows_revoting", "members_only"},
"ChatMemberRestricted": {"can_react_to_messages"},
"send_poll": {"correct_option_id"},
}
def backwards_compat_kwargs(object_name: str) -> set[str]:
return _get_params_base(object_name, BACKWARDS_COMPAT_KWARGS)
IGNORED_PARAM_REQUIREMENTS.update(BACKWARDS_COMPAT_KWARGS)