In my code I'm facing an issue with callbackquery handler, when I'm hit /start command Next button appear and when I'm hitting on that button it gives me reply as hi, till this output is correct. Then when I'm hitting another command /help then help button appears, when I hit that help button then it gives me same reply for next button is hi.
Conclusion: Is there is way to kill old callbackquery handler. I found way is return Conversationhandler.END from callbackquery handler function but it limits my functionality had googled for it but no found expected output.
Here is my code:
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import Updater, CommandHandler, CallbackQueryHandler, ConversationHandler
TELEGRAM_HTTP_API_TOKEN = 'token'
FIRST, SECOND, HELP = range(3)
def start(bot, update):
keyboard = [
[InlineKeyboardButton(u"Next", callback_data=str(FIRST))]
]
reply_markup = InlineKeyboardMarkup(keyboard)
update.message.reply_text(
u"Start handler, Press next",
reply_markup=reply_markup
)
return FIRST
def first(bot, update):
query = update.callback_query
#reply_markup = InlineKeyboardMarkup(keyboard)
bot.send_message(chat_id=query.message.chat_id,
text='hi')
def help(bot,update):
keyboard = [
[InlineKeyboardButton(u"HELP", callback_data=str(HELP))]
]
reply_markup = InlineKeyboardMarkup(keyboard)
update.message.reply_text(
u"Help handler, Press button",
reply_markup=reply_markup
)
return HELP
def myhelp(bot,update):
query = update.callback_query
bot.send_message(chat_id=query.message.chat_id,
text='help')
updater = Updater(TELEGRAM_HTTP_API_TOKEN)
conv_handler = ConversationHandler(
entry_points=[CommandHandler('start', start)],
states={
FIRST: [CallbackQueryHandler(first)]
},
fallbacks=[CommandHandler('start', start)]
)
conv_handler1=ConversationHandler(
entry_points=[CommandHandler('help',help)],
states={
HELP: [CallbackQueryHandler(myhelp)]
},
fallbacks=[CommandHandler('help',help)]
)
updater.dispatcher.add_handler(conv_handler)
updater.dispatcher.add_handler(conv_handler1)
updater.start_polling()
updater.idle()
This is code screenshot output for more detail
Any kind of help is welcome.
You need to make your bot persistent (so state don't get lost), add error handler (to know if something failed) and fallback route (if there is not route matching).
In that case you'll know what's going wrong and where.
Related
From time to time my telegram bot seems to ignore messages. Unfortunately it never happens to me but to other users and only when they are asked to upload a photo or pdf. Sometimes the corresponding handler is not called. The problem persists even when the MessageHandler has no filters at all. I am using python-telegram-bot v13.7.
Please find below a minimal sample. It should print "receiving something" whenever the handler is called but sometimes it doesn't.
Is there anything I can do?
EDIT: modified the sample to be a MWE (you have to provide a valid telegram bot ID). Most of the time it works just fine but sometimes it will fail and not print "receiving something" although the user uploaded some document (mostly images).
from telegram import Update
from telegram.ext import (
Updater,
CommandHandler,
MessageHandler,
Filters,
ConversationHandler,
CallbackContext,
)
UPLOAD = 0
def welcome(update: Update, context: CallbackContext) -> int:
update.message.reply_text('Available commands: /upload')
return ConversationHandler.END
def upload(update: Update, context: CallbackContext) -> int:
update.message.reply_text('Please upload document.')
return UPLOAD
def receive(update: Update, context: CallbackContext) -> int:
print('receiving something...')
update.message.reply_text('Thank you.')
return ConversationHandler.END
def cancel(*args) -> int:
print('cancelled')
return welcome(*args)
def handle(*args, **options):
updater = Updater(" ...... ")
dispatcher = updater.dispatcher
conv_handler = ConversationHandler(
entry_points=[CommandHandler('upload', upload),
MessageHandler(Filters.text, welcome)
],
states={
# UPLOAD: [CommandHandler('cancel', cancel), MessageHandler(Filters.photo | Filters.document.pdf, receive)],
UPLOAD: [CommandHandler('cancel', cancel), MessageHandler(Filters.all, receive)],
},
fallbacks=[CommandHandler('cancel', cancel)],
conversation_timeout=60
)
dispatcher.add_handler(conv_handler)
updater.start_polling()
updater.idle()
handle()
One situation where such a behaviour can occure is a timeout. You have defined a timeout of 60 seconds and if the time is over, the bot silently does nothing and can not react to your uploaded file, because it's not anymore in the correct state for that. If this is you problem you can easily check this by defining a function triggering in the timeout-state.
I changed 2 things of your code:
add a timeout function:
def timeout(*args):
print("timeout")
add function to timeout state (ConversationHandler.TIMEOUT)
...
conv_handler = ConversationHandler(
entry_points=[CommandHandler('upload', upload),
MessageHandler(Filters.text, welcome)],
states={
UPLOAD: [CommandHandler('cancel', cancel), MessageHandler(Filters.all, receive)],
ConversationHandler.TIMEOUT: [MessageHandler(Filters.all, timeout)]
},
fallbacks=[CommandHandler('cancel', cancel)],
conversation_timeout=2,
)
...
(3. Reduce timeout for easier testing :))
From the documentation of conversation_timeout:
When this handler is inactive more than this timeout (in seconds), it will be automatically ended. If this value is 0 or None (default), there will be no timeout. The last received update and the corresponding context will be handled by ALL the handler’s who’s check_update method returns True that are in the state ConversationHandler.TIMEOUT.
A second situation where a bot CAN ignore messages is, when multiple users use the bot simultaneously. As far as i know CAN this lead to a situation where a user "hijacks" anothers state, so that the other users isn't anymore in the state of "uploading", so that this users can not upload a file until he "starts" the upload state.
(I am not 100% shure about this, but you should give it a try)
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
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
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.
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.