82 lines
1.8 KiB
Go
82 lines
1.8 KiB
Go
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
|
|
}
|