#!/usr/bin/env python3 """ Telegram Trade Bot - SECURE Command-based Only responds to YOUR commands in YOUR chat """ import os import requests from telegram import Update from telegram.ext import Application, CommandHandler, ContextTypes # Configuration TELEGRAM_BOT_TOKEN = os.getenv('TELEGRAM_BOT_TOKEN') N8N_WEBHOOK_URL = os.getenv('N8N_WEBHOOK_URL') TRADING_BOT_URL = os.getenv('TRADING_BOT_URL', 'http://trading-bot-v4:3000') API_SECRET_KEY = os.getenv('API_SECRET_KEY', '') ALLOWED_CHAT_ID = int(os.getenv('TELEGRAM_CHAT_ID', '579304651')) async def status_command(update: Update, context: ContextTypes.DEFAULT_TYPE): """Handle /status command - show current open positions""" # Only process from YOUR chat if update.message.chat_id != ALLOWED_CHAT_ID: await update.message.reply_text("āŒ Unauthorized") return print(f"šŸ“Š /status command received", flush=True) try: # Fetch positions from trading bot API response = requests.get( f"{TRADING_BOT_URL}/api/trading/positions", headers={'Authorization': f'Bearer {API_SECRET_KEY}'}, timeout=10 ) print(f"šŸ“„ API Response: {response.status_code}", flush=True) if not response.ok: await update.message.reply_text(f"āŒ Error fetching positions: {response.status_code}") return data = response.json() if not data.get('success'): await update.message.reply_text("āŒ Failed to fetch positions") return # Check if there are active positions positions = data.get('positions', []) if not positions: await update.message.reply_text("šŸ“Š *No open positions*\n\nAll clear! Ready for new signals.", parse_mode='Markdown') return # Format position information for pos in positions: symbol = pos['symbol'] direction = pos['direction'].upper() entry = pos['entryPrice'] current = pos['currentPrice'] size = pos['currentSize'] leverage = pos['leverage'] # P&L pnl_pct = pos['profitPercent'] account_pnl = pos['accountPnL'] unrealized_pnl = pos['unrealizedPnL'] # Targets sl = pos['stopLoss'] tp1 = pos['takeProfit1'] tp2 = pos['takeProfit2'] tp1_hit = pos['tp1Hit'] # Age age_min = pos['ageMinutes'] # Build status message emoji = "🟢" if account_pnl > 0 else "šŸ”“" if account_pnl < 0 else "⚪" direction_emoji = "šŸ“ˆ" if direction == "LONG" else "šŸ“‰" message = f"{emoji} *{symbol}* {direction_emoji} {direction}\n\n" message += f"šŸ’° *P&L:* ${unrealized_pnl:.2f} ({account_pnl:+.2f}% account)\n" message += f"šŸ“Š *Price Change:* {pnl_pct:+.2f}%\n\n" message += f"*Entry:* ${entry:.4f}\n" message += f"*Current:* ${current:.4f}\n\n" message += f"*Targets:*\n" message += f" TP1: ${tp1:.4f} {'āœ…' if tp1_hit else 'ā³'}\n" message += f" TP2: ${tp2:.4f}\n" message += f" SL: ${sl:.4f}\n\n" message += f"*Position:* ${size:.2f} @ {leverage}x\n" message += f"*Age:* {age_min} min" await update.message.reply_text(message, parse_mode='Markdown') print(f"āœ… Status sent: {len(positions)} position(s)", flush=True) except Exception as e: print(f"āŒ Error: {e}", flush=True) await update.message.reply_text(f"āŒ Error: {str(e)}") async def trade_command(update: Update, context: ContextTypes.DEFAULT_TYPE): """Handle trade commands like /buySOL, /sellBTC, etc.""" # Only process from YOUR chat if update.message.chat_id != ALLOWED_CHAT_ID: await update.message.reply_text("āŒ Unauthorized") return # Extract command (remove the /) command = update.message.text[1:].lower() # e.g., "buysol" # Parse action and symbol if command.startswith('buy'): action = 'buy' symbol = command[3:] # e.g., "sol" elif command.startswith('sell'): action = 'sell' symbol = command[4:] # e.g., "btc" else: await update.message.reply_text("ā“ Unknown command") return message = f"{action} {symbol}" print(f"šŸ“Ø Command: {message}", flush=True) # Forward to n8n webhook - send as plain text body like TradingView does try: print(f"šŸ“¤ Sending: {message}", flush=True) response = requests.post( N8N_WEBHOOK_URL, data=message, # Plain text, not JSON headers={'Content-Type': 'text/plain'}, timeout=10 ) print(f"šŸ“„ Response status: {response.status_code}", flush=True) print(f"šŸ“„ Response body: {response.text[:200]}", flush=True) if response.ok: print(f"āœ… Sent: {message}", flush=True) await update.message.reply_text( f"šŸ¤– {action.upper()} {symbol.upper()}\n" f"āœ… Trade command sent!" ) else: print(f"āŒ Error: {response.status_code}", flush=True) await update.message.reply_text(f"āŒ Error: {response.status_code}") except Exception as e: print(f"āŒ Error: {e}", flush=True) await update.message.reply_text(f"āŒ Error: {str(e)}") def main(): """Start the bot""" print(f"šŸš€ Telegram Trade Bot Starting...", flush=True) print(f"šŸ“± Allowed Chat ID: {ALLOWED_CHAT_ID}", flush=True) print(f"šŸ”— Webhook: {N8N_WEBHOOK_URL}", flush=True) print(f"šŸ¤– Trading Bot: {TRADING_BOT_URL}", flush=True) print(f"\nāœ… Commands:", flush=True) print(f" /status - Show open positions", flush=True) print(f" /buySOL, /sellSOL", flush=True) print(f" /buyBTC, /sellBTC", flush=True) print(f" /buyETH, /sellETH", flush=True) # Create application application = Application.builder().token(TELEGRAM_BOT_TOKEN).build() # Add command handlers application.add_handler(CommandHandler("status", status_command)) application.add_handler(CommandHandler("buySOL", trade_command)) application.add_handler(CommandHandler("sellSOL", trade_command)) application.add_handler(CommandHandler("buyBTC", trade_command)) application.add_handler(CommandHandler("sellBTC", trade_command)) application.add_handler(CommandHandler("buyETH", trade_command)) application.add_handler(CommandHandler("sellETH", trade_command)) # Start polling print("\nšŸ¤– Bot ready! Send commands to your Telegram.\n", flush=True) application.run_polling(allowed_updates=Update.ALL_TYPES) if __name__ == '__main__': main()