mirror of
https://github.com/woodchen-ink/Q58Bot.git
synced 2025-07-18 13:52:07 +08:00
158 lines
3.5 KiB
Go
158 lines
3.5 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/adshao/go-binance/v2"
|
|
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
|
)
|
|
|
|
var (
|
|
botToken string
|
|
chatID int64
|
|
symbols []string
|
|
bot *tgbotapi.BotAPI
|
|
lastMsgID int
|
|
singaporeTZ *time.Location
|
|
)
|
|
|
|
func init() {
|
|
var err error
|
|
botToken = os.Getenv("BOT_TOKEN")
|
|
chatID = mustParseInt64(os.Getenv("CHAT_ID"))
|
|
|
|
symbolsRaw := strings.Split(os.Getenv("SYMBOLS"), ",")
|
|
symbols = make([]string, len(symbolsRaw))
|
|
for i, s := range symbolsRaw {
|
|
symbols[i] = strings.ReplaceAll(s, "/", "")
|
|
}
|
|
|
|
// 初始化 singaporeTZ
|
|
singaporeTZ = time.FixedZone("Asia/Singapore", 8*60*60) // UTC+8
|
|
|
|
bot, err = tgbotapi.NewBotAPI(botToken)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func mustParseInt64(s string) int64 {
|
|
i, err := strconv.ParseInt(s, 10, 64)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
return i
|
|
}
|
|
|
|
type tickerInfo struct {
|
|
symbol string
|
|
last float64
|
|
changePercent float64
|
|
}
|
|
|
|
func getTickerInfo(symbol string) (tickerInfo, error) {
|
|
client := binance.NewClient("", "")
|
|
|
|
// 获取当前价格
|
|
ticker, err := client.NewListPricesService().Symbol(symbol).Do(context.Background())
|
|
if err != nil {
|
|
return tickerInfo{}, err
|
|
}
|
|
if len(ticker) == 0 {
|
|
return tickerInfo{}, fmt.Errorf("no ticker found for symbol %s", symbol)
|
|
}
|
|
// 在 getTickerInfo 函数中
|
|
last, err := strconv.ParseFloat(ticker[0].Price, 64)
|
|
if err != nil {
|
|
return tickerInfo{}, err
|
|
}
|
|
|
|
// 获取24小时价格变化
|
|
stats, err := client.NewListPriceChangeStatsService().Symbol(symbol).Do(context.Background())
|
|
if err != nil {
|
|
return tickerInfo{}, err
|
|
}
|
|
if len(stats) == 0 {
|
|
return tickerInfo{}, fmt.Errorf("no price change stats found for symbol %s", symbol)
|
|
}
|
|
changePercent, err := strconv.ParseFloat(stats[0].PriceChangePercent, 64)
|
|
if err != nil {
|
|
return tickerInfo{}, err
|
|
}
|
|
|
|
return tickerInfo{
|
|
symbol: symbol,
|
|
last: last,
|
|
changePercent: changePercent,
|
|
}, nil
|
|
}
|
|
|
|
func formatChange(changePercent float64) string {
|
|
if changePercent > 0 {
|
|
return fmt.Sprintf("🔼 +%.2f%%", changePercent)
|
|
} else if changePercent < 0 {
|
|
return fmt.Sprintf("🔽 %.2f%%", changePercent)
|
|
}
|
|
return fmt.Sprintf("◀▶ %.2f%%", changePercent)
|
|
}
|
|
|
|
func sendPriceUpdate() {
|
|
var now time.Time
|
|
if singaporeTZ != nil {
|
|
now = time.Now().In(singaporeTZ)
|
|
} else {
|
|
now = time.Now().UTC()
|
|
log.Println("Warning: singaporeTZ is nil, using UTC")
|
|
}
|
|
message := fmt.Sprintf("市场更新 - %s (SGT)\n\n", now.Format("2006-01-02 15:04:05"))
|
|
|
|
for _, symbol := range symbols {
|
|
info, err := getTickerInfo(symbol)
|
|
if err != nil {
|
|
log.Printf("Error getting ticker info for %s: %v", symbol, err)
|
|
continue
|
|
}
|
|
|
|
changeStr := formatChange(info.changePercent)
|
|
|
|
message += fmt.Sprintf("*%s*\n", info.symbol)
|
|
message += fmt.Sprintf("价格: $%.7f\n", info.last)
|
|
message += fmt.Sprintf("24h 涨跌: %s\n\n", changeStr)
|
|
}
|
|
|
|
if lastMsgID != 0 {
|
|
deleteMsg := tgbotapi.NewDeleteMessage(chatID, lastMsgID)
|
|
_, err := bot.Request(deleteMsg)
|
|
if err != nil {
|
|
log.Printf("Failed to delete previous message: %v", err)
|
|
}
|
|
}
|
|
|
|
msg := tgbotapi.NewMessage(chatID, message)
|
|
msg.ParseMode = "Markdown"
|
|
sentMsg, err := bot.Send(msg)
|
|
if err != nil {
|
|
log.Printf("Failed to send message: %v", err)
|
|
return
|
|
}
|
|
|
|
lastMsgID = sentMsg.MessageID
|
|
}
|
|
|
|
func RunBinance() {
|
|
log.Println("Starting Binance service...")
|
|
for {
|
|
log.Println("Sending price update...")
|
|
sendPriceUpdate()
|
|
|
|
log.Println("Waiting for next update...")
|
|
time.Sleep(1 * time.Hour)
|
|
}
|
|
}
|