rastof9 commited on
Commit
80b2539
·
verified ·
1 Parent(s): 257b93e

Update bot.py

Browse files
Files changed (1) hide show
  1. bot.py +140 -74
bot.py CHANGED
@@ -2,6 +2,7 @@
2
 
3
  import logging
4
  import os
 
5
  from telegram import Update, ReplyKeyboardMarkup, ReplyKeyboardRemove, InlineKeyboardButton, InlineKeyboardMarkup
6
  from telegram.ext import (
7
  Application,
@@ -22,122 +23,187 @@ logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(level
22
  logger = logging.getLogger(__name__)
23
 
24
  # --- State Definitions for Conversation ---
25
- # We define states to keep track of where the user is in the conversation.
26
- SELECTING_ACTION, SELECTING_STYLE, SELECTING_GENDER = range(3)
 
 
 
 
 
 
 
 
 
 
 
27
 
28
  # --- Initialize the Generation Service ---
29
- # This loads the heavy AI models into memory once when the bot starts.
30
  logger.info("Initializing Generation Service... This may take a while.")
31
  try:
32
  generation_service = GenerationService()
33
  logger.info("Generation Service initialized successfully.")
34
  except Exception as e:
35
  logger.error(f"FATAL: Could not initialize GenerationService. Bot cannot start. Error: {e}")
36
- # In a real deployment, you might want to exit or send an alert here.
37
  generation_service = None
38
 
39
 
40
- # --- Command Handlers ---
 
 
 
 
 
 
 
41
 
42
- async def start_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
43
- """
44
- Handles the /start command. Greets the user, creates their account in the DB if needed,
45
- and shows them their credit balance.
46
- """
47
  user = update.effective_user
48
  logger.info(f"User {user.id} ({user.username}) started the bot.")
49
-
50
- # Get or create user in the database
51
- db_user = database.get_or_create_user(user.id, user.username)
52
-
53
- if not db_user:
54
- await update.message.reply_text("😕 Sorry, there was a problem setting up your account. Please try again later.")
55
- return ConversationHandler.END
56
-
57
- credits = db_user.get('credits', 0)
58
 
59
  welcome_message = (
60
- f"👋 Welcome to the Magic Face Bot, {user.first_name}!\n\n"
61
- "I can transform your face into legendary characters using AI.\n\n"
62
- f"You currently have **{credits}** credits.\n\n"
63
- "To get started, just send me a clear photo of your face."
64
  )
65
-
66
  await update.message.reply_text(welcome_message, parse_mode='Markdown')
 
 
 
 
 
 
 
 
 
 
67
 
68
- # We don't enter a state here yet; we wait for the user to send a photo.
69
- return SELECTING_ACTION
 
 
 
 
 
 
 
 
70
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
 
72
- async def credits_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
73
- """
74
- Handles the /credits command. Shows the user their current credit balance.
75
- """
 
76
  user_id = update.effective_user.id
77
- credits = database.get_user_credits(user_id)
78
- await update.message.reply_text(f"You have **{credits}** credits remaining.", parse_mode='Markdown')
79
-
80
-
81
- async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
82
- """
83
- Handles the /help command.
84
- """
85
- help_text = (
86
- "**How to use the Magic Face Bot:**\n\n"
87
- "1. Send a clear, well-lit photo of your face.\n"
88
- "2. Choose a transformation style from the menu.\n"
89
- "3. Select your gender.\n"
90
- "4. Wait for the AI to work its magic!\n\n"
91
- "**Commands:**\n"
92
- "/start - Restart the bot.\n"
93
- "/credits - Check your credit balance.\n"
94
- "/help - Show this message."
95
- )
96
- await update.message.reply_text(help_text, parse_mode='Markdown')
97
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
 
99
  # --- Main Application ---
100
  def main() -> None:
101
- """
102
- Starts the bot.
103
- """
104
  if not generation_service:
105
  logger.error("Generation service not available. Bot is shutting down.")
106
  return
107
 
108
- # Create the Application and pass it your bot's token.
109
  application = Application.builder().token(config.TELEGRAM_TOKEN).build()
110
 
111
- # --- Conversation Handler (to be built out) ---
112
- # For now, we just have the entry point and basic commands.
113
  conv_handler = ConversationHandler(
114
- entry_points=[
115
- CommandHandler("start", start_command),
116
- # We will add a MessageHandler for photos here later
117
- ],
118
  states={
119
- SELECTING_ACTION: [
120
- # Handlers for when the user sends a photo will go here
121
- ],
122
- SELECTING_STYLE: [
123
- # Handlers for when the user chooses a style
124
- ],
125
- SELECTING_GENDER: [
126
- # Handlers for when the user chooses a gender
127
- ],
128
  },
129
- fallbacks=[CommandHandler("start", start_command)],
130
  )
131
 
 
132
  application.add_handler(conv_handler)
133
- application.add_handler(CommandHandler("help", help_command))
134
- application.add_handler(CommandHandler("credits", credits_command))
135
 
136
- # Run the bot until the user presses Ctrl-C
137
  logger.info("Bot is starting to poll for updates...")
138
  application.run_polling()
139
 
140
 
141
  if __name__ == "__main__":
142
  main()
143
-
 
2
 
3
  import logging
4
  import os
5
+ import traceback
6
  from telegram import Update, ReplyKeyboardMarkup, ReplyKeyboardRemove, InlineKeyboardButton, InlineKeyboardMarkup
7
  from telegram.ext import (
8
  Application,
 
23
  logger = logging.getLogger(__name__)
24
 
25
  # --- State Definitions for Conversation ---
26
+ SELECTING_STYLE, SELECTING_GENDER = range(2)
27
+
28
+ # --- Style Presets ---
29
+ # These are the options the user will see.
30
+ STYLE_PRESETS = [
31
+ {"title": "Mona Lisa", "prompt": "A mesmerizing portrait in the style of Leonardo da Vinci's Mona Lisa, renaissance oil painting, soft sfumato technique", "emoji": "🎨"},
32
+ {"title": "Iron Hero", "prompt": "Hyper realistic portrait as a high-tech superhero, wearing advanced metallic suit, arc reactor glow", "emoji": "🦾"},
33
+ {"title": "Van Gogh", "prompt": "Self-portrait in the style of Vincent van Gogh, bold brushstrokes, vibrant colors, post-impressionist style", "emoji": "🎨"},
34
+ {"title": "Jedi Master", "prompt": "Epic portrait as a Jedi Master from Star Wars, wearing traditional robes, holding a lightsaber", "emoji": "⚔️"},
35
+ {"title": "Greek God", "prompt": "Mythological portrait as a Greek God, wearing flowing robes, golden laurel wreath, Mount Olympus background", "emoji": "⚡"},
36
+ {"title": "Medieval Knight", "prompt": "Noble knight portrait, wearing ornate plate armor, holding a sword, castle background", "emoji": "🛡️"},
37
+ ]
38
+
39
 
40
  # --- Initialize the Generation Service ---
 
41
  logger.info("Initializing Generation Service... This may take a while.")
42
  try:
43
  generation_service = GenerationService()
44
  logger.info("Generation Service initialized successfully.")
45
  except Exception as e:
46
  logger.error(f"FATAL: Could not initialize GenerationService. Bot cannot start. Error: {e}")
 
47
  generation_service = None
48
 
49
 
50
+ # --- Helper Functions ---
51
+ def get_style_keyboard() -> InlineKeyboardMarkup:
52
+ """Creates the inline keyboard for style selection."""
53
+ keyboard = [
54
+ [InlineKeyboardButton(f"{style['emoji']} {style['title']}", callback_data=f"style_{i}")]
55
+ for i, style in enumerate(STYLE_PRESETS)
56
+ ]
57
+ return InlineKeyboardMarkup(keyboard)
58
 
59
+ # --- Conversation Handlers ---
60
+
61
+ async def start_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
62
+ """Handles the /start command."""
 
63
  user = update.effective_user
64
  logger.info(f"User {user.id} ({user.username}) started the bot.")
65
+ database.get_or_create_user(user.id, user.username)
66
+ credits = database.get_user_credits(user.id)
 
 
 
 
 
 
 
67
 
68
  welcome_message = (
69
+ f"👋 Welcome, {user.first_name}!\n\n"
70
+ "To transform your face, just send me a clear photo.\n\n"
71
+ f"You have **{credits}** credits."
 
72
  )
 
73
  await update.message.reply_text(welcome_message, parse_mode='Markdown')
74
+
75
+ async def photo_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
76
+ """Handles the user sending a photo."""
77
+ user = update.effective_user
78
+ photo_file = await update.message.photo[-1].get_file()
79
+
80
+ # Create a temporary directory to store the photo
81
+ temp_dir = "temp_user_photos"
82
+ os.makedirs(temp_dir, exist_ok=True)
83
+ file_path = os.path.join(temp_dir, f"{user.id}_{photo_file.file_unique_id}.jpg")
84
 
85
+ await photo_file.download_to_drive(file_path)
86
+ logger.info(f"Photo from user {user.id} saved to {file_path}")
87
+
88
+ context.user_data['photo_path'] = file_path
89
+
90
+ await update.message.reply_text(
91
+ "Great photo! Now, pick a style for your transformation:",
92
+ reply_markup=get_style_keyboard()
93
+ )
94
+ return SELECTING_STYLE
95
 
96
+ async def style_selected_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
97
+ """Handles the user selecting a style from the inline keyboard."""
98
+ query = update.callback_query
99
+ await query.answer() # Acknowledge the button press
100
+
101
+ style_index = int(query.data.split('_')[1])
102
+ selected_style = STYLE_PRESETS[style_index]
103
+
104
+ context.user_data['prompt'] = selected_style['prompt']
105
+
106
+ logger.info(f"User {update.effective_user.id} selected style: {selected_style['title']}")
107
+
108
+ keyboard = [
109
+ [
110
+ InlineKeyboardButton("♀️ Female", callback_data="gender_Female"),
111
+ InlineKeyboardButton("♂️ Male", callback_data="gender_Male"),
112
+ ]
113
+ ]
114
+ await query.edit_message_text(
115
+ text=f"Style selected: *{selected_style['title']}*.\n\nNow, please select your gender:",
116
+ reply_markup=InlineKeyboardMarkup(keyboard),
117
+ parse_mode='Markdown'
118
+ )
119
+ return SELECTING_GENDER
120
 
121
+ async def gender_selected_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
122
+ """Handles the user selecting a gender and triggers the final generation."""
123
+ query = update.callback_query
124
+ await query.answer()
125
+
126
  user_id = update.effective_user.id
127
+ gender = query.data.split('_')[1]
128
+ context.user_data['gender'] = gender
129
+
130
+ logger.info(f"User {user_id} selected gender: {gender}. Preparing for generation.")
131
+
132
+ # Check for credits before starting
133
+ if database.get_user_credits(user_id) <= 0:
134
+ await query.edit_message_text("Oh no! You're out of credits. Use /buy to get more.")
135
+ return ConversationHandler.END
 
 
 
 
 
 
 
 
 
 
 
136
 
137
+ await query.edit_message_text("✨ Magic in progress... Your masterpiece is being created. This can take several minutes!")
138
+
139
+ try:
140
+ # Get all data from context
141
+ photo_path = context.user_data['photo_path']
142
+ prompt = context.user_data['prompt']
143
+
144
+ # Call the generation service
145
+ image_url = generation_service.generate_magic_image(
146
+ face_images=[photo_path],
147
+ gender=gender,
148
+ prompt=prompt
149
+ )
150
+
151
+ if image_url:
152
+ logger.info(f"Successfully generated image for user {user_id}. URL: {image_url}")
153
+ database.deduct_credit(user_id)
154
+ new_credits = database.get_user_credits(user_id)
155
+
156
+ await context.bot.send_photo(
157
+ chat_id=user_id,
158
+ photo=image_url,
159
+ caption=f"Here is your masterpiece! ✨\n\nYou have {new_credits} credits left."
160
+ )
161
+ else:
162
+ logger.error(f"Image generation failed for user {user_id}.")
163
+ await context.bot.send_message(user_id, "😕 Something went wrong during the magic spell. Please try again with a different photo.")
164
+
165
+ except Exception as e:
166
+ logger.error(f"An error occurred in the generation process for user {user_id}: {e}")
167
+ traceback.print_exc()
168
+ await context.bot.send_message(user_id, "Sorry, a critical error occurred. The developer has been notified.")
169
+
170
+ finally:
171
+ # Clean up the temporary photo
172
+ if 'photo_path' in context.user_data and os.path.exists(context.user_data['photo_path']):
173
+ os.remove(context.user_data['photo_path'])
174
+
175
+ # End the conversation
176
+ return ConversationHandler.END
177
+
178
+ async def cancel_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
179
+ """Cancels and ends the conversation."""
180
+ await update.message.reply_text("Action cancelled.", reply_markup=ReplyKeyboardRemove())
181
+ return ConversationHandler.END
182
 
183
  # --- Main Application ---
184
  def main() -> None:
 
 
 
185
  if not generation_service:
186
  logger.error("Generation service not available. Bot is shutting down.")
187
  return
188
 
 
189
  application = Application.builder().token(config.TELEGRAM_TOKEN).build()
190
 
 
 
191
  conv_handler = ConversationHandler(
192
+ entry_points=[MessageHandler(filters.PHOTO, photo_handler)],
 
 
 
193
  states={
194
+ SELECTING_STYLE: [CallbackQueryHandler(style_selected_handler, pattern="^style_")],
195
+ SELECTING_GENDER: [CallbackQueryHandler(gender_selected_handler, pattern="^gender_")],
 
 
 
 
 
 
 
196
  },
197
+ fallbacks=[CommandHandler("start", start_command), CommandHandler("cancel", cancel_command)],
198
  )
199
 
200
+ application.add_handler(CommandHandler("start", start_command))
201
  application.add_handler(conv_handler)
202
+ # Other handlers can be added here
 
203
 
 
204
  logger.info("Bot is starting to poll for updates...")
205
  application.run_polling()
206
 
207
 
208
  if __name__ == "__main__":
209
  main()