Search code examples
pythonpython-telegram-bot

How to process user messages after bot action in telegram?


I have written a simple telegram bot in python with the python-telegram-bot library that rolls a n-sided die and returns the result to the user.

My code for this:

# Let's make a dice bot
import random
import time
from telegram.ext import Updater,CommandHandler,MessageHandler, Filters
from telegram import Update

updater = Updater(token='MY-TOKEN', use_context=True)
dispatcher = updater.dispatcher

# relevant function here:
def dice_roll(sides):
    roll = random.randint(1,sides)
    return roll

def dice(update, context):
    result = dice_roll(int(context.args[0]))
    lucky_dice = "You rolled a "+str(result)
    context.bot.send_message(chat_id=update.message.chat_id, text=lucky_dice)
dice_handler = CommandHandler('rollDice', dice)
dispatcher.add_handler(dice_handler)

# Polling function here:
updater.start_polling(clean=True)

What I'd like to improve

I do not really like the way users interact with the bot at the moment. To roll a die, the user has to define the number of sides of the die in the command argument, like this:

$user: /rollDice 6
$bot: You rolled a 5

This isn't really user-friendly, and it's also prone to errors if users add anything else to the argument or simply forget.

Instead, I'd like the bot to ask the user for the input explicitly, like this:

$user: /rollDice
$bot: Please enter the number of sides, the die should have
$user: 6
$bot: You rolled a 5

I looked into force_reply, but my main issue is that I do not know how to access a new update/message within a handler function.

If I try something like this:

def dice(update, context):
    context.bot.send_message(chat_id=update.message.chat_id, text="Please enter the number of sides, the die should have") # new line
    result = dice_roll(int(context.args[0]))
    lucky_dice = "You rolled a "+str(result)
    context.bot.send_message(chat_id=update.message.chat_id, text=lucky_dice)
dice_handler = CommandHandler('rollDice', dice)
dispatcher.add_handler(dice_handler)

it does not really work, because a) the bot does not really give the user time to reply (adding a sleepstatement here seems very wrong) and b) it refers back to the original "command message" and not any new message sent in the meantime.

Any help is appreciated.


Solution

  • You can use ConversationHandler to achieve this, I am using similar code for my bot which asks user the bus number and replies with timetable. Most of the code is borrowed from: https://codeclimate.com/github/leandrotoledo/python-telegram-bot/examples/conversationbot.py/source

    from telegram.ext import (Updater, CommandHandler, RegexHandler, ConversationHandler)
    import random
    
    # states
    ROLLDICE = range(1)
    
    def start(bot, update):
        update.message.reply_text('Please enter the number of sides, the die should have')
    
        return ROLLDICE
    
    def cancel(bot, update):
        update.message.reply_text('Bye! I hope we can talk again some day.')
        return ConversationHandler.END
    
    def rolldice(bot, update):
        roll = random.randint(1, int(update.message.text))
        update.message.reply_text('You rolled: ' + str(roll))
        return ConversationHandler.END
    
    def main():
        updater = Updater(TOKEN)
        dp = updater.dispatcher
    
        conv_handler = ConversationHandler(
            entry_points=[CommandHandler('start', start)],
            states={ROLLDICE: [RegexHandler('^[0-9]+$', rolldice)]},
            fallbacks=[CommandHandler('cancel', cancel)]
        )
    
        dp.add_handler(conv_handler)
        updater.start_polling()
    
        updater.idle()
    
    if __name__ == '__main__':
        main()
    

    Output:

    /start
    Please enter the number of sides, the die should have
    6
    You rolled: 2