How to use ChosenInlineResultHandler in Python Telegram Bot - python

I try use python-telegram-bot
I do not understand how to handle InlineKeyboardButton correctly.
def start(bot, update):
currencies = [currency for currency in API().get_currencies()]
keyboard = [[InlineKeyboardButton("{}".format(c), callback_data='{}'.format(c))] for c in currencies]
reply_markup = InlineKeyboardMarkup(keyboard)
update.message.reply_text('Select the currency you want to exchange:', reply_markup=reply_markup)
updater.dispatcher.add_handler(CommandHandler('start', start))
Now, I need to process the selection by passing it to another function with the help of ChosenInlineResultHandler, but I do not understand how to do this.

You are using Inline Buttons and the query coming back is simply CallbackQuery but not InlineQuery, yes the names are a bit confusing by the Telegram Bot API.
You can use telegram.ext.CallbackQueryHandler to catch the queries upon buttons pressed.
def button_callback(bot, update):
# data is the callback_data where you declared in the buttons
query = update.callback_query.data
if query == "something":
# do something here
updater.dispatcher.add_handler(CallbackQueryHandler(button_callback))
This is a minimal example of how to catch the buttons' data. You can check here for a complete example.

Related

How to send a random string via Inline Telegram commands?

I have created a bot (using python-telegram-bot) that upon choosing a type of query, the bot should randomly choose one of the available strings as the reply.
My function to create replies is as follows:
def generate_reply():
replies = """
Hello
Goodbye
Thanks!
Your welcome!
See you around!""".splitlines()
r = random.choice(replies).strip()
return r
And the functions to reply to the users are as follows:
#Inline Reply
def inlinequery(update, context):
query = update.inline_query.query
results = [InlineQueryResultArticle(id=uuid4(), title="Interact",
input_message_content=InputTextMessageContent(
generate_reply()))]
update.inline_query.answer(results)
#Normal reply
def reply(update, context):
update.message.reply_text(generate_reply())
And after creating the bot I add it to the bot using:
dp.add_handler(CommandHandler("reply", reply))
dp.add_handler(InlineQueryHandler(inlinequery))
when I use /reply in chat it works as intended, but wherever I use an inline command in a chat with another user or a group, the random choice apparently stops working.How can I get around this problem?
I found out the answer to my question. Apparently Telegram caches the answers to similar inline queries for some time. For this to work correctly you should set cache_time to something you'd like, in my case 0.
#Inline Reply
def inlinequery(update, context):
query = update.inline_query.query
results = [InlineQueryResultArticle(id=uuid4(), title="Interact",
input_message_content=InputTextMessageContent(
generate_reply()))]
update.inline_query.answer(results, cache_time=0)

Wait for user input before a send_message

I'm having a problem with handling incoming messages. I have a main menu in my bot with one InlineKeyboardMarkup. When the button is pressed, the bot should wait for user input and then send a message. But in my case, it sends the message, then it waits for user input and then it sends again the message. And that's a big problem, because after the message it sends, it should go back to main menu.
I'm using pythonTelegramBotAPI (telebot)
That's the code:
#bot.callback_query_handler(func = lambda call: True)
def query_handler(call):
bot.answer_callback_query(callback_query_id = call.id, text = '')
cid = call.message.chat.id
mid = call.message.message_id
msg = call.message
if call.data == 'request':
bot.edit_message_text('Test - Answer to request', cid, mid, reply_markup = markup)
request_handler(msg)
################# Keyboard sections #################
#bot.message_handler()
def request_handler(msg):
if msg.content_type == 'text':
bot.send_message(msg.chat.id, 'Request accepted')
# and here the code to go back, that I didn't do yet
Perhaps this conversational bot example will help you to understand how to build dialog with user.
Correct answer
The decorator #bot.messge_handler() is used to tell to the function that it must be handle incoming messages. So, when there's a new message, the function will do the code inside it automatically. This means that the function must not be called manually as is not required. Calling the function will, in fact, run the code inside it, instead of initializing it and waiting for an input.
And here's a tip: if the user send a message, the function is automatically called. But what happens when the bot send a message? The function is also called, because it doesn't make difference about who sent the message. So, to avoid using the function also for bot messages, just put an if in like this:
if msg.from_user.is_bot == False:
# Here put what the function should do
The if, as you can see, check if the message is coming from the bot.

Python Telegram Bot get the bot to respond to message

I am currently using python-telegram-bot library to make a telegram bot. My problem is I am trying to have my bot respond back when using the inline command. So when a user sends the bot #botname 'text', I want the to store the 'text' as a string and then have my bot send something back with that variable.
For some reason I can not get this to work. I tried the code below, but it doesn't work...I also posted the example from the github that works but not in the way i want.
My code
def inlinequery(update, context):
"""Handle the inline query."""
query = update.inline_query.query
text = query.message_text
print(text)
update.message.reply_text(text)
Example Code
#Sends message when #botname is used
def inlinequery(update, context):
"""Handle the inline query."""
query = update.inline_query.query
results = [
InlineQueryResultArticle(
id=uuid4(),
title="Caps",
input_message_content=InputTextMessageContent(
query.upper())),
InlineQueryResultArticle(
id=uuid4(),
title="Bold",
input_message_content=InputTextMessageContent(
"*{}*".format(escape_markdown(query)),
parse_mode=ParseMode.MARKDOWN)),
InlineQueryResultArticle(
id=uuid4(),
title="Italic",
input_message_content=InputTextMessageContent(
"_{}_".format(escape_markdown(query)),
parse_mode=ParseMode.MARKDOWN))]
update.inline_query.answer(results)
def main():
# Get the dispatcher to register handlers
dp = updater.dispatcher
dp.add_handler(InlineQueryHandler(inlinequery))
# Start the Bot
updater.start_polling()
if __name__ == '__main__':
main()
You can use the User object of the inline query to send them a message. Keep in mind that the user has to have started a private chat with the bot before the bot can send them messages.
I modified your attempt. It should work, but i have not tested it:
def inlinequery(update, context):
"""Handle the inline query."""
query = update.inline_query
text = query.query
print(text)
query.from_user.send_message(text)
Related docs:
InlineQuery.user
User.send_message

Best practice for a back button implementation in telegram conversation bot

I'm trying to create a telegram conversation bot using python-telegram-bot package and I wondered what is the best practice for implementing a "back to main menu" button for each state in the conversation.
For some reason, I feels like i'm doing it the wrong way. The back to main menu button actually does nothing except to drawing the main menu buttons which should be immediate, but still it's usually takes 0.5-3 seconds and sometimes even longer (Only one user at a time talking to the bot).
I'm using CallbackQueryHandler(back_to_main_menu, pattern='^main_menu$') for each state basically which I'm quite sure it's not the right way to do it although I couldn't find any other better solution so far.
Notice: the example below is just a little example of how I use the CallbackQueryHandler. My state machine got 13 states, in 10 of them I got a back to main menu button.
A short example of what I need:
import os
from telegram.ext import Updater, ConversationHandler, CommandHandler, CallbackQueryHandler
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, ReplyKeyboardRemove
FIRST, SECOND, MAIN_MENU = range(3)
def get_main_menu():
return [[InlineKeyboardButton("First", callback_data='first')],
[InlineKeyboardButton("Second", callback_data='second')]]
def add_main_menu_button(bot, update, message, menu):
query = update.callback_query
menu.append([InlineKeyboardButton("Back to main menu", callback_data="main_menu")])
reply_markup = InlineKeyboardMarkup(menu)
bot.edit_message_text(message, chat_id=query.message.chat_id, message_id=query.message.message_id,
reply_markup=reply_markup)
def back_to_main_menu(bot, update):
query = update.callback_query
reply_markup = InlineKeyboardMarkup(get_main_menu())
bot.edit_message_text("Example example 2", chat_id=query.message.chat_id, message_id=query.message.message_id, reply_markup=reply_markup)
return MAIN_MENU
def second(bot, update):
add_main_menu_button(bot, update, "E e", list())
def first(bot, update):
add_main_menu_button(bot, update, "T t", list())
def start(bot, update):
reply_markup = InlineKeyboardMarkup(get_main_menu())
update.message.reply_text("Example example 1", reply_markup=reply_markup)
return MAIN_MENU
def cancel(bot, update):
update.message.reply_text('Bye! I hope we can talk again some day.', reply_markup=ReplyKeyboardRemove())
return ConversationHandler.END
def main():
updater = Updater(os.environ["BOT_TOKEN"])
conversation_handler = ConversationHandler(entry_points=[CommandHandler('start', start)],
states={FIRST: [CallbackQueryHandler(back_to_main_menu, pattern='^main_menu$')],
SECOND: [CallbackQueryHandler(back_to_main_menu, pattern='^main_menu$')],
MAIN_MENU: [CallbackQueryHandler(first, pattern='first'),
CallbackQueryHandler(second)]},
fallbacks=[CommandHandler('cancel', cancel)],
allow_reentry=True
)
updater.dispatcher.add_handler(conversation_handler)
updater.start_polling()
updater.idle()
In addition, when I use multiple CallbackQueryHandler in a conversation I get the following warning:
WARNING:root:If 'per_message=False', 'CallbackQueryHandler' will not be tracked for every message
which makes me think I'm doing something wrong.
Thanks.
For each conversation you only need to specify one CallbackQueryHandler as a fallback. For example:
START = range(1)
start_conv_handler = ConversationHandler(entry_points=[CommandHandler('start', start)],
states={
START: [CommandHandler('start', start)]
},
fallbacks= CallbackQueryHandler(main_callback_handler)]
)
And then within that single CallbackQueryHandler you can plan all the neceesary actions you want your buttons to perform. Since you can add whatever information you need to the query that the handler receive, you can play with more complicated scenarios by putting return CallbackQueryHandler at the end of each function that describes a conversation step (start in my example).
So it would look like:
def start(bot, update):
... your code here
return CallbackQueryHandler

How to solve updater.dispatcher.add_hanlder in python Telegram bot API

I'm writing a python telegram bot and I want to work with inline keyboard bottom.
I write this code below:
from telegram import *
from telegram.ext import *
mbti_message = 'This is a test massage that should sent in MBTI part'
def startx(bot, update):
keyboard = [[InlineKeyboardButton("Option 1", callback_data='1'),
InlineKeyboardButton("Option 2", callback_data='2')],
[InlineKeyboardButton("Option 3", callback_data='3')]]
reply_markup = InlineKeyboardMarkup(keyboard)
chat_id = update.message.chat_id
bot.sendMessage(chat_id, "Message Sent From Function STARTX", reply_markup=reply_markup)
def buttomx(bot, update):
query = update.callback_query
bot.edit_message_text(text="Selected option: %s" % query.data,
chat_id=query.message.chat_id,
message_id=query.message.message_id)
def start (bot,update):
keyboard_list = [[InlineKeyboardButton("ABOUT US", callback_data='1')],
[InlineKeyboardButton("MBTI Test Start", callback_data='2')]]
reply_markup = InlineKeyboardMarkup(keyboard_list)
chat_id = update.message.chat_id
start_message_sent = "Welcome To My Bot Please Chose Your Options"
bot.sendMessage(chat_id,start_message_sent, reply_markup=reply_markup)
bot.sendMessage(chat_id,start_message_sent_global,reply_markup=reply_markup)
def bottom(bot, update):
counter = 0
query = update.callback_query
chat_id = query.message.chat_id
selected_option = int(query.data)
if selected_option==1 :
bot.sendMessage(chat_id,"You Chose About Us Section Thanks For Choosing Us")
elif selected_option==2:
bot.sendMessage(chat_id,"You Are Ready To Start Test ...")
command = 'mbti'
updater.dispatcher.add_handler(CommandHandler(command=command, callback=startx))
updater.dispatcher.add_handler(CallbackQueryHandler(buttomx))
updater.start_polling()
When user press bottom MBTI set command to mbti and pass it to command handler and when command handler get mbti command start to run starx function.
but when i use this code below in the if condition it send it before checking if condition
updater.dispatcher.add_handler (CommandHandler (command = command , startx)
What should i do in this condition?
Use a ConversationHandler for deep interactive menu. other way you can storing userState, and call function with State.
for more information see docs example it also supported user_data and RegExp for powerful handling message from users.
don't forget: data stores in memory and you may have lost or incorrect in some case. good think cleaned user_data in entry point,
each function can have many returns to other entry and with other side - each of entry has many different function in different matching case.

Categories