Add callback query support

This commit is contained in:
Dan 2018-04-28 23:48:38 +02:00
parent c1459aa22c
commit 9c7935702f
11 changed files with 194 additions and 44 deletions

View File

@ -500,7 +500,9 @@ def start():
f.write("\n 0xb0700020: \"pyrogram.client.types.reply_markup.InlineKeyboardMarkup\",")
f.write("\n 0xb0700021: \"pyrogram.client.types.reply_markup.KeyboardButton\",")
f.write("\n 0xb0700022: \"pyrogram.client.types.reply_markup.ReplyKeyboardMarkup\",")
f.write("\n 0xb0700023: \"pyrogram.client.types.reply_markup.ReplyKeyboardRemove\"")
f.write("\n 0xb0700023: \"pyrogram.client.types.reply_markup.ReplyKeyboardRemove\",")
f.write("\n 0xb0700024: \"pyrogram.client.types.CallbackQuery\"")
f.write("\n}\n")

View File

@ -30,7 +30,7 @@ from .client.types import (
Audio, Chat, ChatMember, ChatPhoto, Contact, Document, InputMediaPhoto,
InputMediaVideo, InputPhoneContact, Location, Message, MessageEntity,
PhotoSize, Sticker, Update, User, UserProfilePhotos, Venue, Video,
VideoNote, Voice
VideoNote, Voice, CallbackQuery
)
from .client.types.reply_markup import (
ForceReply, InlineKeyboardButton, InlineKeyboardMarkup,
@ -38,5 +38,6 @@ from .client.types.reply_markup import (
)
from .client import (
Client, ChatAction, ParseMode, Emoji,
MessageHandler, RawUpdateHandler, Filters
MessageHandler, CallbackQueryHandler, RawUpdateHandler,
Filters
)

View File

@ -20,5 +20,5 @@ from .chat_action import ChatAction
from .client import Client
from .emoji import Emoji
from .filters import Filters
from .handlers import MessageHandler, RawUpdateHandler
from .handlers import MessageHandler, CallbackQueryHandler, RawUpdateHandler
from .parse_mode import ParseMode

View File

@ -227,6 +227,13 @@ class Client:
return decorator
def on_callback_query(self, filters=None, group: int = 0):
def decorator(func):
self.add_handler(pyrogram.CallbackQueryHandler(func, filters), group)
return func
return decorator
def on_raw_update(self, group: int = 0):
"""Use this decorator to automatically register a function for handling
raw updates. This does the same thing as :meth:`add_handler` using the
@ -3525,3 +3532,19 @@ class Client:
)
return messages if is_list else messages[0]
def answer_callback_cuery(self,
callback_query_id: str,
text: str = None,
show_alert: bool = None,
url: str = None,
cache_time: int = 0):
return self.send(
functions.messages.SetBotCallbackAnswer(
query_id=int(callback_query_id),
cache_time=cache_time,
alert=show_alert,
message=text,
url=url
)
)

View File

@ -25,23 +25,23 @@ from threading import Thread
import pyrogram
from pyrogram.api import types
from .. import utils
from ..handlers import RawUpdateHandler, MessageHandler
from ..handlers import RawUpdateHandler, CallbackQueryHandler, MessageHandler
log = logging.getLogger(__name__)
class Dispatcher:
MESSAGE_UPDATES = (
NEW_MESSAGE_UPDATES = (
types.UpdateNewMessage,
types.UpdateNewChannelMessage
)
EDIT_UPDATES = (
EDIT_MESSAGE_UPDATES = (
types.UpdateEditMessage,
types.UpdateEditChannelMessage
)
ALLOWED_UPDATES = MESSAGE_UPDATES + EDIT_UPDATES
MESSAGE_UPDATES = NEW_MESSAGE_UPDATES + EDIT_MESSAGE_UPDATES
def __init__(self, client, workers):
self.client = client
@ -84,18 +84,25 @@ class Dispatcher:
args = (self.client, update, users, chats)
else:
if not isinstance(handler, MessageHandler):
continue
message = (update.message
or update.channel_post
or update.edited_message
or update.edited_channel_post)
if not handler.check(message):
continue
callback_query = update.callback_query
args = (self.client, message)
if message and isinstance(handler, MessageHandler):
if not handler.check(message):
continue
args = (self.client, message)
elif callback_query and isinstance(handler, CallbackQueryHandler):
if not handler.check(callback_query):
continue
args = (self.client, callback_query)
else:
continue
handler.callback(*args)
break
@ -117,7 +124,7 @@ class Dispatcher:
self.dispatch(update, users=users, chats=chats, is_raw=True)
if isinstance(update, Dispatcher.ALLOWED_UPDATES):
if isinstance(update, Dispatcher.MESSAGE_UPDATES):
if isinstance(update.message, types.Message):
parser = utils.parse_message
elif isinstance(update.message, types.MessageService):
@ -131,27 +138,35 @@ class Dispatcher:
users,
chats
)
is_edited_message = isinstance(update, Dispatcher.EDIT_MESSAGE_UPDATES)
self.dispatch(
pyrogram.Update(
message=((message if message.chat.type != "channel"
else None) if not is_edited_message
else None),
edited_message=((message if message.chat.type != "channel"
else None) if is_edited_message
else None),
channel_post=((message if message.chat.type == "channel"
else None) if not is_edited_message
else None),
edited_channel_post=((message if message.chat.type == "channel"
else None) if is_edited_message
else None)
)
)
elif isinstance(update, types.UpdateBotCallbackQuery):
self.dispatch(
pyrogram.Update(
callback_query=utils.parse_callback_query(
self.client, update, users, chats
)
)
)
else:
continue
is_edited_message = isinstance(update, Dispatcher.EDIT_UPDATES)
self.dispatch(
pyrogram.Update(
message=((message if message.chat.type != "channel"
else None) if not is_edited_message
else None),
edited_message=((message if message.chat.type != "channel"
else None) if is_edited_message
else None),
channel_post=((message if message.chat.type == "channel"
else None) if not is_edited_message
else None),
edited_channel_post=((message if message.chat.type == "channel"
else None) if is_edited_message
else None)
)
)
except Exception as e:
log.error(e, exc_info=True)

View File

@ -16,4 +16,4 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from .handlers import MessageHandler, RawUpdateHandler
from .handlers import MessageHandler, CallbackQueryHandler, RawUpdateHandler

View File

@ -52,6 +52,18 @@ class MessageHandler(Handler):
)
class CallbackQueryHandler(Handler):
def __init__(self, callback: callable, filters=None):
super().__init__(callback, filters)
def check(self, callback_query):
return (
self.filters(callback_query)
if self.filters
else True
)
class RawUpdateHandler(Handler):
"""The Raw Update handler class. Used to handle raw updates. It is intended to be used with
:meth:`add_handler() <pyrogram.Client.add_handler>`

View File

@ -17,6 +17,7 @@
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from .audio import Audio
from .callback_query import CallbackQuery
from .chat import Chat
from .chat_member import ChatMember
from .chat_photo import ChatPhoto

View File

@ -0,0 +1,76 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
#
# This file is part of Pyrogram.
#
# Pyrogram is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pyrogram 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 General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from pyrogram.api.core import Object
class CallbackQuery(Object):
"""This object represents an incoming callback query from a callback button in an inline keyboard.
If the button that originated the query was attached to a message sent by the bot, the field message
will be present. If the button was attached to a message sent via the bot (in inline mode),
the field inline_message_id will be present. Exactly one of the fields data or game_short_name will be present.
Attributes:
ID: ``0xb0700024``
Args:
id (``str``):
Unique identifier for this query.
from_user (:obj:`User <pyrogram.types.User>`):
Sender.
chat_instance (``str``, optional):
Message with the callback button that originated the query. Note that message content and message date will
not be available if the message is too old.
message (:obj:`Message <pyrogram.types.Message>`, optional):
Identifier of the message sent via the bot in inline mode, that originated the query.
inline_message_id (``str``):
Global identifier, uniquely corresponding to the chat to which the message with the callback button was
sent. Useful for high scores in games.
data (``str``, optional):
Data associated with the callback button. Be aware that a bad client can send arbitrary data in this field.
game_short_name (``str``, optional):
Short name of a Game to be returned, serves as the unique identifier for the game.
"""
ID = 0xb0700024
def __init__(
self,
id: str,
from_user,
chat,
chat_instance: str,
message=None,
inline_message_id: str = None,
data: str = None,
game_short_name: str = None
):
self.id = id # string
self.from_user = from_user # User
self.chat = chat
self.message = message # flags.0?Message
self.inline_message_id = inline_message_id # flags.1?string
self.chat_instance = chat_instance # string
self.data = data # flags.2?string
self.game_short_name = game_short_name # flags.3?string

View File

@ -34,12 +34,12 @@ class InlineKeyboardButton(Object):
text (``str``):
Label text on the button.
url (``str``, optional):
HTTP url to be opened when button is pressed.
callback_data (``str``, optional):
Data to be sent in a callback query to the bot when button is pressed, 1-64 bytes.
url (``str``, optional):
HTTP url to be opened when button is pressed.
switch_inline_query (``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
@ -68,8 +68,8 @@ class InlineKeyboardButton(Object):
def __init__(
self,
text: str,
url: str = None,
callback_data: str = None,
url: str = None,
switch_inline_query: str = None,
switch_inline_query_current_chat: str = None,
callback_game=None,
@ -90,13 +90,13 @@ class InlineKeyboardButton(Object):
text=b.text,
url=b.url
)
if isinstance(b, KeyboardButtonCallback):
return InlineKeyboardButton(
text=b.text,
callback_data=b.data.decode()
)
if isinstance(b, KeyboardButtonSwitchInline):
if b.same_peer:
return InlineKeyboardButton(
@ -110,12 +110,12 @@ class InlineKeyboardButton(Object):
)
def write(self):
if self.url:
return KeyboardButtonUrl(self.text, self.url)
if self.callback_data:
return KeyboardButtonCallback(self.text, self.callback_data.encode())
if self.url:
return KeyboardButtonUrl(self.text, self.url)
if self.switch_inline_query:
return KeyboardButtonSwitchInline(self.text, self.switch_inline_query)

View File

@ -721,3 +721,23 @@ def parse_photos(photos):
total_count=total_count,
photos=user_profile_photos
)
def parse_callback_query(client, callback_query, users, chats):
if isinstance(callback_query.peer, types.PeerUser):
chat = parse_user_chat(users[callback_query.peer.user_id])
elif isinstance(callback_query.peer, types.PeerChat):
chat = parse_chat_chat(chats[callback_query.peer.chat_id])
else:
chat = parse_channel_chat(chats[callback_query.peer.channel_id])
return pyrogram_types.CallbackQuery(
id=callback_query.query_id,
from_user=parse_user(users[callback_query.user_id]),
chat=chat,
message=client.get_messages(chat.id, callback_query.msg_id),
chat_instance=str(callback_query.chat_instance),
data=callback_query.data.decode(),
game_short_name=callback_query.game_short_name
# TODO: add inline_message_id
)