Skip to content
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
5 changes: 5 additions & 0 deletions changes/unreleased/5234.FgQx53cjQCCm2d7tEiMR.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
features = "Added the class :class:`telegram.SentGuestMessage` and the method :meth:`telegram.Bot.answer_guest_query` as well as the filter :attr:`telegram.ext.filters.UpdateType.GUEST_MESSAGE` for Bot API 10.0 Guest Mode support."
[[pull_requests]]
uid = "5234"
author_uids = ["Phil9l"]
closes_threads = ["5228"]
1 change: 1 addition & 0 deletions docs/source/telegram.at-tree.rst
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ Available Types
telegram.replykeyboardmarkup
telegram.replykeyboardremove
telegram.replyparameters
telegram.sentguestmessage
telegram.sentwebappmessage
telegram.shareduser
telegram.story
Expand Down
6 changes: 6 additions & 0 deletions docs/source/telegram.sentguestmessage.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
SentGuestMessage
================

.. autoclass:: telegram.SentGuestMessage
:members:
:show-inheritance:
2 changes: 2 additions & 0 deletions src/telegram/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@
"RevenueWithdrawalStateSucceeded",
"SecureData",
"SecureValue",
"SentGuestMessage",
"SentWebAppMessage",
"SharedUser",
"ShippingAddress",
Expand Down Expand Up @@ -595,6 +596,7 @@
from ._reply import ExternalReplyInfo, ReplyParameters, TextQuote
from ._replykeyboardmarkup import ReplyKeyboardMarkup
from ._replykeyboardremove import ReplyKeyboardRemove
from ._sentguestmessage import SentGuestMessage
from ._sentwebappmessage import SentWebAppMessage
from ._shared import ChatShared, SharedUser, UsersShared
from ._story import Story
Expand Down
48 changes: 48 additions & 0 deletions src/telegram/_bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
from telegram._preparedkeyboardbutton import PreparedKeyboardButton
from telegram._reaction import ReactionType, ReactionTypeCustomEmoji, ReactionTypeEmoji
from telegram._reply import ReplyParameters
from telegram._sentguestmessage import SentGuestMessage
from telegram._sentwebappmessage import SentWebAppMessage
from telegram._story import Story
from telegram._telegramobject import TelegramObject
Expand Down Expand Up @@ -5848,6 +5849,51 @@ async def answer_web_app_query(

return SentWebAppMessage.de_json(api_result, self)

async def answer_guest_query(
self,
guest_query_id: str,
result: "InlineQueryResult",
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict | None = None,
) -> SentGuestMessage:
"""Use this method to reply to a received guest message.

.. versionadded:: NEXT.VERSION

Args:
guest_query_id (:obj:`str`): Unique identifier for the query to be answered.
result (:class:`telegram.InlineQueryResult`): An object describing the message to be
sent.

Returns:
:class:`telegram.SentGuestMessage`: On success, a sent
:class:`telegram.SentGuestMessage` is returned.

Raises:
:class:`telegram.error.TelegramError`

"""
data: JSONDict = {
"guest_query_id": guest_query_id,
"result": self._insert_defaults_for_ilq_results(result),
}

api_result = await self._post(
"answerGuestQuery",
data,
read_timeout=read_timeout,
write_timeout=write_timeout,
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
api_kwargs=api_kwargs,
)

return SentGuestMessage.de_json(api_result, self)

async def restrict_chat_member(
self,
chat_id: str | int,
Expand Down Expand Up @@ -12399,6 +12445,8 @@ def to_dict(self, recursive: bool = True) -> JSONDict: # noqa: ARG002
"""Alias for :meth:`answer_pre_checkout_query`"""
answerWebAppQuery = answer_web_app_query
"""Alias for :meth:`answer_web_app_query`"""
answerGuestQuery = answer_guest_query
"""Alias for :meth:`answer_guest_query`"""
restrictChatMember = restrict_chat_member
"""Alias for :meth:`restrict_chat_member`"""
promoteChatMember = promote_chat_member
Expand Down
54 changes: 54 additions & 0 deletions src/telegram/_sentguestmessage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/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 an object that represents a Telegram Sent Guest Message."""

from telegram._telegramobject import TelegramObject
from telegram._utils.types import JSONDict


class SentGuestMessage(TelegramObject):
"""Describes an inline message sent by a guest bot.

Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`inline_message_id` are equal.

.. versionadded:: NEXT.VERSION

Args:
inline_message_id (:obj:`str`): Identifier of the sent inline message.

Attributes:
inline_message_id (:obj:`str`): Identifier of the sent inline message.
"""

__slots__ = ("inline_message_id",)

def __init__(
self,
inline_message_id: str,
*,
api_kwargs: JSONDict | None = None,
):
super().__init__(api_kwargs=api_kwargs)
# Required
self.inline_message_id: str = inline_message_id

self._id_attrs = (self.inline_message_id,)

self._freeze()
1 change: 0 additions & 1 deletion src/telegram/_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,6 @@ def effective_user(self) -> "User | None":
if message := (
self.message
or self.edited_message
or self.channel_post
or self.business_message
or self.edited_business_message
or self.guest_message
Expand Down
24 changes: 24 additions & 0 deletions src/telegram/ext/_extbot.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
PreparedKeyboardButton,
ReactionType,
ReplyParameters,
SentGuestMessage,
SentWebAppMessage,
StarAmount,
StarTransactions,
Expand Down Expand Up @@ -1119,6 +1120,28 @@ async def answer_web_app_query(
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
)

async def answer_guest_query(
self,
guest_query_id: str,
result: "InlineQueryResult",
*,
read_timeout: ODVInput[float] = DEFAULT_NONE,
write_timeout: ODVInput[float] = DEFAULT_NONE,
connect_timeout: ODVInput[float] = DEFAULT_NONE,
pool_timeout: ODVInput[float] = DEFAULT_NONE,
api_kwargs: JSONDict | None = None,
rate_limit_args: RLARGS | None = None,
) -> SentGuestMessage:
return await super().answer_guest_query(
guest_query_id=guest_query_id,
result=result,
read_timeout=read_timeout,
write_timeout=write_timeout,
connect_timeout=connect_timeout,
pool_timeout=pool_timeout,
api_kwargs=self._merge_api_rl_kwargs(api_kwargs, rate_limit_args),
)

async def approve_chat_join_request(
self,
chat_id: str | int,
Expand Down Expand Up @@ -5647,6 +5670,7 @@ async def save_prepared_keyboard_button(
answerShippingQuery = answer_shipping_query
answerPreCheckoutQuery = answer_pre_checkout_query
answerWebAppQuery = answer_web_app_query
answerGuestQuery = answer_guest_query
restrictChatMember = restrict_chat_member
promoteChatMember = promote_chat_member
setChatPermissions = set_chat_permissions
Expand Down
20 changes: 19 additions & 1 deletion src/telegram/ext/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,10 @@ def check_update(self, update: Update) -> bool | FilterDataDict | None:
:attr:`~telegram.Update.business_message`
or :attr:`~telegram.Update.edited_business_message`.
.. versionchanged:: NEXT.VERSION
This filter now also returns :obj:`True` if the update contains
:attr:`~telegram.Update.guest_message`.
Args:
update (:class:`telegram.Update`): The update to check.
Expand All @@ -281,7 +285,8 @@ def check_update(self, update: Update) -> bool | FilterDataDict | None:
:attr:`~telegram.Update.channel_post`, :attr:`~telegram.Update.message`,
:attr:`~telegram.Update.edited_channel_post`,
:attr:`~telegram.Update.edited_message`, :attr:`telegram.Update.business_message`,
:attr:`telegram.Update.edited_business_message`, or :obj:`False` otherwise.
:attr:`telegram.Update.edited_business_message`,
:attr:`telegram.Update.guest_message`, or :obj:`False` otherwise.
"""
return bool( # Only message updates should be handled.
update.channel_post
Expand All @@ -290,6 +295,7 @@ def check_update(self, update: Update) -> bool | FilterDataDict | None:
or update.edited_message
or update.business_message
or update.edited_business_message
or update.guest_message
)


Expand Down Expand Up @@ -2894,6 +2900,18 @@ def filter(self, update: Update) -> bool:
.. versionadded:: 21.1
"""

class _GuestMessage(UpdateFilter):
__slots__ = ()

def filter(self, update: Update) -> bool:
return update.guest_message is not None

GUEST_MESSAGE = _GuestMessage(name="filters.UpdateType.GUEST_MESSAGE")
"""Updates with :attr:`telegram.Update.guest_message`.
.. versionadded:: NEXT.VERSION
"""


class User(_ChatUserBaseFilter):
"""Filters messages to allow only those which are from specified user ID(s) or
Expand Down
2 changes: 2 additions & 0 deletions tests/auxil/dummy_objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
PollOption,
PreparedInlineMessage,
PreparedKeyboardButton,
SentGuestMessage,
SentWebAppMessage,
StarAmount,
StarTransaction,
Expand Down Expand Up @@ -136,6 +137,7 @@
),
"PreparedKeyboardButton": PreparedKeyboardButton(id=1234),
"PreparedInlineMessage": PreparedInlineMessage(id="dummy_id", expiration_date=_DUMMY_DATE),
"SentGuestMessage": SentGuestMessage(inline_message_id="dummy_inline_message_id"),
"SentWebAppMessage": SentWebAppMessage(inline_message_id="dummy_inline_message_id"),
"StarAmount": StarAmount(amount=100, nanostar_amount=356),
"StarTransactions": StarTransactions(
Expand Down
20 changes: 20 additions & 0 deletions tests/ext/test_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -2471,6 +2471,7 @@ def test_update_type_message(self, update):
assert not filters.UpdateType.BUSINESS_MESSAGES.check_update(update)
assert not filters.UpdateType.BUSINESS_MESSAGE.check_update(update)
assert not filters.UpdateType.EDITED_BUSINESS_MESSAGE.check_update(update)
assert not filters.UpdateType.GUEST_MESSAGE.check_update(update)

def test_update_type_edited_message(self, update):
update.edited_message, update.message = update.message, update.edited_message
Expand All @@ -2484,6 +2485,7 @@ def test_update_type_edited_message(self, update):
assert not filters.UpdateType.BUSINESS_MESSAGES.check_update(update)
assert not filters.UpdateType.BUSINESS_MESSAGE.check_update(update)
assert not filters.UpdateType.EDITED_BUSINESS_MESSAGE.check_update(update)
assert not filters.UpdateType.GUEST_MESSAGE.check_update(update)

def test_update_type_channel_post(self, update):
update.channel_post, update.message = update.message, update.edited_message
Expand All @@ -2497,6 +2499,7 @@ def test_update_type_channel_post(self, update):
assert not filters.UpdateType.BUSINESS_MESSAGES.check_update(update)
assert not filters.UpdateType.BUSINESS_MESSAGE.check_update(update)
assert not filters.UpdateType.EDITED_BUSINESS_MESSAGE.check_update(update)
assert not filters.UpdateType.GUEST_MESSAGE.check_update(update)

def test_update_type_edited_channel_post(self, update):
update.edited_channel_post, update.message = update.message, update.edited_message
Expand All @@ -2510,6 +2513,7 @@ def test_update_type_edited_channel_post(self, update):
assert not filters.UpdateType.BUSINESS_MESSAGES.check_update(update)
assert not filters.UpdateType.BUSINESS_MESSAGE.check_update(update)
assert not filters.UpdateType.EDITED_BUSINESS_MESSAGE.check_update(update)
assert not filters.UpdateType.GUEST_MESSAGE.check_update(update)

def test_update_type_business_message(self, update):
update.business_message, update.message = update.message, update.edited_message
Expand All @@ -2523,6 +2527,7 @@ def test_update_type_business_message(self, update):
assert filters.UpdateType.BUSINESS_MESSAGES.check_update(update)
assert filters.UpdateType.BUSINESS_MESSAGE.check_update(update)
assert not filters.UpdateType.EDITED_BUSINESS_MESSAGE.check_update(update)
assert not filters.UpdateType.GUEST_MESSAGE.check_update(update)

def test_update_type_edited_business_message(self, update):
update.edited_business_message, update.message = update.message, update.edited_message
Expand All @@ -2536,6 +2541,21 @@ def test_update_type_edited_business_message(self, update):
assert filters.UpdateType.BUSINESS_MESSAGES.check_update(update)
assert not filters.UpdateType.BUSINESS_MESSAGE.check_update(update)
assert filters.UpdateType.EDITED_BUSINESS_MESSAGE.check_update(update)
assert not filters.UpdateType.GUEST_MESSAGE.check_update(update)

def test_update_type_guest_message(self, update):
update.guest_message, update.message = update.message, update.edited_message
assert not filters.UpdateType.MESSAGE.check_update(update)
assert not filters.UpdateType.EDITED_MESSAGE.check_update(update)
assert not filters.UpdateType.MESSAGES.check_update(update)
assert not filters.UpdateType.CHANNEL_POST.check_update(update)
assert not filters.UpdateType.EDITED_CHANNEL_POST.check_update(update)
assert not filters.UpdateType.CHANNEL_POSTS.check_update(update)
assert not filters.UpdateType.EDITED.check_update(update)
assert not filters.UpdateType.BUSINESS_MESSAGES.check_update(update)
assert not filters.UpdateType.BUSINESS_MESSAGE.check_update(update)
assert not filters.UpdateType.EDITED_BUSINESS_MESSAGE.check_update(update)
assert filters.UpdateType.GUEST_MESSAGE.check_update(update)
Comment thread
Phil9l marked this conversation as resolved.

def test_merged_short_circuit_and(self, update, base_class):
update.message.text = "/test"
Expand Down
Loading
Loading