Pārlūkot izejas kodu

Refactor Telegram bot to support v20.x style - Updated command handler setup to directly utilize the application for improved clarity and organization. Enhanced logging during bot initialization and shutdown processes, ensuring better traceability and user feedback. Adjusted comments and method calls to align with the new version, contributing to a more streamlined and reliable bot operation.

Carles Sentis 2 nedēļas atpakaļ
vecāks
revīzija
e73dcaa327
1 mainītis faili ar 71 papildinājumiem un 68 dzēšanām
  1. 71 68
      src/bot/core.py

+ 71 - 68
src/bot/core.py

@@ -8,7 +8,7 @@ import logging
 import telegram # Import telegram to check version
 import telegram # Import telegram to check version
 from datetime import datetime
 from datetime import datetime
 from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
 from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
-from telegram.ext import Application, ContextTypes, CommandHandler, CallbackQueryHandler, MessageHandler, filters, Updater
+from telegram.ext import Application, ContextTypes, CommandHandler, CallbackQueryHandler, MessageHandler, filters
 
 
 from src.config.config import Config
 from src.config.config import Config
 from src.trading.trading_engine import TradingEngine
 from src.trading.trading_engine import TradingEngine
@@ -65,47 +65,45 @@ class TelegramTradingBot:
         if not self.application:
         if not self.application:
             return
             return
         
         
-        dp = self.application.dispatcher # Get dispatcher for v13.x
+        # Basic bot commands (directly on application for v20.x)
-        
+        self.application.add_handler(CommandHandler("start", self.start_command))
-        # Basic bot commands
+        self.application.add_handler(CommandHandler("help", self.help_command))
-        dp.add_handler(CommandHandler("start", self.start_command))
-        dp.add_handler(CommandHandler("help", self.help_command))
         
         
         # Trading commands
         # Trading commands
-        dp.add_handler(CommandHandler("long", self.trading_commands.long_command))
+        self.application.add_handler(CommandHandler("long", self.trading_commands.long_command))
-        dp.add_handler(CommandHandler("short", self.trading_commands.short_command))
+        self.application.add_handler(CommandHandler("short", self.trading_commands.short_command))
-        dp.add_handler(CommandHandler("exit", self.trading_commands.exit_command))
+        self.application.add_handler(CommandHandler("exit", self.trading_commands.exit_command))
-        dp.add_handler(CommandHandler("sl", self.trading_commands.sl_command))
+        self.application.add_handler(CommandHandler("sl", self.trading_commands.sl_command))
-        dp.add_handler(CommandHandler("tp", self.trading_commands.tp_command))
+        self.application.add_handler(CommandHandler("tp", self.trading_commands.tp_command))
-        dp.add_handler(CommandHandler("coo", self.trading_commands.coo_command))
+        self.application.add_handler(CommandHandler("coo", self.trading_commands.coo_command))
         
         
         # Info commands
         # Info commands
-        dp.add_handler(CommandHandler("balance", self.info_commands.balance_command))
+        self.application.add_handler(CommandHandler("balance", self.info_commands.balance_command))
-        dp.add_handler(CommandHandler("positions", self.info_commands.positions_command))
+        self.application.add_handler(CommandHandler("positions", self.info_commands.positions_command))
-        dp.add_handler(CommandHandler("orders", self.info_commands.orders_command))
+        self.application.add_handler(CommandHandler("orders", self.info_commands.orders_command))
-        dp.add_handler(CommandHandler("stats", self.info_commands.stats_command))
+        self.application.add_handler(CommandHandler("stats", self.info_commands.stats_command))
-        dp.add_handler(CommandHandler("trades", self.info_commands.trades_command))
+        self.application.add_handler(CommandHandler("trades", self.info_commands.trades_command))
-        dp.add_handler(CommandHandler("market", self.info_commands.market_command))
+        self.application.add_handler(CommandHandler("market", self.info_commands.market_command))
-        dp.add_handler(CommandHandler("price", self.info_commands.price_command))
+        self.application.add_handler(CommandHandler("price", self.info_commands.price_command))
-        dp.add_handler(CommandHandler("performance", self.info_commands.performance_command))
+        self.application.add_handler(CommandHandler("performance", self.info_commands.performance_command))
-        dp.add_handler(CommandHandler("daily", self.info_commands.daily_command))
+        self.application.add_handler(CommandHandler("daily", self.info_commands.daily_command))
-        dp.add_handler(CommandHandler("weekly", self.info_commands.weekly_command))
+        self.application.add_handler(CommandHandler("weekly", self.info_commands.weekly_command))
-        dp.add_handler(CommandHandler("monthly", self.info_commands.monthly_command))
+        self.application.add_handler(CommandHandler("monthly", self.info_commands.monthly_command))
-        dp.add_handler(CommandHandler("risk", self.info_commands.risk_command))
+        self.application.add_handler(CommandHandler("risk", self.info_commands.risk_command))
-        dp.add_handler(CommandHandler("balance_adjustments", self.info_commands.balance_adjustments_command))
+        self.application.add_handler(CommandHandler("balance_adjustments", self.info_commands.balance_adjustments_command))
-        dp.add_handler(CommandHandler("commands", self.info_commands.commands_command))
+        self.application.add_handler(CommandHandler("commands", self.info_commands.commands_command))
-        dp.add_handler(CommandHandler("c", self.info_commands.commands_command))  # Alias
+        self.application.add_handler(CommandHandler("c", self.info_commands.commands_command))  # Alias
         
         
         # Management commands
         # Management commands
-        dp.add_handler(CommandHandler("monitoring", self.management_commands.monitoring_command))
+        self.application.add_handler(CommandHandler("monitoring", self.management_commands.monitoring_command))
-        dp.add_handler(CommandHandler("alarm", self.management_commands.alarm_command))
+        self.application.add_handler(CommandHandler("alarm", self.management_commands.alarm_command))
-        dp.add_handler(CommandHandler("logs", self.management_commands.logs_command))
+        self.application.add_handler(CommandHandler("logs", self.management_commands.logs_command))
-        dp.add_handler(CommandHandler("debug", self.management_commands.debug_command))
+        self.application.add_handler(CommandHandler("debug", self.management_commands.debug_command))
-        dp.add_handler(CommandHandler("version", self.management_commands.version_command))
+        self.application.add_handler(CommandHandler("version", self.management_commands.version_command))
-        dp.add_handler(CommandHandler("keyboard", self.management_commands.keyboard_command))
+        self.application.add_handler(CommandHandler("keyboard", self.management_commands.keyboard_command))
         
         
         # Callback and message handlers
         # Callback and message handlers
-        dp.add_handler(CallbackQueryHandler(self.trading_commands.button_callback))
+        self.application.add_handler(CallbackQueryHandler(self.trading_commands.button_callback))
         
         
     async def start_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
     async def start_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
         """Handle the /start command."""
         """Handle the /start command."""
@@ -278,7 +276,7 @@ For support or issues, check the logs or contact the administrator.
             logger.error(f"Error sending help message in /help: {e}")
             logger.error(f"Error sending help message in /help: {e}")
     
     
     async def run(self):
     async def run(self):
-        """Run the Telegram bot (v13.x compatible)."""
+        """Run the Telegram bot with manual initialization and shutdown (v20.x style)."""
         if not Config.TELEGRAM_BOT_TOKEN:
         if not Config.TELEGRAM_BOT_TOKEN:
             logger.error("❌ TELEGRAM_BOT_TOKEN not configured")
             logger.error("❌ TELEGRAM_BOT_TOKEN not configured")
             return
             return
@@ -287,67 +285,72 @@ For support or issues, check the logs or contact the administrator.
             logger.error("❌ TELEGRAM_CHAT_ID not configured")
             logger.error("❌ TELEGRAM_CHAT_ID not configured")
             return
             return
         
         
-        logger.info(f"🔧 Using python-telegram-bot version: {telegram.__version__} (Running in v13.x compatibility mode)")
+        logger.info(f"🔧 Using python-telegram-bot version: {telegram.__version__} (Running in v20.x style)")
 
 
-        # Create Updater for v13.x compatibility
+        # Create application
-        # self.application is already initialized as Updater in __init__ if we move it there, or here.
+        self.application = Application.builder().token(Config.TELEGRAM_BOT_TOKEN).drop_pending_updates(True).build()
-        # For consistency, let's ensure it's initialized if not already.
-        if not isinstance(self.application, Updater):
-            self.application = Updater(token=Config.TELEGRAM_BOT_TOKEN, use_context=True)
         
         
         # Connect notification manager to the bot application
         # Connect notification manager to the bot application
         self.notification_manager.set_bot_application(self.application)
         self.notification_manager.set_bot_application(self.application)
         
         
-        # Set up handlers (handlers are added to dispatcher inside setup_handlers)
+        # Set up handlers
         self.setup_handlers()
         self.setup_handlers()
 
 
+        keep_running_future = asyncio.Future() # Future to keep the bot alive
+
         try:
         try:
-            logger.info(f"🚀 Starting Telegram trading bot v{self.version} (v13.x mode)...")
+            logger.info("🚀 Initializing bot application (v20.x style)...")
+            await self.application.initialize()
+            
+            logger.info(f"🚀 Starting Telegram trading bot v{self.version} (v20.x style)...")
             
             
-            # Send startup notification (await is fine, send_message is async)
             await self.send_message(
             await self.send_message(
-                f"🤖 <b>Manual Trading Bot v{self.version} Started (v13.x mode)</b>\n\n"
+                f"🤖 <b>Manual Trading Bot v{self.version} Started (v20.x style)</b>\n\n"
                 f"✅ Connected to Hyperliquid {'Testnet' if Config.HYPERLIQUID_TESTNET else 'Mainnet'}\n"
                 f"✅ Connected to Hyperliquid {'Testnet' if Config.HYPERLIQUID_TESTNET else 'Mainnet'}\n"
                 f"📊 Default Symbol: {Config.DEFAULT_TRADING_TOKEN}\n"
                 f"📊 Default Symbol: {Config.DEFAULT_TRADING_TOKEN}\n"
                 f"🔄 All systems ready!\n\n"
                 f"🔄 All systems ready!\n\n"
                 "Use /start for quick actions or /help for all commands."
                 "Use /start for quick actions or /help for all commands."
             )
             )
             
             
-            # Start subsystems (market_monitor needs to be started as async)
-            # For v13, typically MarketMonitor might not be started with await here unless it's run in a separate async loop
-            # or if start_polling itself is managed within an asyncio context by the main script.
-            # Given trading_bot.py uses asyncio.run(), this should be okay.
             await self.market_monitor.start()
             await self.market_monitor.start()
             
             
-            logger.info("🔄 Starting PTB Updater polling (v13.x mode)...")
+            logger.info("▶️ Starting PTB application (handlers, dispatcher, polling...) (v20.x style)...")
-            self.application.start_polling(drop_pending_updates=True)
+            await self.application.start()
             
             
-            logger.info("Bot is now running. Waiting for signals to stop (e.g., Ctrl+C)...")
+            logger.info("Bot is now running. Awaiting external stop signal (e.g., Ctrl+C) (v20.x style)...")
-            self.application.idle() # This is blocking and handles signals
+            await keep_running_future
-            logger.info("PTB Updater has stopped/idled gracefully.")
                     
                     
         except (KeyboardInterrupt, SystemExit):
         except (KeyboardInterrupt, SystemExit):
-            logger.info("🛑 Bot run interrupted by user/system. Initiating shutdown...")
+            logger.info("🛑 Bot run interrupted by user/system. Initiating shutdown (v20.x style)...")
+            if not keep_running_future.done():
+                keep_running_future.set_exception(KeyboardInterrupt())
+        except asyncio.CancelledError:
+            logger.info("🛑 Bot run task cancelled. Initiating shutdown (v20.x style)...")
+            if not keep_running_future.done():
+                keep_running_future.cancel()
         except Exception as e:
         except Exception as e:
-            logger.error(f"❌ Unhandled error in bot run loop (v13.x mode): {e}", exc_info=True)
+            logger.error(f"❌ Unhandled error in bot run loop (v20.x style): {e}", exc_info=True)
+            if not keep_running_future.done():
+                keep_running_future.set_exception(e)
         finally:
         finally:
-            logger.info("🔌 Starting graceful shutdown sequence in TelegramTradingBot.run (v13.x mode)...")
+            logger.info("🔌 Starting graceful shutdown sequence in TelegramTradingBot.run (v20.x style)...")
             try:
             try:
                 logger.info("Stopping market monitor...")
                 logger.info("Stopping market monitor...")
                 await self.market_monitor.stop()
                 await self.market_monitor.stop()
                 logger.info("Market monitor stopped.")
                 logger.info("Market monitor stopped.")
 
 
-                if hasattr(self.application, 'running') and self.application.running:
+                if self.application:
-                    logger.info("Stopping PTB Updater (v13.x mode)...")
+                    logger.info("Attempting to stop PTB application (v20.x style)...")
-                    self.application.stop()
+                    await self.application.stop() 
-                    logger.info("PTB Updater stopped.")
+                    logger.info("PTB application stop attempted.")
-                elif isinstance(self.application, Updater):
+                    
-                    # If not `running` attribute, just call stop for Updater as a fallback.
+                    logger.info("Shutting down PTB application (v20.x style)...")
-                    logger.info("PTB Updater might not be running or attribute 'running' not found, attempting stop anyway...")
+                    await self.application.shutdown()
-                    self.application.stop()
+                    logger.info("PTB application shut down.")
-                    logger.info("PTB Updater stop attempted.")
+                else:
+                    logger.warning("Application object was None during shutdown in TelegramTradingBot.run.")
                 
                 
-                logger.info("✅ Graceful shutdown sequence in TelegramTradingBot.run (v13.x mode) complete.")
+                logger.info("✅ Graceful shutdown sequence in TelegramTradingBot.run (v20.x style) complete.")
                 
                 
             except Exception as e:
             except Exception as e:
-                logger.error(f"💥 Error during shutdown sequence in TelegramTradingBot.run (v13.x mode): {e}", exc_info=True) 
+                logger.error(f"💥 Error during shutdown sequence in TelegramTradingBot.run (v20.x style): {e}", exc_info=True)