/** * Telegram notification utilities * * Sends direct notifications to Telegram bot without requiring n8n */ interface TelegramNotificationOptions { symbol: string direction: 'long' | 'short' entryPrice: number exitPrice: number positionSize: number realizedPnL: number exitReason: string holdTimeSeconds: number maxDrawdown?: number maxGain?: number } interface TelegramWithdrawalOptions { type: 'withdrawal' amount: number signature: string availableProfit: number totalWithdrawn: number } type TelegramOptions = TelegramNotificationOptions | TelegramWithdrawalOptions /** * Send Telegram notification for position closure */ export async function sendPositionClosedNotification(options: TelegramNotificationOptions): Promise { try { const token = process.env.TELEGRAM_BOT_TOKEN const chatId = process.env.TELEGRAM_CHAT_ID if (!token || !chatId) { console.log('āš ļø Telegram credentials not configured, skipping notification') return } const profitEmoji = options.realizedPnL >= 0 ? 'šŸ’š' : 'šŸ”“' const exitReasonEmoji = getExitReasonEmoji(options.exitReason) const directionEmoji = options.direction === 'long' ? 'šŸ“ˆ' : 'šŸ“‰' const priceChange = ((options.exitPrice - options.entryPrice) / options.entryPrice * 100) * (options.direction === 'long' ? 1 : -1) const holdTime = formatHoldTime(options.holdTimeSeconds) const message = `${exitReasonEmoji} POSITION CLOSED ${directionEmoji} ${options.symbol} ${options.direction.toUpperCase()} šŸ’° P&L: $${options.realizedPnL.toFixed(2)} (${priceChange >= 0 ? '+' : ''}${priceChange.toFixed(2)}%) šŸ“Š Size: $${options.positionSize.toFixed(2)} šŸ“ Entry: $${options.entryPrice.toFixed(2)} šŸŽÆ Exit: $${options.exitPrice.toFixed(2)} ā± Hold Time: ${holdTime} šŸ”š Exit: ${options.exitReason.toUpperCase()} ${options.maxGain ? `\nšŸ“ˆ Max Gain: +${options.maxGain.toFixed(2)}%` : ''} ${options.maxDrawdown ? `\nšŸ“‰ Max Drawdown: -${options.maxDrawdown.toFixed(2)}%` : ''}` const url = `https://api.telegram.org/bot${token}/sendMessage` const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ chat_id: chatId, text: message, parse_mode: 'HTML' }) }) if (!response.ok) { const errorData = await response.json() console.error('āŒ Telegram notification failed:', errorData) } else { console.log('āœ… Telegram notification sent') } } catch (error) { console.error('āŒ Error sending Telegram notification:', error) // Don't throw - notification failure shouldn't break position closing } } /** * Send Telegram notification for smart validation events */ export async function sendValidationNotification(options: { event: 'queued' | 'confirmed' | 'abandoned' | 'expired' | 'executed' symbol: string direction: 'long' | 'short' originalPrice: number currentPrice?: number qualityScore: number validationTime?: number // seconds priceChange?: number // percentage }): Promise { try { const token = process.env.TELEGRAM_BOT_TOKEN const chatId = process.env.TELEGRAM_CHAT_ID if (!token || !chatId) { return } const directionEmoji = options.direction === 'long' ? 'šŸ“ˆ' : 'šŸ“‰' let message = '' switch (options.event) { case 'queued': message = `ā° SIGNAL QUEUED FOR VALIDATION ${directionEmoji} ${options.symbol} ${options.direction.toUpperCase()} šŸ“Š Quality Score: ${options.qualityScore}/100 šŸ“ Price: $${options.originalPrice.toFixed(2)} 🧠 Watching for price confirmation... āœ… Will enter if ${options.direction === 'long' ? '+0.3%' : '-0.3%'} āŒ Will abandon if ${options.direction === 'long' ? '-0.4%' : '+0.4%'}` break case 'confirmed': message = `āœ… SIGNAL VALIDATED - ENTERING NOW! ${directionEmoji} ${options.symbol} ${options.direction.toUpperCase()} šŸ’” Original: $${options.originalPrice.toFixed(2)} (quality ${options.qualityScore}) šŸŽÆ Confirmed: $${options.currentPrice?.toFixed(2)} (${options.priceChange! >= 0 ? '+' : ''}${options.priceChange?.toFixed(2)}%) ā± Validation Time: ${Math.floor(options.validationTime! / 60)}min ${Math.floor(options.validationTime! % 60)}s šŸš€ Executing trade now...` break case 'abandoned': message = `āŒ SIGNAL ABANDONED ${directionEmoji} ${options.symbol} ${options.direction.toUpperCase()} šŸ’” Original: $${options.originalPrice.toFixed(2)} (quality ${options.qualityScore}) āš ļø Current: $${options.currentPrice?.toFixed(2)} (${options.priceChange! >= 0 ? '+' : ''}${options.priceChange?.toFixed(2)}%) āœ… Saved from potential loser! ā± Monitored for ${Math.floor(options.validationTime! / 60)}min ${Math.floor(options.validationTime! % 60)}s` break case 'expired': message = `ā±ļø SIGNAL EXPIRED ${directionEmoji} ${options.symbol} ${options.direction.toUpperCase()} šŸ“Š Quality: ${options.qualityScore}/100 šŸ“ Original Price: $${options.originalPrice.toFixed(2)} ā° No confirmation after 10 minutes šŸ”„ Move wasn't strong enough` break case 'executed': message = `āœ… VALIDATED TRADE OPENED ${directionEmoji} ${options.symbol} ${options.direction.toUpperCase()} šŸ’” Original Signal: $${options.originalPrice.toFixed(2)} (quality ${options.qualityScore}) šŸŽÆ Entry: $${options.currentPrice?.toFixed(2)} šŸ“Š Slippage: ${options.priceChange?.toFixed(2)}% ā± Validation took ${Math.floor(options.validationTime! / 60)}min ${Math.floor(options.validationTime! % 60)}s` break } const url = `https://api.telegram.org/bot${token}/sendMessage` const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ chat_id: chatId, text: message, parse_mode: 'HTML' }) }) if (!response.ok) { console.error('āŒ Validation Telegram notification failed') } } catch (error) { console.error('āŒ Error sending validation notification:', error) } } /** * Get emoji for exit reason */ function getExitReasonEmoji(reason: string): string { const reasonUpper = reason.toUpperCase() if (reasonUpper.includes('TP1')) return 'šŸŽÆ' if (reasonUpper.includes('TP2')) return 'šŸŽÆšŸŽÆ' if (reasonUpper.includes('SL') || reasonUpper.includes('STOP')) return 'šŸ›‘' if (reasonUpper.includes('MANUAL')) return 'šŸ‘¤' if (reasonUpper.includes('EMERGENCY')) return '🚨' if (reasonUpper.includes('GHOST')) return 'šŸ‘»' return 'āœ…' } /** * Format hold time in human-readable format */ function formatHoldTime(seconds: number): string { const hours = Math.floor(seconds / 3600) const minutes = Math.floor((seconds % 3600) / 60) const secs = seconds % 60 if (hours > 0) { return `${hours}h ${minutes}m` } else if (minutes > 0) { return `${minutes}m ${secs}s` } else { return `${secs}s` } } /** * Send Telegram notification (supports both position closures and withdrawals) */ export async function sendTelegramNotification(options: TelegramOptions): Promise { if ('type' in options && options.type === 'withdrawal') { return sendWithdrawalNotification(options) } return sendPositionClosedNotification(options as TelegramNotificationOptions) } /** * Send withdrawal notification */ async function sendWithdrawalNotification(options: TelegramWithdrawalOptions): Promise { try { const token = process.env.TELEGRAM_BOT_TOKEN const chatId = process.env.TELEGRAM_CHAT_ID if (!token || !chatId) { console.log('āš ļø Telegram credentials not configured, skipping notification') return } const message = `šŸ’ø PROFIT WITHDRAWAL šŸ’° Amount: $${options.amount.toFixed(2)} USDC šŸ“Š Available Profit: $${options.availableProfit.toFixed(2)} šŸ“ˆ Total Withdrawn: $${options.totalWithdrawn.toFixed(2)} šŸ”— Transaction: ${options.signature.substring(0, 8)}... āœ… Funds sent to your wallet` const url = `https://api.telegram.org/bot${token}/sendMessage` const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ chat_id: chatId, text: message, parse_mode: 'HTML' }) }) if (!response.ok) { const errorData = await response.json() console.error('āŒ Telegram notification failed:', errorData) } else { console.log('āœ… Telegram withdrawal notification sent') } } catch (error) { console.error('āŒ Error sending Telegram notification:', error) } } /** * Send a generic text message to Telegram * Used for alerts, revenge blocks, system notifications */ export async function sendTelegramMessage(message: string): Promise { try { const token = process.env.TELEGRAM_BOT_TOKEN const chatId = process.env.TELEGRAM_CHAT_ID if (!token || !chatId) { console.log('āš ļø Telegram credentials not configured, skipping notification') return } const url = `https://api.telegram.org/bot${token}/sendMessage` const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ chat_id: chatId, text: message, parse_mode: 'HTML' }) }) if (!response.ok) { const errorData = await response.json() console.error('āŒ Telegram notification failed:', errorData) } } catch (error) { console.error('āŒ Error sending Telegram notification:', error) } }