I am working on a inline telegram bot.
The bot should be invoked through any chat so I am using the inline method, however the bot now uses a conversation flow that requires the conversation to be started by using the /start command which is not what I want.
After calling the bot with the command I set the user should see message 1 then click on a button which will show a new selection of buttons and another message.
My problem is that now the bot shows the inital message and 2 buttons, but when I click on the button nothing happens. I believe this is due to the ConversationHandler States and how it's setup
conv_handler = ConversationHandler(
entry_points=[CommandHandler('start', inlinequery)],
states={
FIRST: [
CallbackQueryHandler(one, pattern='^' + str(ONE) + '$'),
CallbackQueryHandler(two, pattern='^' + str(TWO) + '$'),
CallbackQueryHandler(three, pattern='^' + str(THREE) + '$'),
],
SECOND: [
CallbackQueryHandler(start_over, pattern='^' + str(ONE) + '$'),
CallbackQueryHandler(end, pattern='^' + str(TWO) + '$'),
],
},
fallbacks=[CommandHandler('start', inlinequery)],
Based on this it is waiting for the /start command to initiate the conv_handler. I want It to start when the user sends in any chat @botusername <command I set> which is written in the function inlinequery.
The code:
from datetime import datetime
from uuid import uuid4
import logging
import emojihash
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
from telegram.ext import (
Updater,
CommandHandler,
CallbackQueryHandler,
ConversationHandler,
CallbackContext,
)
from telegram.ext import InlineQueryHandler, CommandHandler, CallbackContext
from telegram.utils.helpers import escape_markdown
from telegram import InlineQueryResultArticle, ParseMode, InputTextMessageContent, Update
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)
logger = logging.getLogger(__name__)
TransactionDateTime: str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
TransactionNumber: int = 1
TotalTransactions:int = 1
EmojiCode: str = emojihash.eh1("unique password" , 5) #TODO: make more complex
Emojihash: str =emojihash.eh1("unique code",5)
FIRST, SECOND = range(2)
ONE, TWO, THREE, FOUR = range(4)
verified_message_2="message 2"
verified_message_1 = "Message 1 "
def inlinequery(update: Update, context: CallbackContext) -> None:
print("Inline hit!")
# print the inline query from the update.
query = update.inline_query.query
print("len(query):" + str(len(query)))
if len(query) > 0:
print("query[-1] == " "?: " + str(query[-1] == "?"))
print("query[-1] == " + query[-1])
# len(query) > 1 and query[-1] == " "
if len(query) == 0 or query[-1] != ".":
print("Empty query, showing message to seller to type username of buyer")
results = [
InlineQueryResultArticle(
id="Noop",
title="title",
input_message_content=InputTextMessageContent("I don't know how to use this bot yet. I was supposed to type the username but clicked this button anyway. Give me a second to figure this out."),
)
]
update.inline_query.answer(results)
# else if the query ends with a period character:
elif len(query) > 1 and query[-1] == ".":
buyer_username = query
SellerUserName: str = update.inline_query.from_user.username
print("buyer_username:" + buyer_username)
EmojiCode: str = emojihash.eh1("unique password" + SellerUserName + str(update.inline_query.from_user.id), 5)
keyboard = [
[
InlineKeyboardButton(EmojiCode, callback_data=str(ONE)),
],
[
InlineKeyboardButton(Emojihash, callback_data=str(TWO)),
],
]
reply_markup = InlineKeyboardMarkup(keyboard)
context.bot.send_message(chat_id=update.inline_query.from_user.id,text=verified_message_1, reply_markup=reply_markup)
return FIRST
def start_over(update: Update, context: CallbackContext) -> int:
query = update.callback_query
logger.info("User clicked on button %s", query.data)
SellerUserName: str = update.inline_query.from_user.username
buyer_username = query
print("buyer_username:" + buyer_username)
EmojiCode: str = emojihash.eh1("unique password" + SellerUserName + str(update.inline_query.from_user.id), 5)
SellerUserName: str = update.inline_query.from_user.username
verified_message_1 = f"""message 1 """
query.answer()
keyboard = [
[
InlineKeyboardButton(EmojiCode, callback_data=str(ONE)),
],
[
InlineKeyboardButton(Emojihash, callback_data=str(TWO)),
],
]
reply_markup = InlineKeyboardMarkup(keyboard)
context.bot.send_message(chat_id=update.inline_query.from_user.id,text=verified_message_1, reply_markup=reply_markup)
return FIRST
def one(update: Update, context: CallbackContext) -> int:
query = update.callback_query
logger.info("User clicked on button %s", query.data)
query.answer()
keyboard = [
[
InlineKeyboardButton(EmojiCode, callback_data=str(THREE)),
],
[
InlineKeyboardButton(Emojihash, callback_data=str(TWO)),
],
]
reply_markup = InlineKeyboardMarkup(keyboard)
query.edit_message_text(
text=verified_message_2, reply_markup=reply_markup
)
return FIRST
def two(update: Update, context: CallbackContext) -> int:
query = update.callback_query
logger.info("User clicked on button %s", query.data)
query.answer()
keyboard = [
[
InlineKeyboardButton("Yes", callback_data=str(ONE)),
],
[
InlineKeyboardButton("No", callback_data=str(TWO)),
],
]
reply_markup = InlineKeyboardMarkup(keyboard)
query.edit_message_text(
text="You clicked on the wrong code. Do you want to try again?", reply_markup=reply_markup
)
return SECOND
def three(update: Update, context: CallbackContext) -> int:
query = update.callback_query
logger.info("User clicked on button %s", query.data)
buyer_username = query
SellerUserName: str = update.inline_query.from_user.username
print("buyer_username:" + buyer_username)
SellerUserName: str = update.inline_query.from_user.username
query.answer()
keyboard = [
[
InlineKeyboardButton(text='Yes', url=f'https://t.me/{SellerUserName}'),
],
[
InlineKeyboardButton("No", callback_data=str(TWO)),
],
[ InlineKeyboardButton("Read Again", callback_data=str(ONE)),
],
]
reply_markup = InlineKeyboardMarkup(keyboard)
query.edit_message_text(
text=f"""With this you have confirmed you read the messages above.
Go back to chat with seller?""", reply_markup=reply_markup
)
return SECOND
def end(update: Update, context: CallbackContext) -> int:
query = update.callback_query
logger.info("User clicked on button %s", query.data)
query.answer()
query.edit_message_text(text="Process stopped")
return ConversationHandler.END
def main() -> None:
"""Run the bot."""
updater = Updater("TOKEN")
dispatcher = updater.dispatcher
conv_handler = ConversationHandler(
entry_points=[CommandHandler('start', inlinequery)],
states={
FIRST: [
CallbackQueryHandler(one, pattern='^' + str(ONE) + '$'),
CallbackQueryHandler(two, pattern='^' + str(TWO) + '$'),
CallbackQueryHandler(three, pattern='^' + str(THREE) + '$'),
],
SECOND: [
CallbackQueryHandler(start_over, pattern='^' + str(ONE) + '$'),
CallbackQueryHandler(end, pattern='^' + str(TWO) + '$'),
],
},
fallbacks=[CommandHandler('start', inlinequery)],
)
# Add ConversationHandler to dispatcher that will be used for handling updates
dispatcher.add_handler(conv_handler)
dispatcher.add_handler(InlineQueryHandler(inlinequery))
# Start the Bot
updater.start_polling()
# Run the bot until you press Ctrl-C or the process receives SIGINT,
# SIGTERM or SIGABRT. This should be used most of the time, since
# start_polling() is non-blocking and will stop the bot gracefully.
updater.idle()
if __name__ == '__main__':
main()
I tried switching out the Command handler to be a InlineQueryHandler, but that didn't give any results
that requires the conversation to be started by using the /start command which is not what I want.
This is not the case - you can use any handler as entry point.
I tried switching out the Command handler to be a InlineQueryHandler, but that didn't give any results
This is one caveat here: The per_chat
setting of ConversationHandler
defaults to True
, but InlineQuery
are not linked to a chat_id
. If you set per_chat=False
, using an InlineQueryHandler
as entry point should work just fine. See also here for more info on what the per_*
settings do.
Disclaimer: I'm currently the maintainer of python-telegram-bot
.