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", 5):
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_to_memory("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¶
flowchart TB
%% Documentation: https://mermaid-js.github.io/mermaid/#/flowchart
A(("/start")):::entryPoint -->|Hi! My name is Professor Bot...| B((GENDER)):::state
B --> |"- Boy <br /> - Girl <br /> - Other"|C("(choice)"):::userInput
C --> |I see! Please send me a photo...| D((PHOTO)):::state
D --> E("/skip"):::userInput
D --> F("(photo)"):::userInput
E --> |I bet you look great!| G[\ /]:::userInput
F --> |Gorgeous!| G[\ /]
G --> |"Now, send me your location .."| H((LOCATION)):::state
H --> I("/skip"):::userInput
H --> J("(location)"):::userInput
I --> |You seem a bit paranoid!| K[\" "/]:::userInput
J --> |Maybe I can visit...| K
K --> |"Tell me about yourself..."| L(("BIO")):::state
L --> M("(text)"):::userInput
M --> |"Thanks and bye!"| End(("END")):::termination
classDef userInput fill:#2a5279, color:#ffffff, stroke:#ffffff
classDef state fill:#222222, color:#ffffff, stroke:#ffffff
classDef entryPoint fill:#009c11, stroke:#42FF57, color:#ffffff
classDef termination fill:#bb0007, stroke:#E60109, color:#ffffff