conversationbot.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
 18
 19from telegram import __version__ as TG_VER
 20
 21try:
 22    from telegram import __version_info__
 23except ImportError:
 24    __version_info__ = (0, 0, 0, 0, 0)  # type: ignore[assignment]
 25
 26if __version_info__ < (20, 0, 0, "alpha", 1):
 27    raise RuntimeError(
 28        f"This example is not compatible with your current PTB version {TG_VER}. To view the "
 29        f"{TG_VER} version of this example, "
 30        f"visit https://docs.python-telegram-bot.org/en/v{TG_VER}/examples.html"
 31    )
 32from telegram import ReplyKeyboardMarkup, ReplyKeyboardRemove, Update
 33from telegram.ext import (
 34    Application,
 35    CommandHandler,
 36    ContextTypes,
 37    ConversationHandler,
 38    MessageHandler,
 39    filters,
 40)
 41
 42# Enable logging
 43logging.basicConfig(
 44    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
 45)
 46logger = logging.getLogger(__name__)
 47
 48GENDER, PHOTO, LOCATION, BIO = range(4)
 49
 50
 51async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
 52    """Starts the conversation and asks the user about their gender."""
 53    reply_keyboard = [["Boy", "Girl", "Other"]]
 54
 55    await update.message.reply_text(
 56        "Hi! My name is Professor Bot. I will hold a conversation with you. "
 57        "Send /cancel to stop talking to me.\n\n"
 58        "Are you a boy or a girl?",
 59        reply_markup=ReplyKeyboardMarkup(
 60            reply_keyboard, one_time_keyboard=True, input_field_placeholder="Boy or Girl?"
 61        ),
 62    )
 63
 64    return GENDER
 65
 66
 67async def gender(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
 68    """Stores the selected gender and asks for a photo."""
 69    user = update.message.from_user
 70    logger.info("Gender of %s: %s", user.first_name, update.message.text)
 71    await update.message.reply_text(
 72        "I see! Please send me a photo of yourself, "
 73        "so I know what you look like, or send /skip if you don't want to.",
 74        reply_markup=ReplyKeyboardRemove(),
 75    )
 76
 77    return PHOTO
 78
 79
 80async def photo(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
 81    """Stores the photo and asks for a location."""
 82    user = update.message.from_user
 83    photo_file = await update.message.photo[-1].get_file()
 84    await photo_file.download("user_photo.jpg")
 85    logger.info("Photo of %s: %s", user.first_name, "user_photo.jpg")
 86    await update.message.reply_text(
 87        "Gorgeous! Now, send me your location please, or send /skip if you don't want to."
 88    )
 89
 90    return LOCATION
 91
 92
 93async def skip_photo(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
 94    """Skips the photo and asks for a location."""
 95    user = update.message.from_user
 96    logger.info("User %s did not send a photo.", user.first_name)
 97    await update.message.reply_text(
 98        "I bet you look great! Now, send me your location please, or send /skip."
 99    )
100
101    return LOCATION
102
103
104async def location(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
105    """Stores the location and asks for some info about the user."""
106    user = update.message.from_user
107    user_location = update.message.location
108    logger.info(
109        "Location of %s: %f / %f", user.first_name, user_location.latitude, user_location.longitude
110    )
111    await update.message.reply_text(
112        "Maybe I can visit you sometime! At last, tell me something about yourself."
113    )
114
115    return BIO
116
117
118async def skip_location(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
119    """Skips the location and asks for info about the user."""
120    user = update.message.from_user
121    logger.info("User %s did not send a location.", user.first_name)
122    await update.message.reply_text(
123        "You seem a bit paranoid! At last, tell me something about yourself."
124    )
125
126    return BIO
127
128
129async def bio(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
130    """Stores the info about the user and ends the conversation."""
131    user = update.message.from_user
132    logger.info("Bio of %s: %s", user.first_name, update.message.text)
133    await update.message.reply_text("Thank you! I hope we can talk again some day.")
134
135    return ConversationHandler.END
136
137
138async def cancel(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
139    """Cancels and ends the conversation."""
140    user = update.message.from_user
141    logger.info("User %s canceled the conversation.", user.first_name)
142    await update.message.reply_text(
143        "Bye! I hope we can talk again some day.", reply_markup=ReplyKeyboardRemove()
144    )
145
146    return ConversationHandler.END
147
148
149def main() -> None:
150    """Run the bot."""
151    # Create the Application and pass it your bot's token.
152    application = Application.builder().token("TOKEN").build()
153
154    # Add conversation handler with the states GENDER, PHOTO, LOCATION and BIO
155    conv_handler = ConversationHandler(
156        entry_points=[CommandHandler("start", start)],
157        states={
158            GENDER: [MessageHandler(filters.Regex("^(Boy|Girl|Other)$"), gender)],
159            PHOTO: [MessageHandler(filters.PHOTO, photo), CommandHandler("skip", skip_photo)],
160            LOCATION: [
161                MessageHandler(filters.LOCATION, location),
162                CommandHandler("skip", skip_location),
163            ],
164            BIO: [MessageHandler(filters.TEXT & ~filters.COMMAND, bio)],
165        },
166        fallbacks=[CommandHandler("cancel", cancel)],
167    )
168
169    application.add_handler(conv_handler)
170
171    # Run the bot until the user presses Ctrl-C
172    application.run_polling()
173
174
175if __name__ == "__main__":
176    main()

State Diagram

_images/conversationbot.png