Queer European MD passionate about IT
Browse Source

Callback query handler written

Davte 5 years ago
parent
commit
c477f290dd
1 changed files with 75 additions and 10 deletions
  1. 75 10
      davtelepot/bot.py

+ 75 - 10
davtelepot/bot.py

@@ -20,7 +20,7 @@ from api import TelegramBot, TelegramError
 from database import ObjectWithDatabase
 from database import ObjectWithDatabase
 from utilities import (
 from utilities import (
     escape_html_chars, get_secure_key, make_inline_query_answer,
     escape_html_chars, get_secure_key, make_inline_query_answer,
-    make_lines_of_buttons
+    make_lines_of_buttons, remove_html_tags
 )
 )
 
 
 # Do not log aiohttp `INFO` and `DEBUG` levels
 # Do not log aiohttp `INFO` and `DEBUG` levels
@@ -453,11 +453,55 @@ class Bot(TelegramBot, ObjectWithDatabase):
         return
         return
 
 
     async def callback_query_handler(self, update):
     async def callback_query_handler(self, update):
-        """Handle Telegram `callback_query` update."""
-        logging.info(
-            f"The following update was received: {update}\n"
-            "However, this callback_query handler does nothing yet."
-        )
+        """Handle Telegram `callback_query` update.
+
+        A callback query is sent when users press inline keyboard buttons.
+        Bad clients may send malformed or deceiving callback queries:
+            never put secrets in buttons and always check request validity!
+        Get an `answer` from the callback handler associated to the query
+            prefix and use it to edit the source message (or send new ones
+            if text is longer than single message limit).
+        Anyway, the query is answered, otherwise the client would hang and
+            the bot would look like idle.
+        """
+        assert 'data' in update, "Malformed callback query lacking data field."
+        answer = dict()
+        data = update['data']
+        for start_text, handler in self.callback_handlers.items():
+            if data.startswith(start_text):
+                _function = handler['function']
+                if asyncio.iscoroutinefunction(_function):
+                    answer = await _function(update)
+                else:
+                    answer = _function(update)
+                break
+        if type(answer) is str:
+            answer = dict(text=answer)
+        assert type(answer) is dict, "Invalid callback query answer."
+        if 'edit' in answer:
+            message_identifier = self.get_message_identifier(update)
+            edit = answer['edit']
+            method = (
+                self.edit_message_text if 'text' in edit
+                else self.editMessageCaption if 'caption' in edit
+                else self.editMessageReplyMarkup if 'reply_markup' in edit
+                else (lambda *args, **kwargs: None)
+            )
+            try:
+                await method(**message_identifier, **edit)
+            except TelegramError as e:
+                logging.info("Message was not modified:\n{}".format(e))
+        try:
+            return await self.answerCallbackQuery(
+                callback_query_id=update['id'],
+                **{
+                    key: (val[:180] if key == 'text' else val)
+                    for key, val in answer.items()
+                    if key in ('text', 'show_alert', 'cache_time')
+                }
+            )
+        except TelegramError as e:
+            logging.error(e)
         return
         return
 
 
     async def shipping_query_handler(self, update):
     async def shipping_query_handler(self, update):
@@ -1103,7 +1147,10 @@ class Bot(TelegramBot, ObjectWithDatabase):
                 reply_to_update=True
                 reply_to_update=True
             )
             )
         elif 'callback_query' in update:
         elif 'callback_query' in update:
-            pass
+            await self.answerCallbackQuery(
+                callback_query_id=update['id'],
+                text=remove_html_tags(self.maintenance_message[:45])
+            )
         elif 'inline_query' in update:
         elif 'inline_query' in update:
             await self.answer_inline_query(
             await self.answer_inline_query(
                 update['inline_query']['id'],
                 update['inline_query']['id'],
@@ -1164,7 +1211,7 @@ class Bot(TelegramBot, ObjectWithDatabase):
         self.get_chat_id = getter
         self.get_chat_id = getter
 
 
     @staticmethod
     @staticmethod
-    def get_identifier_from_update_or_user_id(user_id=None, update=None):
+    def get_user_identifier(user_id=None, update=None):
         """Get telegram id of user given an update.
         """Get telegram id of user given an update.
 
 
         Result itself may be passed as either parameter (for backward
         Result itself may be passed as either parameter (for backward
@@ -1181,6 +1228,24 @@ class Bot(TelegramBot, ObjectWithDatabase):
         )
         )
         return identifier
         return identifier
 
 
+    @staticmethod
+    def get_message_identifier(update=dict()):
+        """Get a message identifier dictionary to edit `update`.
+
+        Pass the result as keyword arguments to `edit...` API methods.
+        """
+        if 'message' in update:
+            update = update['message']
+        if 'chat' in update and 'message_id' in update:
+            return dict(
+                chat_id=update['chat']['id'],
+                message_id=update['message_id']
+            )
+        elif 'inline_message_id' in update:
+            return dict(
+                inline_message_id=update['inline_message_id']
+            )
+
     def set_individual_text_message_handler(self, handler,
     def set_individual_text_message_handler(self, handler,
                                             update=None, user_id=None):
                                             update=None, user_id=None):
         """Set a custom text message handler for the user.
         """Set a custom text message handler for the user.
@@ -1190,7 +1255,7 @@ class Bot(TelegramBot, ObjectWithDatabase):
         Custom handlers last one single use, but they can call this method and
         Custom handlers last one single use, but they can call this method and
             set themselves as next custom text message handler.
             set themselves as next custom text message handler.
         """
         """
-        identifier = self.get_identifier_from_update_or_user_id(
+        identifier = self.get_user_identifier(
             user_id=user_id,
             user_id=user_id,
             update=update
             update=update
         )
         )
@@ -1207,7 +1272,7 @@ class Bot(TelegramBot, ObjectWithDatabase):
         Any text message update from the user will be handled by default
         Any text message update from the user will be handled by default
             handlers for commands, aliases and text.
             handlers for commands, aliases and text.
         """
         """
-        identifier = self.get_identifier_from_update_or_user_id(
+        identifier = self.get_user_identifier(
             user_id=user_id,
             user_id=user_id,
             update=update
             update=update
         )
         )