【问题标题】:how to make Telegram bot dynamic keyboardbutton in python every button on one row如何在 python 中使 Telegram bot 动态键盘按钮在一行中的每个按钮
【发布时间】:2020-08-25 17:32:07
【问题描述】:

您好,我在 python 上也是 stackoverflow 中的新手...我想制作一个键盘按钮(replykeyboardmarkup),它从数据库中读取数据以及键盘每一行上的每个按钮以显示。

我想在 telegram.bot 中创建自定义键盘 例如:我们有从数据库获取的字符串数组。我们如何将数据从数组推送到 InlineKeyboardMarkup? 动态,响应迅速的按钮。

我能做什么? 谢谢

【问题讨论】:

  • 嗨,欢迎来到堆栈溢出。请参阅How to Ask 链接以获取有关如何提出问题并相应更新您的问题的更多详细信息。

标签: python


【解决方案1】:

在Telegram Bot API 中,他们没有提到如何处理动态InlineKeyboardButton。但是有一种方法可以使用CallbackQuery 来做这种事情。在以下示例中,假设 stringList 变量将保存数据库查询结果,为此我使用 stringList 作为 Pyton Dictionary 与 Python 3.7 和 pyTelegramBotAPI是为Telegram Bot API 实现的。

stringList = {"Name": "John", "Language": "Python", "API": "pyTelegramBotAPI"}

让我们根据 stringList 通过从 telebot 导入类型来创建按钮。在这里 callback_data 实现了一个 List ,它帮助我们将数据从按钮传递到查询处理程序。有一个限制,如果您的 callback_data 超过 64 字节 Telegram Bot API 将生成 BUTTON_DATA_INVALID 响应。请参考更多关于Markup Errors 以避免此类异常。

markup = types.InlineKeyboardMarkup()

for key, value in stringList.items():
    markup.add(types.InlineKeyboardButton(text=value,
                                          callback_data="['value', '" + value + "', '" + key + "']"),
               types.InlineKeyboardButton(text=crossIcon,
                                          callback_data="['key', '" + key + "']"))

下图是上面代码的结果。

我正在使用这个十字图标向您展示如何删除动态按钮以及如何通过删除该动态按钮来编辑InlineKeyboardButton。如您所见(以上代码),有两个CallbackQuery 处理第一个按钮和十字按钮。

您可以在以下代码行中捕获第一个按钮的 callback_data。

if (call.data.startswith("['value'")):
    print(f"call.data : {call.data} , type : {type(call.data)}")
    print(f"ast.literal_eval(call.data) : {ast.literal_eval(call.data)} , type : {type(ast.literal_eval(call.data))}")
    valueFromCallBack = ast.literal_eval(call.data)[1]
    keyFromCallBack = ast.literal_eval(call.data)[2]
    bot.answer_callback_query(callback_query_id=call.id, show_alert=True,
                              text="You Clicked " + valueFromCallBack + " and key is " + keyFromCallBack)

有两个打印语句可以准确地查看我们要处理的数据类型。当我们单击 John 的第一个按钮时,第一个打印语句向我们显示字符串类型 List。

call.data : ['value', 'John', 'Name'] , type : <class 'str'>

我们几乎更接近解决方案,但使用的是字符串类型 List。通过参考question 的答案,我找到了将字符串类型 List 转换为普通 List 的解决方案。 Python ast(Abstract Syntax Trees) 是表示源代码抽象句法结构的模块。使用ast.literal_eval(),我们可以从第二个打印语句中得到以下输出。

ast.literal_eval(call.data) : ['value', 'John', 'Name'] , type : <class 'list'>

同时显示此警报,因为我们在上面的代码中使用了answerCallbackQuery。

我们可以得到这样的输出,因为我们用值列表填充按钮。同样,您可以在 callback_data 中传递 List 并在 callback_query_handler 中处理该 List。我们来看看点击十字图标会发生什么。

if (call.data.startswith("['key'")):
    keyFromCallBack = ast.literal_eval(call.data)[1]
    del stringList[keyFromCallBack]
    bot.edit_message_text(chat_id=call.message.chat.id,
                          text="Here are the values of stringList", message_id=call.message.message_id,
                          reply_markup=makeKeyboard(), parse_mode='HTML')

它将转到以“'[key'”开头的answerCallbackQuery,并使用给定的密钥(del stringList[keyFromCallBack])删除Pyton Dictionary。让我们点击第一个十字图标看看会发生什么。

第一个按钮将消失,因为editMessageText 使用可用的字典详细信息重新生成。您可以调用一些数据库查询,而不是删除 Pyton Dictionary。

这是上述示例的完整代码。

import telebot
import ast
import time
from telebot import types

bot = telebot.TeleBot("YOUR_BOT_API_KEY_HERE")

stringList = {"Name": "John", "Language": "Python", "API": "pyTelegramBotAPI"}
crossIcon = u"\u274C"

def makeKeyboard():
    markup = types.InlineKeyboardMarkup()

    for key, value in stringList.items():
        markup.add(types.InlineKeyboardButton(text=value,
                                              callback_data="['value', '" + value + "', '" + key + "']"),
        types.InlineKeyboardButton(text=crossIcon,
                                   callback_data="['key', '" + key + "']"))

    return markup

@bot.message_handler(commands=['test'])
def handle_command_adminwindow(message):
    bot.send_message(chat_id=message.chat.id,
                     text="Here are the values of stringList",
                     reply_markup=makeKeyboard(),
                     parse_mode='HTML')

@bot.callback_query_handler(func=lambda call: True)
def handle_query(call):

    if (call.data.startswith("['value'")):
        print(f"call.data : {call.data} , type : {type(call.data)}")
        print(f"ast.literal_eval(call.data) : {ast.literal_eval(call.data)} , type : {type(ast.literal_eval(call.data))}")
        valueFromCallBack = ast.literal_eval(call.data)[1]
        keyFromCallBack = ast.literal_eval(call.data)[2]
        bot.answer_callback_query(callback_query_id=call.id,
                              show_alert=True,
                              text="You Clicked " + valueFromCallBack + " and key is " + keyFromCallBack)

    if (call.data.startswith("['key'")):
        keyFromCallBack = ast.literal_eval(call.data)[1]
        del stringList[keyFromCallBack]
        bot.edit_message_text(chat_id=call.message.chat.id,
                              text="Here are the values of stringList",
                              message_id=call.message.message_id,
                              reply_markup=makeKeyboard(),
                              parse_mode='HTML')
while True:
    try:
        bot.polling(none_stop=True, interval=0, timeout=0)
    except:
        time.sleep(10)

要测试此代码,请在您的机器人窗口中输入 /test 命令。

【讨论】:

    【解决方案2】:

    这是使用 python-telegram-bot:

    如果你有一个城市列表并且你想这样显示:

    def city(update,context):
      list_of_cities = ['Erode','Coimbatore','London', 'Thunder Bay', 'California']
      button_list = []
      for each in list_of_cities:
         button_list.append(InlineKeyboardButton(each, callback_data = each))
      reply_markup=InlineKeyboardMarkup(build_menu(button_list,n_cols=1)) #n_cols = 1 is for single column and mutliple rows
      bot.send_message(chat_id=update.message.chat_id, text='Choose from the following',reply_markup=reply_markup)
    
    
    def build_menu(buttons,n_cols,header_buttons=None,footer_buttons=None):
      menu = [buttons[i:i + n_cols] for i in range(0, len(buttons), n_cols)]
      if header_buttons:
        menu.insert(0, header_buttons)
      if footer_buttons:
        menu.append(footer_buttons)
      return menu
    

    如果您需要以下任何澄清评论!

    【讨论】:

    • 你的脚本帮了很多忙,但我想把它放在 conversationhandler 中,所以我想知道在这种情况下我该如何处理 callback_data ?
    【解决方案3】:

    如果你使用python-telegram-bot,根据这个snippets, 应该是这样的

    >>> custom_keyboard = [['top-left', 'top-right'], 
    ...                    ['bottom-left', 'bottom-right']]
    >>> reply_markup = telegram.ReplyKeyboardMarkup(custom_keyboard)
    >>> bot.send_message(chat_id=chat_id, 
    ...                  text="Custom Keyboard Test", 
    ...                  reply_markup=reply_markup)
    

    请注意,我没有检查代码。让我知道您是否想要。

    【讨论】:

      【解决方案4】:

      如果您使用 python-telegram-bot 作为库,您可以查看他的文档here

      此外,如果您不使用该库,您可以在代码中调整该函数

      【讨论】:

      • 是的,我正在使用 python-telegram-bot。我也在学习,但还是找不到这个方法。
      • 库中没有。您应该复制并粘贴并使用该功能。
      【解决方案5】:

      使用python电报机器人

      dict = {"Name": "John", "Language": "Python", "API": "pyTelegramBotAPI"}
      
      buttons = []
      
      for key, value in dice.items():
          buttons.append(
          [InlineKeyboardButton(text = key, url = 'google.com')]
          )
      keyboard = InlineKeyboardMarkup(buttons)
      bot.sendMessage(chat_id,text = 'f', reply_markup = keyboard)
      

      【讨论】:

      • 如何获取值作为按下按钮而不是按键的响应?
      【解决方案6】:

      lib: python-telegram-bot

      考虑到其他答案提供了有关如何创建和删除 InlineKeyboardButton 的信息,遵循如何使用 edit_message_reply_markup 方法以“动态、响应式”方式编辑它们:

      from telegram import InlineKeyboardButton, InlineKeyboardMarkup
      from telegram.ext import Updater, CommandHandler, CallbackQueryHandler
      from random import seed
      from random import random
      
      ...
      
      def start(bot, update):
          keyboard = [
                      [InlineKeyboardButton("Click button 1", callback_data='callback_1')],
                      [InlineKeyboardButton("Click button 1", callback_data='callback_2')]
                  ]
          reply_markup = InlineKeyboardMarkup(keyboard)
          message_reply_text = 'Click one of these buttons'
          update.message.reply_text(message_reply_text, reply_markup=reply_markup)
      
      def press_button_callback(bot, update):
          seed(1)
          random_number = str(random())
          keyboard = [
                      [InlineKeyboardButton('Click button 1' , callback_data='callback_1')],
                      [InlineKeyboardButton('Click button 1 ' + random_number, callback_data='callback_2')]
                  ]
      
          reply_markup = InlineKeyboardMarkup(keyboard)
          update.callback_query.edit_message_reply_markup(reply_markup)
      
      def create_updater():
         updater = Updater('XXXXXXXXXXXXXXXXXXXXXXXXXXXXX')
         updater.dispatcher.add_handler(CommandHandler('start', start))
         updater.dispatcher.add_handler(CallbackQueryHandler(press_button_callback))
         updater.dispatcher.add_error_handler(error)
      return updater
      
      ...
      

      【讨论】:

        【解决方案7】:

        只需一个按钮,试试这个 -

        import requests
        
        headers = {
            'Content-Type': 'application/json',
        }
        
        data = '{"chat_id":"@entropytrades", "text":"Testing", "reply_markup": {"inline_keyboard": [[{"text":"Visit Unofficed", "url": "http://unofficed.com"}]]} }'
        
        response = requests.post('https://api.telegram.org/bot$apiToken/sendMessage', headers=headers, data=data)
        

        【讨论】:

          猜你喜欢
          • 2017-09-26
          • 1970-01-01
          • 1970-01-01
          • 2017-01-05
          • 2022-07-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-11-17
          相关资源
          最近更新 更多