Explorar el Código

Refactor TradingCommands and management command handling - Simplified the TradingCommands initialization by removing unnecessary parameters. Enhanced the custom keyboard feature to dynamically generate button layouts based on configuration, improving user interaction and feedback during command execution.

Carles Sentis hace 5 días
padre
commit
994d8f035a
Se han modificado 3 ficheros con 39 adiciones y 120 borrados
  1. 1 8
      src/bot/core.py
  2. 30 14
      src/commands/management_commands.py
  3. 8 98
      src/commands/trading_commands.py

+ 1 - 8
src/bot/core.py

@@ -38,16 +38,9 @@ class TelegramTradingBot:
         self.market_monitor.set_notification_manager(self.notification_manager)
         
         # Initialize command handlers
+        self.trading_commands = TradingCommands(self.trading_engine, self.notification_manager)
         self.info_commands = InfoCommands(self.trading_engine)
         self.management_commands = ManagementCommands(self.trading_engine, self.market_monitor)
-        # Pass info_commands, management_commands, and the bot instance (self) to TradingCommands
-        self.trading_commands = TradingCommands(
-            self.trading_engine, 
-            self.notification_manager, 
-            self.info_commands, 
-            self.management_commands,
-            self # Pass the TelegramTradingBot instance itself
-        )
         
     def is_authorized(self, chat_id: str) -> bool:
         """Check if the chat ID is authorized to use the bot."""

+ 30 - 14
src/commands/management_commands.py

@@ -471,25 +471,41 @@ Will trigger when {token} price moves {alarm['direction']} ${target_price:,.2f}
             await context.bot.send_message(chat_id=chat_id, text="❌ Unauthorized access.")
             return
         
-        # Check if custom keyboard is enabled in config
         keyboard_enabled = getattr(Config, 'TELEGRAM_CUSTOM_KEYBOARD_ENABLED', False)
-        
-        if keyboard_enabled:
-            # Create a simple reply keyboard with common commands
-            keyboard = [
-                [KeyboardButton("/balance"), KeyboardButton("/positions")],
-                [KeyboardButton("/orders"), KeyboardButton("/stats")],
-                [KeyboardButton("/daily"), KeyboardButton("/performance")],
-                [KeyboardButton("/help"), KeyboardButton("/commands")]
-            ]
+        keyboard_layout_str = getattr(Config, 'TELEGRAM_CUSTOM_KEYBOARD_LAYOUT', '')
+
+        if keyboard_enabled and keyboard_layout_str:
+            rows_str = keyboard_layout_str.split('|')
+            keyboard = []
+            button_details_message = ""
+            for row_str in rows_str:
+                button_commands = row_str.split(',')
+                row_buttons = []
+                for cmd_text in button_commands:
+                    cmd_text = cmd_text.strip()
+                    if cmd_text: # Ensure not empty after strip
+                        row_buttons.append(KeyboardButton(cmd_text))
+                        # For the message, show the command and a brief description if possible
+                        # This part would require a mapping from command to description
+                        # For now, just list the command text.
+                        button_details_message += f"• {cmd_text}\n"
+                if row_buttons:
+                    keyboard.append(row_buttons)
             
+            if not keyboard:
+                await context.bot.send_message(chat_id=chat_id, text="⚠️ Custom keyboard layout is empty or invalid in config.", parse_mode='HTML')
+                return
+
             reply_markup = ReplyKeyboardMarkup(
                 keyboard,
                 resize_keyboard=True,
-                one_time_keyboard=False,
+                one_time_keyboard=False, # Persistent keyboard
                 selective=True
             )
             
-            await context.bot.send_message(chat_id=chat_id, text="⌨️ <b>Custom Keyboard Activated!</b>\n\n🎯 <b>Your quick buttons are now ready:</b>\n• /balance - Account balance\n• /positions - Open positions\n• /orders - Active orders\n• /stats - Trading statistics\n• /daily - Daily performance\n• /performance - Performance stats\n• /help - Help guide\n• /commands - Command menu\n\n💡 <b>How to use:</b>\nTap any button below to send the command instantly!\n\n🔧 These buttons will stay at the bottom of your chat.", reply_markup=reply_markup, parse_mode='HTML')
-        else:
-            await context.bot.send_message(chat_id=chat_id, text="❌ <b>Custom Keyboard Disabled</b>\n\n🔧 <b>To enable:</b>\n• Set TELEGRAM_CUSTOM_KEYBOARD_ENABLED=true in your .env file\n• Restart the bot\n• Run /keyboard again\n\n📋 <b>Current config:</b>\n• Enabled: {keyboard_enabled}\n• Layout: {getattr(Config, 'TELEGRAM_CUSTOM_KEYBOARD_LAYOUT', 'default')}", parse_mode='HTML') 
+            message_text = f"⌨️ <b>Custom Keyboard Activated!</b>\n\n🎯 <b>Your quick buttons based on config are ready:</b>\n{button_details_message}\n💡 <b>How to use:</b>\nTap any button below to send the command instantly!\n\n🔧 These buttons will stay at the bottom of your chat."
+            await context.bot.send_message(chat_id=chat_id, text=message_text, reply_markup=reply_markup, parse_mode='HTML')
+        elif not keyboard_enabled:
+            await context.bot.send_message(chat_id=chat_id, text="❌ <b>Custom Keyboard Disabled</b>\n\n🔧 <b>To enable:</b>\n• Set TELEGRAM_CUSTOM_KEYBOARD_ENABLED=true in your .env file\n• Restart the bot\n• Run /keyboard again\n\n📋 <b>Current config:</b>\n• Enabled: {keyboard_enabled}", parse_mode='HTML')
+        else: # keyboard_enabled is true, but layout_str is empty
+            await context.bot.send_message(chat_id=chat_id, text="⚠️ Custom keyboard is enabled but the layout string (TELEGRAM_CUSTOM_KEYBOARD_LAYOUT) is empty in your configuration. Please define a layout.", parse_mode='HTML') 

+ 8 - 98
src/commands/trading_commands.py

@@ -15,13 +15,10 @@ logger = logging.getLogger(__name__)
 class TradingCommands:
     """Handles all trading-related Telegram commands."""
     
-    def __init__(self, trading_engine, notification_manager, info_commands_instance, management_commands_instance, telegram_bot_instance):
-        """Initialize with trading engine, notification manager, and other command instances."""
+    def __init__(self, trading_engine, notification_manager):
+        """Initialize with trading engine and notification manager."""
         self.trading_engine = trading_engine
         self.notification_manager = notification_manager
-        self.info_commands_instance = info_commands_instance
-        self.management_commands_instance = management_commands_instance
-        self.telegram_bot_instance = telegram_bot_instance
     
     def _is_authorized(self, chat_id: str) -> bool:
         """Check if the chat ID is authorized."""
@@ -475,7 +472,7 @@ This will place a limit {exit_side} order at ${stop_price:,.2f} to protect your
             elif position_type == "SHORT" and tp_price >= entry_price:
                 await context.bot.send_message(chat_id=chat_id, text=(
                     f"⚠️ Take profit price should be BELOW entry price for short positions\n\n"
-                    f"📊 Your {token} SHORT position:\n"
+                    f"�� Your {token} SHORT position:\n"
                     f"• Entry Price: ${entry_price:,.2f}\n"
                     f"• Take Profit: ${tp_price:,.2f} ❌\n\n"
                     f"💡 Try a lower price like: /tp {token} {entry_price * 0.95:.0f}"
@@ -592,41 +589,6 @@ This action cannot be undone.
         
         callback_data = query.data
         
-        # Prepare a dictionary to map callback_data to command handlers
-        # The command handlers expect an Update object and a Context object
-        
-        # Create a mock Update object based on the query for command handlers
-        # The command handlers usually expect update.message to be present
-        # and context.args for commands that take arguments (not needed for these simple buttons)
-        
-        # Command map for info and management commands
-        # These commands are simple and don't typically require args from buttons
-        command_map = {
-            "balance": self.info_commands_instance.balance_command,
-            "positions": self.info_commands_instance.positions_command,
-            "orders": self.info_commands_instance.orders_command,
-            "stats": self.info_commands_instance.stats_command,
-            "trades": self.info_commands_instance.trades_command,
-            "market": self.info_commands_instance.market_command,
-            "price": self.info_commands_instance.price_command,
-            "performance": self.info_commands_instance.performance_command,
-            "daily": self.info_commands_instance.daily_command,
-            "weekly": self.info_commands_instance.weekly_command,
-            "monthly": self.info_commands_instance.monthly_command,
-            "risk": self.info_commands_instance.risk_command,
-            "balance_adjustments": self.info_commands_instance.balance_adjustments_command,
-            "commands": self.info_commands_instance.commands_command,
-            # Management commands
-            "monitoring": self.management_commands_instance.monitoring_command,
-            "alarm": self.management_commands_instance.alarm_command,
-            "logs": self.management_commands_instance.logs_command,
-            "debug": self.management_commands_instance.debug_command,
-            "version": self.management_commands_instance.version_command,
-            "keyboard": self.management_commands_instance.keyboard_command,
-            # Help command handled by the main bot instance
-            "help": self.telegram_bot_instance.help_command
-        }
-
         try:
             if callback_data.startswith('confirm_long_'):
                 await self._execute_long_callback(query, callback_data)
@@ -643,63 +605,11 @@ This action cannot be undone.
             elif callback_data == 'cancel_order':
                 await query.edit_message_text("❌ Order cancelled.")
             
-            # Handle info and management command button callbacks by direct execution
-            elif callback_data in command_map:
-                # For these simple button commands, context.args is typically empty.
-                # The original message (query.message) is used as the basis for the Update object.
-                # Some commands might expect `update.effective_chat.id` or similar from the Update object.
-                # query.message should provide `chat.id`.
-                # We need to ensure `context.args` is empty or not accessed by these simple commands.
-                # Most of these commands (e.g. balance, positions) don't use context.args when called via slash commands without args.
-                
-                # Create a new Update object that the command handlers expect.
-                # The crucial part is that `update.message` (or `update.effective_message`) is available.
-                # And `update.effective_chat.id` can be derived.
-                # For commands that might send a new message, they need context.bot.
-                
-                # Edit the message to indicate the action is being processed, then call the command.
-                # This provides immediate feedback to the user.
-                button_text = query.message.reply_markup.inline_keyboard[0][0].text # Attempt to get button text for feedback
-                for row in query.message.reply_markup.inline_keyboard:
-                    for button in row:
-                        if button.callback_data == callback_data:
-                            button_text = button.text
-                            break
-                    if button_text != query.message.reply_markup.inline_keyboard[0][0].text: # check if found
-                        break
-                
-                await query.edit_message_text(f"⏳ Processing '{button_text}'...") # Feedback
-
-                # Construct an Update object that mimics one from a CommandHandler
-                # Ensure context.args is empty for these parameter-less commands
-                current_context_args = context.args
-                context.args = [] # Temporarily set args to empty for these button calls
-                
-                command_handler_update = Update(
-                    update_id=query.update_id, 
-                    message=query.message # Command handlers expect update.message or update.effective_message
-                    # callback_query=query # Some handlers might inspect this, but usually not for simple commands
-                )
-                # Ensure effective_chat is set correctly for the command handler
-                if command_handler_update.message:
-                     command_handler_update.effective_chat = command_handler_update.message.chat
-
-
-                await command_map[callback_data](command_handler_update, context)
-                
-                context.args = current_context_args # Restore context.args
-
-
-                # After the command executes, it might have sent its own messages.
-                # We might want to remove the "Processing..." message or the keyboard.
-                # For now, let's assume the command itself handles the output.
-                # Optionally, delete the "Processing..." message if the command sends a new one.
-                # await query.delete_message() # Or query.edit_message_text("✅ Done.") if command doesn't send a message.
-
-            else:
-                logger.warning(f"Unhandled callback_data: {callback_data}")
-                await query.edit_message_text(f"❓ Unknown action: {callback_data}. Please try again or use a command.")
-
+            # Handle info command button callbacks
+            elif callback_data in ['balance', 'positions', 'orders', 'stats', 'trades', 'market', 'price', 
+                                 'performance', 'daily', 'weekly', 'monthly', 'alarm', 'monitoring', 'logs', 'help']:
+                await query.edit_message_text(f"✅ Please use /{callback_data} command to get the latest data.")
+            
         except Exception as e:
             await query.edit_message_text(f"❌ Error processing order: {e}")
             logger.error(f"Error in button callback: {e}")