Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.4k views
in Technique[技术] by (71.8m points)

how to make Telegram bot dynamic keyboardbutton in python every button on one row

Hi i new in stackoverflow also on python... I want to make a keyboardbutton(replykeyboardmarkup) that read data from the database and every button on every row of the keyboard to show .

i want to create custom keyboard in telegram.bot For example: We have array of string that get from database.. how we can push data from array to InlineKeyboardMarkup? dynamic, responsive button.

What i can do? thankss

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

In the Telegram Bot API they didn't mention about how to deal with dynamic InlineKeyboardButton. But there is a way to do this kind of things using CallbackQuery. In the following example assume stringList variable will be hold the database query results, to do this I am using stringList as a Pyton Dictionary with Python 3.7 and pyTelegramBotAPI which is implement for the Telegram Bot API.

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

Let's create buttons according to stringList by importing types from telebot. In here callback_data fulfill with a List which helps us to pass the data from button to query handlers. There is a limitation, if your callback_data is more than 64 bytes Telegram Bot API will generate BUTTON_DATA_INVALID respond. Please refer more about Markup Errors to avoid those kind of exceptions.

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 + "']"))

Below image is the result of above code.

Result of the InlineKeyboardButton

I am using this cross icon to show you how to delete dynamic button and how to edit the InlineKeyboardButton with removing that dynamic button. As you can see (above code) there are two CallbackQuery which handle the first button and the cross button.

You can capture first button's callback_data in following code lines.

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)

There are two print statements to see exactly which type of data that we are going to handle. First print statement shows us the string type List when we click the first button of John.

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

We are almost closer to the solution, but with the string type List. I found a solution to convert string type List into the normal List by referring answers of this question. Python ast(Abstract Syntax Trees) is the module which representation of the abstract syntactic structure of source code. Using ast.literal_eval() we can get the following output from the second print statement.

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

Meantime display this alert as well becasue of answerCallbackQuery which we are using in the above code.

Result of the answerCallbackQuery

We can get output like this because we fill the button with List of values. Likewise you can pass the List inside callback_data and handle that List in callback_query_handler. Let's take a look at what will happens when click the cross icon.

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')

It will go to answerCallbackQuery which start with " '[key' " and delete the Pyton Dictionary by using the given key (del stringList[keyFromCallBack]). Let's click first cross icon see what will happen.

Regenerate dynamic buttons according to the List

First button will be disappeared because editMessageText regenerate with available Dictionary details. Instead of deleting Pyton Dictionary you can call some database query.

Here is the full code of the above examples.

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)

To test this code type /test command in your bot window.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...