package main import ( "bytes" "encoding/json" "fmt" "log" "net/http" "time" ) // TelegramSender sends alerts to a Telegram chat via Bot API. type TelegramSender struct { botToken string chatID string client *http.Client enabled bool } // NewTelegramSender creates a sender. Returns a no-op sender if token or chatID is empty. func NewTelegramSender(botToken, chatID string) *TelegramSender { return &TelegramSender{ botToken: botToken, chatID: chatID, client: &http.Client{ Timeout: 10 * time.Second, Transport: &http.Transport{ Proxy: http.ProxyFromEnvironment, }, }, enabled: botToken != "" && chatID != "", } } // IsEnabled returns true if both token and chatID are configured. func (t *TelegramSender) IsEnabled() bool { return t.enabled } // SendAlert sends a formatted momentum alert to Telegram. func (t *TelegramSender) SendAlert(alert BinanceAlert) error { if !t.enabled { return nil } // Build message icon := "🔴" directionWord := "暴跌" if alert.Direction == "up" { icon = "🟢" directionWord = "暴涨" } sign := "+" if alert.ChangePct < 0 { sign = "" } text := fmt.Sprintf("%s *%s* %s %s%.2f%% | $%.4f", icon, alert.Coin, directionWord, sign, alert.ChangePct, alert.Price) payload := map[string]interface{}{ "chat_id": t.chatID, "text": text, "parse_mode": "Markdown", } body, _ := json.Marshal(payload) url := fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", t.botToken) resp, err := t.client.Post(url, "application/json", bytes.NewReader(body)) if err != nil { return fmt.Errorf("telegram API call failed: %w", err) } defer resp.Body.Close() if resp.StatusCode != 200 { return fmt.Errorf("telegram API returned %d", resp.StatusCode) } log.Printf("[Telegram] Alert sent: %s %s %.2f%%", alert.Coin, alert.Direction, alert.ChangePct) return nil }