import os from typing import Optional from telegram import Bot, InlineKeyboardButton, InlineKeyboardMarkup, WebAppInfo from telegram.ext import Application, CommandHandler, MessageHandler, filters import httpx from .database import Database, User class QuixoticBot: def __init__(self, token: str, web_app_url: str, database: Database): self.token = token self.web_app_url = web_app_url self.db = database self.bot = Bot(token=token) self.application = Application.builder().token(token).build() self.setup_handlers() def setup_handlers(self): """Setup bot command and message handlers""" # Command handlers self.application.add_handler(CommandHandler("start", self.start_command)) self.application.add_handler(CommandHandler("help", self.help_command)) # Message handlers self.application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, self.handle_message)) async def start_command(self, update, context): """Handle /start command""" user = update.effective_user chat_id = update.effective_chat.id # Save or update user in database try: db_user = await self.db.get_user_by_telegram_id(str(user.id)) if not db_user: await self.db.add_user( telegram_id=str(user.id), username=user.username, first_name=user.first_name, last_name=user.last_name, language_code=user.language_code ) except Exception as e: print(f"Database error in start command: {e}") # Create inline keyboard with web app button keyboard = [ [InlineKeyboardButton( "🎵 Open Quixotic", web_app=WebAppInfo(url=self.web_app_url) )] ] reply_markup = InlineKeyboardMarkup(keyboard) welcome_text = ( f"👋 Welcome to Quixotic, {user.first_name}!\n\n" "🎵 Search and download music from SoundCloud\n" "🚀 Fast MP3 conversion\n" "📱 Easy-to-use interface\n\n" "Click the button below to get started!" ) await context.bot.send_message( chat_id=chat_id, text=welcome_text, reply_markup=reply_markup ) async def help_command(self, update, context): """Handle /help command""" help_text = ( "🎵 *Quixotic Bot Help*\n\n" "*Commands:*\n" "/start - Start the bot and open the music app\n" "/help - Show this help message\n\n" "*How to use:*\n" "1. Click 'Open Quixotic' to launch the web app\n" "2. Search for your favorite songs\n" "3. Convert and download as MP3\n" "4. Music files will be sent directly to this chat\n\n" "*Features:*\n" "• SoundCloud music search\n" "• High-quality MP3 conversion\n" "• Fast downloads\n" "• Search history tracking\n\n" "Enjoy your music! 🎶" ) await update.message.reply_text( help_text, parse_mode='Markdown' ) async def handle_message(self, update, context): """Handle text messages""" # For now, just respond with instructions to use the web app keyboard = [ [InlineKeyboardButton( "🎵 Open Quixotic", web_app=WebAppInfo(url=self.web_app_url) )] ] reply_markup = InlineKeyboardMarkup(keyboard) await update.message.reply_text( "Use the web app to search and download music! 🎵", reply_markup=reply_markup ) async def send_audio_file(self, chat_id: int, audio_url: str, title: str, performer: Optional[str] = None, thumbnail: Optional[str] = None): """Send audio file to user""" try: print(f"📤 Sending audio to chat {chat_id}") # Download the audio file first async with httpx.AsyncClient() as client: response = await client.get(audio_url) response.raise_for_status() audio_data = response.content # Send audio await self.bot.send_audio( chat_id=chat_id, audio=audio_data, title=title, performer=performer or "Unknown Artist", duration=None, # Let Telegram figure it out caption=f"🎵 {title}" + (f" by {performer}" if performer else ""), thumbnail=thumbnail if thumbnail else None ) print(f"✅ Audio sent successfully to chat {chat_id}") except Exception as e: print(f"❌ Failed to send audio to chat {chat_id}: {e}") # Send error message error_message = ( "❌ Failed to send audio file. " "The file might be too large or temporarily unavailable." ) try: await self.bot.send_message(chat_id=chat_id, text=error_message) except Exception as msg_error: print(f"❌ Failed to send error message: {msg_error}") raise e async def start_polling(self): """Start bot polling""" print("🤖 Starting bot polling...") await self.application.initialize() await self.application.start() await self.application.updater.start_polling() async def close(self): """Close bot application""" if self.application: await self.application.stop() await self.application.shutdown() print("🤖 Bot closed")