conversationbot2.py

  1#!/usr/bin/env python
  2# pylint: disable=unused-argument, wrong-import-position
  3# This program is dedicated to the public domain under the CC0 license.
  4
  5"""
  6First, a few callback functions are defined. Then, those functions are passed to
  7the Application and registered at their respective places.
  8Then, the bot is started and runs until we press Ctrl-C on the command line.
  9
 10Usage:
 11Example of a bot-user conversation using ConversationHandler.
 12Send /start to initiate the conversation.
 13Press Ctrl-C on the command line or send a signal to the process to stop the
 14bot.
 15"""
 16
 17import logging
 18from typing import Dict
 19
 20from telegram import __version__ as TG_VER
 21
 22try:
 23    from telegram import __version_info__
 24except ImportError:
 25    __version_info__ = (0, 0, 0, 0, 0)  # type: ignore[assignment]
 26
 27if __version_info__ < (20, 0, 0, "alpha", 1):
 28    raise RuntimeError(
 29        f"This example is not compatible with your current PTB version {TG_VER}. To view the "
 30        f"{TG_VER} version of this example, "
 31        f"visit https://docs.python-telegram-bot.org/en/v{TG_VER}/examples.html"
 32    )
 33from telegram import ReplyKeyboardMarkup, ReplyKeyboardRemove, Update
 34from telegram.ext import (
 35    Application,
 36    CommandHandler,
 37    ContextTypes,
 38    ConversationHandler,
 39    MessageHandler,
 40    filters,
 41)
 42
 43# Enable logging
 44logging.basicConfig(
 45    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
 46)
 47logger = logging.getLogger(__name__)
 48
 49CHOOSING, TYPING_REPLY, TYPING_CHOICE = range(3)
 50
 51reply_keyboard = [
 52    ["Age", "Favourite colour"],
 53    ["Number of siblings", "Something else..."],
 54    ["Done"],
 55]
 56markup = ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True)
 57
 58
 59def facts_to_str(user_data: Dict[str, str]) -> str:
 60    """Helper function for formatting the gathered user info."""
 61    facts = [f"{key} - {value}" for key, value in user_data.items()]
 62    return "\n".join(facts).join(["\n", "\n"])
 63
 64
 65async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
 66    """Start the conversation and ask user for input."""
 67    await update.message.reply_text(
 68        "Hi! My name is Doctor Botter. I will hold a more complex conversation with you. "
 69        "Why don't you tell me something about yourself?",
 70        reply_markup=markup,
 71    )
 72
 73    return CHOOSING
 74
 75
 76async def regular_choice(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
 77    """Ask the user for info about the selected predefined choice."""
 78    text = update.message.text
 79    context.user_data["choice"] = text
 80    await update.message.reply_text(f"Your {text.lower()}? Yes, I would love to hear about that!")
 81
 82    return TYPING_REPLY
 83
 84
 85async def custom_choice(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
 86    """Ask the user for a description of a custom category."""
 87    await update.message.reply_text(
 88        'Alright, please send me the category first, for example "Most impressive skill"'
 89    )
 90
 91    return TYPING_CHOICE
 92
 93
 94async def received_information(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
 95    """Store info provided by user and ask for the next category."""
 96    user_data = context.user_data
 97    text = update.message.text
 98    category = user_data["choice"]
 99    user_data[category] = text
100    del user_data["choice"]
101
102    await update.message.reply_text(
103        "Neat! Just so you know, this is what you already told me:"
104        f"{facts_to_str(user_data)}You can tell me more, or change your opinion"
105        " on something.",
106        reply_markup=markup,
107    )
108
109    return CHOOSING
110
111
112async def done(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
113    """Display the gathered info and end the conversation."""
114    user_data = context.user_data
115    if "choice" in user_data:
116        del user_data["choice"]
117
118    await update.message.reply_text(
119        f"I learned these facts about you: {facts_to_str(user_data)}Until next time!",
120        reply_markup=ReplyKeyboardRemove(),
121    )
122
123    user_data.clear()
124    return ConversationHandler.END
125
126
127def main() -> None:
128    """Run the bot."""
129    # Create the Application and pass it your bot's token.
130    application = Application.builder().token("TOKEN").build()
131
132    # Add conversation handler with the states CHOOSING, TYPING_CHOICE and TYPING_REPLY
133    conv_handler = ConversationHandler(
134        entry_points=[CommandHandler("start", start)],
135        states={
136            CHOOSING: [
137                MessageHandler(
138                    filters.Regex("^(Age|Favourite colour|Number of siblings)$"), regular_choice
139                ),
140                MessageHandler(filters.Regex("^Something else...$"), custom_choice),
141            ],
142            TYPING_CHOICE: [
143                MessageHandler(
144                    filters.TEXT & ~(filters.COMMAND | filters.Regex("^Done$")), regular_choice
145                )
146            ],
147            TYPING_REPLY: [
148                MessageHandler(
149                    filters.TEXT & ~(filters.COMMAND | filters.Regex("^Done$")),
150                    received_information,
151                )
152            ],
153        },
154        fallbacks=[MessageHandler(filters.Regex("^Done$"), done)],
155    )
156
157    application.add_handler(conv_handler)
158
159    # Run the bot until the user presses Ctrl-C
160    application.run_polling()
161
162
163if __name__ == "__main__":
164    main()

State Diagram

_images/conversationbot2.png