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.
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.
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.
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.