Skip to content

Commit 94de75f

Browse files
committed
Bring back the possibility to use strings as callback query data
In case bytes (which is the type used by telegram) can't be successfully decoded into strings, the raw bytes are presented instead of trying to decode by ignoring/replacing errors.
1 parent 0914654 commit 94de75f

3 files changed

Lines changed: 32 additions & 11 deletions

File tree

pyrogram/client/methods/bots/request_callback_answer.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def request_callback_answer(
2727
self,
2828
chat_id: Union[int, str],
2929
message_id: int,
30-
callback_data: bytes,
30+
callback_data: Union[str, bytes],
3131
timeout: int = 10
3232
):
3333
"""Request a callback answer from bots.
@@ -42,7 +42,7 @@ def request_callback_answer(
4242
message_id (``int``):
4343
The message id the inline keyboard is attached on.
4444
45-
callback_data (``bytes``):
45+
callback_data (``str`` | ``bytes``):
4646
Callback data associated with the inline button you want to get the answer from.
4747
4848
timeout (``int``, *optional*):
@@ -56,11 +56,15 @@ def request_callback_answer(
5656
RPCError: In case of a Telegram RPC error.
5757
TimeoutError: In case the bot fails to answer within 10 seconds.
5858
"""
59+
60+
# Telegram only wants bytes, but we are allowed to pass strings too.
61+
data = bytes(callback_data, "utf-8") if isinstance(callback_data, str) else callback_data
62+
5963
return self.send(
6064
functions.messages.GetBotCallbackAnswer(
6165
peer=self.resolve_peer(chat_id),
6266
msg_id=message_id,
63-
data=callback_data
67+
data=data
6468
),
6569
retries=0,
6670
timeout=timeout

pyrogram/client/types/keyboards/callback_query.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
from base64 import b64encode
2020
from struct import pack
21+
from typing import Union
2122

2223
import pyrogram
2324
from pyrogram.api import types
@@ -51,7 +52,7 @@ class CallbackQuery(PyrogramType, Update):
5152
inline_message_id (``str``):
5253
Identifier of the message sent via the bot in inline mode, that originated the query.
5354
54-
data (``bytes``, *optional*):
55+
data (``str`` | ``bytes``, *optional*):
5556
Data associated with the callback button. Be aware that a bad client can send arbitrary data in this field.
5657
5758
game_short_name (``str``, *optional*):
@@ -70,7 +71,7 @@ def __init__(
7071
chat_instance: str,
7172
message: "pyrogram.Message" = None,
7273
inline_message_id: str = None,
73-
data: bytes = None,
74+
data: Union[str, bytes] = None,
7475
game_short_name: str = None
7576
):
7677
super().__init__(client)
@@ -80,7 +81,7 @@ def __init__(
8081
self.chat_instance = chat_instance
8182
self.message = message
8283
self.inline_message_id = inline_message_id
83-
self.data = str(data, "utf-8")
84+
self.data = data
8485
self.game_short_name = game_short_name
8586

8687
@staticmethod
@@ -110,13 +111,20 @@ def _parse(client, callback_query, users) -> "CallbackQuery":
110111
b"-_"
111112
).decode().rstrip("=")
112113

114+
# Try to decode callback query data into string. If that fails, fallback to bytes instead of decoding by
115+
# ignoring/replacing errors, this way, button clicks will still work.
116+
try:
117+
data = callback_query.data.decode()
118+
except UnicodeDecodeError:
119+
data = callback_query.data
120+
113121
return CallbackQuery(
114122
id=str(callback_query.query_id),
115123
from_user=User._parse(client, users[callback_query.user_id]),
116124
message=message,
117125
inline_message_id=inline_message_id,
118126
chat_instance=str(callback_query.chat_instance),
119-
data=callback_query.data,
127+
data=data,
120128
game_short_name=callback_query.game_short_name,
121129
client=client
122130
)

pyrogram/client/types/keyboards/inline_keyboard_button.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class InlineKeyboardButton(PyrogramType):
3535
text (``str``):
3636
Label text on the button.
3737
38-
callback_data (``bytes``, *optional*):
38+
callback_data (``str`` | ``bytes``, *optional*):
3939
Data to be sent in a callback query to the bot when button is pressed, 1-64 bytes.
4040
4141
url (``str``, *optional*):
@@ -75,7 +75,7 @@ def __init__(
7575

7676
self.text = str(text)
7777
self.url = url
78-
self.callback_data = bytes(callback_data, "utf-8") if isinstance(callback_data, str) else callback_data
78+
self.callback_data = callback_data
7979
self.switch_inline_query = switch_inline_query
8080
self.switch_inline_query_current_chat = switch_inline_query_current_chat
8181
self.callback_game = callback_game
@@ -90,9 +90,16 @@ def read(o):
9090
)
9191

9292
if isinstance(o, KeyboardButtonCallback):
93+
# Try decode data to keep it as string, but if fails, fallback to bytes so we don't lose any information,
94+
# instead of decoding by ignoring/replacing errors.
95+
try:
96+
data = o.data.decode()
97+
except UnicodeDecodeError:
98+
data = o.data
99+
93100
return InlineKeyboardButton(
94101
text=o.text,
95-
callback_data=o.data
102+
callback_data=data
96103
)
97104

98105
if isinstance(o, KeyboardButtonSwitchInline):
@@ -115,7 +122,9 @@ def read(o):
115122

116123
def write(self):
117124
if self.callback_data is not None:
118-
return KeyboardButtonCallback(text=self.text, data=self.callback_data)
125+
# Telegram only wants bytes, but we are allowed to pass strings too, for convenience.
126+
data = bytes(self.callback_data, "utf-8") if isinstance(self.callback_data, str) else self.callback_data
127+
return KeyboardButtonCallback(text=self.text, data=data)
119128

120129
if self.url is not None:
121130
return KeyboardButtonUrl(text=self.text, url=self.url)

0 commit comments

Comments
 (0)