Skip to content

Allow Input of Type Sticker for Several Methods #4616

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Dec 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,8 @@ markers = [
"req",
]
asyncio_mode = "auto"
log_format = "%(funcName)s - Line %(lineno)d - %(message)s"
# log_level = "DEBUG" # uncomment to see DEBUG logs
log_cli_format = "%(funcName)s - Line %(lineno)d - %(message)s"
# log_cli_level = "DEBUG" # uncomment to see DEBUG logs

# MYPY:
[tool.mypy]
Expand Down
72 changes: 54 additions & 18 deletions telegram/_bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -6622,7 +6622,7 @@ async def add_sticker_to_set(

async def set_sticker_position_in_set(
self,
sticker: str,
sticker: Union[str, "Sticker"],
position: int,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
Expand All @@ -6634,7 +6634,11 @@ async def set_sticker_position_in_set(
"""Use this method to move a sticker in a set created by the bot to a specific position.

Args:
sticker (:obj:`str`): File identifier of the sticker.
sticker (:obj:`str` | :class:`~telegram.Sticker`): File identifier of the sticker or
the sticker object.

.. versionchanged:: NEXT.VERSION
Accepts also :class:`telegram.Sticker` instances.
position (:obj:`int`): New sticker position in the set, zero-based.

Returns:
Expand All @@ -6644,7 +6648,10 @@ async def set_sticker_position_in_set(
:class:`telegram.error.TelegramError`

"""
data: JSONDict = {"sticker": sticker, "position": position}
data: JSONDict = {
"sticker": sticker if isinstance(sticker, str) else sticker.file_id,
"position": position,
}
return await self._post(
"setStickerPositionInSet",
data,
Expand Down Expand Up @@ -6749,7 +6756,7 @@ async def create_new_sticker_set(

async def delete_sticker_from_set(
self,
sticker: str,
sticker: Union[str, "Sticker"],
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
Expand All @@ -6760,7 +6767,11 @@ async def delete_sticker_from_set(
"""Use this method to delete a sticker from a set created by the bot.

Args:
sticker (:obj:`str`): File identifier of the sticker.
sticker (:obj:`str` | :class:`telegram.Sticker`): File identifier of the sticker or
the sticker object.

.. versionchanged:: NEXT.VERSION
Accepts also :class:`telegram.Sticker` instances.

Returns:
:obj:`bool`: On success, :obj:`True` is returned.
Expand All @@ -6769,7 +6780,7 @@ async def delete_sticker_from_set(
:class:`telegram.error.TelegramError`

"""
data: JSONDict = {"sticker": sticker}
data: JSONDict = {"sticker": sticker if isinstance(sticker, str) else sticker.file_id}
return await self._post(
"deleteStickerFromSet",
data,
Expand Down Expand Up @@ -6937,7 +6948,7 @@ async def set_sticker_set_title(

async def set_sticker_emoji_list(
self,
sticker: str,
sticker: Union[str, "Sticker"],
emoji_list: Sequence[str],
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
Expand All @@ -6953,7 +6964,11 @@ async def set_sticker_emoji_list(
.. versionadded:: 20.2

Args:
sticker (:obj:`str`): File identifier of the sticker.
sticker (:obj:`str` | :class:`~telegram.Sticker`): File identifier of the sticker or
the sticker object.

.. versionchanged:: NEXT.VERSION
Accepts also :class:`telegram.Sticker` instances.
emoji_list (Sequence[:obj:`str`]): A sequence of
:tg-const:`telegram.constants.StickerLimit.MIN_STICKER_EMOJI`-
:tg-const:`telegram.constants.StickerLimit.MAX_STICKER_EMOJI` emoji associated with
Expand All @@ -6965,7 +6980,10 @@ async def set_sticker_emoji_list(
Raises:
:class:`telegram.error.TelegramError`
"""
data: JSONDict = {"sticker": sticker, "emoji_list": emoji_list}
data: JSONDict = {
"sticker": sticker if isinstance(sticker, str) else sticker.file_id,
"emoji_list": emoji_list,
}
return await self._post(
"setStickerEmojiList",
data,
Expand All @@ -6978,7 +6996,7 @@ async def set_sticker_emoji_list(

async def set_sticker_keywords(
self,
sticker: str,
sticker: Union[str, "Sticker"],
keywords: Optional[Sequence[str]] = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
Expand All @@ -6994,7 +7012,11 @@ async def set_sticker_keywords(
.. versionadded:: 20.2

Args:
sticker (:obj:`str`): File identifier of the sticker.
sticker (:obj:`str` | :class:`~telegram.Sticker`): File identifier of the sticker or
the sticker object.

.. versionchanged:: NEXT.VERSION
Accepts also :class:`telegram.Sticker` instances.
keywords (Sequence[:obj:`str`]): A sequence of
0-:tg-const:`telegram.constants.StickerLimit.MAX_SEARCH_KEYWORDS` search keywords
for the sticker with total length up to
Expand All @@ -7006,7 +7028,10 @@ async def set_sticker_keywords(
Raises:
:class:`telegram.error.TelegramError`
"""
data: JSONDict = {"sticker": sticker, "keywords": keywords}
data: JSONDict = {
"sticker": sticker if isinstance(sticker, str) else sticker.file_id,
"keywords": keywords,
}
return await self._post(
"setStickerKeywords",
data,
Expand All @@ -7019,7 +7044,7 @@ async def set_sticker_keywords(

async def set_sticker_mask_position(
self,
sticker: str,
sticker: Union[str, "Sticker"],
mask_position: Optional[MaskPosition] = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
Expand All @@ -7035,7 +7060,11 @@ async def set_sticker_mask_position(
.. versionadded:: 20.2

Args:
sticker (:obj:`str`): File identifier of the sticker.
sticker (:obj:`str` | :class:`~telegram.Sticker`): File identifier of the sticker or
the sticker object.

.. versionchanged:: NEXT.VERSION
Accepts also :class:`telegram.Sticker` instances.
mask_position (:class:`telegram.MaskPosition`, optional): A object with the position
where the mask should be placed on faces. Omit the parameter to remove the mask
position.
Expand All @@ -7046,7 +7075,10 @@ async def set_sticker_mask_position(
Raises:
:class:`telegram.error.TelegramError`
"""
data: JSONDict = {"sticker": sticker, "mask_position": mask_position}
data: JSONDict = {
"sticker": sticker if isinstance(sticker, str) else sticker.file_id,
"mask_position": mask_position,
}
return await self._post(
"setStickerMaskPosition",
data,
Expand Down Expand Up @@ -9248,7 +9280,7 @@ async def replace_sticker_in_set(
self,
user_id: int,
name: str,
old_sticker: str,
old_sticker: Union[str, "Sticker"],
sticker: "InputSticker",
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
Expand All @@ -9266,7 +9298,11 @@ async def replace_sticker_in_set(
Args:
user_id (:obj:`int`): User identifier of the sticker set owner.
name (:obj:`str`): Sticker set name.
old_sticker (:obj:`str`): File identifier of the replaced sticker.
old_sticker (:obj:`str` | :class:`~telegram.Sticker`): File identifier of the replaced
sticker or the sticker object itself.

.. versionchanged:: NEXT.VERSION
Accepts also :class:`telegram.Sticker` instances.
sticker (:class:`telegram.InputSticker`): An object with information about the added
sticker. If exactly the same sticker had already been added to the set, then the
set remains unchanged.
Expand All @@ -9280,7 +9316,7 @@ async def replace_sticker_in_set(
data: JSONDict = {
"user_id": user_id,
"name": name,
"old_sticker": old_sticker,
"old_sticker": old_sticker if isinstance(old_sticker, str) else old_sticker.file_id,
"sticker": sticker,
}

Expand Down
12 changes: 6 additions & 6 deletions telegram/ext/_extbot.py
Original file line number Diff line number Diff line change
Expand Up @@ -1426,7 +1426,7 @@ async def delete_my_commands(

async def delete_sticker_from_set(
self,
sticker: str,
sticker: Union[str, "Sticker"],
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
Expand Down Expand Up @@ -3660,7 +3660,7 @@ async def set_passport_data_errors(

async def set_sticker_position_in_set(
self,
sticker: str,
sticker: Union[str, "Sticker"],
position: int,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
Expand Down Expand Up @@ -4114,7 +4114,7 @@ async def delete_sticker_set(

async def set_sticker_emoji_list(
self,
sticker: str,
sticker: Union[str, "Sticker"],
emoji_list: Sequence[str],
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
Expand All @@ -4136,7 +4136,7 @@ async def set_sticker_emoji_list(

async def set_sticker_keywords(
self,
sticker: str,
sticker: Union[str, "Sticker"],
keywords: Optional[Sequence[str]] = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
Expand All @@ -4158,7 +4158,7 @@ async def set_sticker_keywords(

async def set_sticker_mask_position(
self,
sticker: str,
sticker: Union[str, "Sticker"],
mask_position: Optional[MaskPosition] = None,
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
Expand Down Expand Up @@ -4250,7 +4250,7 @@ async def replace_sticker_in_set(
self,
user_id: int,
name: str,
old_sticker: str,
old_sticker: Union[str, "Sticker"],
sticker: "InputSticker",
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
Expand Down
48 changes: 48 additions & 0 deletions tests/_files/test_sticker.py
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,54 @@ async def make_assertion(*_, **kwargs):
monkeypatch.setattr(sticker.get_bot(), "get_file", make_assertion)
assert await sticker.get_file()

async def test_delete_sticker_from_set_sticker_input(self, offline_bot, sticker, monkeypatch):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
return request_data.json_parameters["sticker"] == sticker.file_id

monkeypatch.setattr(offline_bot.request, "post", make_assertion)
assert await offline_bot.delete_sticker_from_set(sticker)

async def test_replace_sticker_in_set_sticker_input(self, offline_bot, sticker, monkeypatch):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
return request_data.json_parameters["old_sticker"] == sticker.file_id

monkeypatch.setattr(offline_bot.request, "post", make_assertion)
assert await offline_bot.replace_sticker_in_set(
user_id=1, name="name", sticker="sticker", old_sticker=sticker
)

async def test_set_sticker_emoji_list_sticker_input(self, offline_bot, sticker, monkeypatch):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
return request_data.json_parameters["sticker"] == sticker.file_id

monkeypatch.setattr(offline_bot.request, "post", make_assertion)
assert await offline_bot.set_sticker_emoji_list(sticker, ["emoji"])

async def test_set_sticker_mask_position_sticker_input(
self, offline_bot, sticker, monkeypatch
):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
return request_data.json_parameters["sticker"] == sticker.file_id

monkeypatch.setattr(offline_bot.request, "post", make_assertion)
assert await offline_bot.set_sticker_mask_position(sticker, MaskPosition("eyes", 1, 2, 3))

async def test_set_sticker_position_in_set_sticker_input(
self, offline_bot, sticker, monkeypatch
):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
return request_data.json_parameters["sticker"] == sticker.file_id

monkeypatch.setattr(offline_bot.request, "post", make_assertion)
assert await offline_bot.set_sticker_position_in_set(sticker, 1)

async def test_set_sticker_keywords_sticker_input(self, offline_bot, sticker, monkeypatch):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
return request_data.json_parameters["sticker"] == sticker.file_id

monkeypatch.setattr(offline_bot.request, "post", make_assertion)
assert await offline_bot.set_sticker_keywords(sticker, ["keyword"])


@pytest.mark.xdist_group("stickerset")
class TestStickerSetWithRequest:
Expand Down
11 changes: 11 additions & 0 deletions tests/auxil/bot_method_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
InputTextMessageContent,
LinkPreviewOptions,
ReplyParameters,
Sticker,
TelegramObject,
)
from telegram._utils.defaultvalue import DEFAULT_NONE, DefaultValue
Expand Down Expand Up @@ -317,6 +318,16 @@ def build_kwargs(
kws["error_message"] = "error"
elif name == "options":
kws[name] = ["option1", "option2"]
elif name in ("sticker", "old_sticker"):
kws[name] = Sticker(
file_id="file_id",
file_unique_id="file_unique_id",
width=1,
height=1,
is_animated=False,
is_video=False,
type="regular",
)
else:
kws[name] = True

Expand Down
18 changes: 10 additions & 8 deletions tests/test_official/arg_type_checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
_get_params_base,
_unionizer,
cached_type_hints,
extract_mappings,
resolve_forward_refs_in_type,
wrap_with_none,
)
Expand Down Expand Up @@ -144,7 +145,7 @@ def check_param_type(
)

# CHECKING:
# Each branch manipulates the `mapped_type` (except for 4) ) to match the `ptb_annotation`.
# Each branch manipulates the `mapped_type` (except for 5) ) to match the `ptb_annotation`.

# 1) HANDLING ARRAY TYPES:
# Now let's do the checking, starting with "Array of ..." types.
Expand Down Expand Up @@ -174,9 +175,11 @@ def check_param_type(

# 2) HANDLING OTHER TYPES:
# Special case for send_* methods where we accept more types than the official API:
elif ptb_param.name in PTCE.ADDITIONAL_TYPES and obj.__name__.startswith("send"):
log("Checking that `%s` has an additional argument!\n", ptb_param.name)
mapped_type = mapped_type | PTCE.ADDITIONAL_TYPES[ptb_param.name]
elif additional_types := extract_mappings(PTCE.ADDITIONAL_TYPES, obj, ptb_param.name):
log("Checking that `%s` accepts additional types for some parameters!\n", obj.__name__)
for at in additional_types:
log("Checking that `%s` is an additional type for `%s`!\n", at, ptb_param.name)
mapped_type = mapped_type | at

# 3) HANDLING DATETIMES:
elif (
Expand Down Expand Up @@ -205,11 +208,10 @@ def check_param_type(

# 5) COMPLEX TYPES:
# Some types are too complicated, so we replace our annotation with a simpler type:
elif any(ptb_param.name in key for key in PTCE.COMPLEX_TYPES):
elif overrides := extract_mappings(PTCE.COMPLEX_TYPES, obj, ptb_param.name):
exception_type = overrides[0]
log("Converting `%s` to a simpler type!\n", ptb_param.name)
for (param_name, is_expected_class), exception_type in PTCE.COMPLEX_TYPES.items():
if ptb_param.name == param_name and is_class is is_expected_class:
ptb_annotation = wrap_with_none(tg_parameter, exception_type, obj)
ptb_annotation = wrap_with_none(tg_parameter, exception_type, obj)

# 6) HANDLING DEFAULTS PARAMETERS:
# Classes whose parameters are all ODVInput should be converted and checked.
Expand Down
Loading
Loading