mirror of
https://github.com/woodchen-ink/Q58Bot.git
synced 2025-07-18 05:42:06 +08:00
新增 虚拟币价格实时获取
This commit is contained in:
parent
98ebbce6f1
commit
04ff32efdd
@ -20,6 +20,10 @@
|
||||
- 发送详细的价格更新,包括当前价格、24小时变化、高低点等
|
||||
- 可自定义货币对, 更新频率可自行在代码里修改
|
||||
|
||||
### 虚拟币价格实时获取
|
||||
- 从币安获取所有交易对, 缓存到内存里, 每小时刷新一次
|
||||
- 当群消息触及关键词时, 会返回对应虚拟币/USDT的价格
|
||||
|
||||
### 链接拦截
|
||||
- 新增: 当非管理员时, 才会进行链接拦截
|
||||
- 非白名单域名链接, 在发送第二次会被拦截撤回
|
||||
|
@ -35,3 +35,37 @@ func DeleteMessageAfterDelay(bot *tgbotapi.BotAPI, chatID int64, messageID int,
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func SendMessage(bot *tgbotapi.BotAPI, chatID int64, text string) error {
|
||||
msg := tgbotapi.NewMessage(chatID, text)
|
||||
_, err := bot.Send(msg)
|
||||
return err
|
||||
}
|
||||
|
||||
func SendErrorMessage(bot *tgbotapi.BotAPI, chatID int64, errMsg string) {
|
||||
SendMessage(bot, chatID, errMsg)
|
||||
}
|
||||
|
||||
const (
|
||||
maxMessageLength = 4000
|
||||
)
|
||||
|
||||
func SendLongMessage(bot *tgbotapi.BotAPI, chatID int64, prefix string, items []string) error {
|
||||
message := prefix + "\n"
|
||||
for i, item := range items {
|
||||
newLine := fmt.Sprintf("%d. %s\n", i+1, item)
|
||||
if len(message)+len(newLine) > maxMessageLength {
|
||||
if err := SendMessage(bot, chatID, message); err != nil {
|
||||
return err
|
||||
}
|
||||
message = ""
|
||||
}
|
||||
message += newLine
|
||||
}
|
||||
|
||||
if message != "" {
|
||||
return SendMessage(bot, chatID, message)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -118,6 +118,14 @@ func RunBinance() {
|
||||
symbols = core.Symbols
|
||||
singaporeTZ = core.SingaporeTZ
|
||||
|
||||
// 初始化并加载交易对
|
||||
if err := LoadSymbols(); err != nil {
|
||||
log.Fatalf("Failed to load trading pairs: %v", err)
|
||||
}
|
||||
|
||||
// 启动每小时刷新交易对缓存
|
||||
go StartSymbolRefresh(1 * time.Hour)
|
||||
|
||||
// 立即发送一次价格更新
|
||||
sendPriceUpdate()
|
||||
|
||||
|
85
service/binance/symbols.go
Normal file
85
service/binance/symbols.go
Normal file
@ -0,0 +1,85 @@
|
||||
package binance
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/adshao/go-binance/v2"
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
)
|
||||
|
||||
var (
|
||||
symbolsMu sync.RWMutex
|
||||
)
|
||||
|
||||
// LoadSymbols 初始化并缓存所有交易对
|
||||
func LoadSymbols() error {
|
||||
client := binance.NewClient("", "")
|
||||
exchangeInfo, err := client.NewExchangeInfoService().Do(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
symbolsMu.Lock()
|
||||
defer symbolsMu.Unlock()
|
||||
|
||||
symbols = nil // 清空旧的符号列表
|
||||
for _, symbol := range exchangeInfo.Symbols {
|
||||
if symbol.Status == "TRADING" && symbol.QuoteAsset == "USDT" {
|
||||
symbols = append(symbols, symbol.BaseAsset)
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("Loaded %d trading pairs", len(symbols))
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAllSymbols 获取缓存的交易对列表
|
||||
func GetAllSymbols() []string {
|
||||
symbolsMu.RLock()
|
||||
defer symbolsMu.RUnlock()
|
||||
return symbols
|
||||
}
|
||||
|
||||
// StartSymbolRefresh 每小时刷新一次交易对缓存
|
||||
func StartSymbolRefresh(interval time.Duration) {
|
||||
ticker := time.NewTicker(interval)
|
||||
go func() {
|
||||
for range ticker.C {
|
||||
log.Println("Refreshing trading pairs...")
|
||||
if err := LoadSymbols(); err != nil {
|
||||
log.Printf("Failed to refresh symbols: %v", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// HandleSymbolQuery 处理虚拟币名查询
|
||||
func HandleSymbolQuery(bot *tgbotapi.BotAPI, message *tgbotapi.Message) {
|
||||
// 获取所有虚拟币名
|
||||
symbols := GetAllSymbols()
|
||||
|
||||
// 检查消息内容中是否包含虚拟币名
|
||||
for _, symbol := range symbols {
|
||||
if strings.Contains(strings.ToUpper(message.Text), symbol) {
|
||||
// 查询价格并回复
|
||||
info, err := getTickerInfo(symbol + "USDT") // 查询对应USDT价格
|
||||
if err != nil {
|
||||
log.Printf("Error getting ticker info for %s: %v", symbol, err)
|
||||
return
|
||||
}
|
||||
replyMessage := fmt.Sprintf("*%s*\n价格: $%.7f\n24h 涨跌: %s\n",
|
||||
info.symbol,
|
||||
info.last,
|
||||
formatChange(info.changePercent))
|
||||
msg := tgbotapi.NewMessage(message.Chat.ID, replyMessage)
|
||||
msg.ParseMode = "Markdown"
|
||||
bot.Send(msg)
|
||||
return // 找到并回复后退出
|
||||
}
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ import (
|
||||
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
"github.com/woodchen-ink/Q58Bot/core"
|
||||
"github.com/woodchen-ink/Q58Bot/service/binance"
|
||||
"github.com/woodchen-ink/Q58Bot/service/group_member_management"
|
||||
"github.com/woodchen-ink/Q58Bot/service/link_filter"
|
||||
"github.com/woodchen-ink/Q58Bot/service/prompt_reply"
|
||||
@ -73,7 +74,10 @@ func processMessage(bot *tgbotapi.BotAPI, message *tgbotapi.Message, linkFilter
|
||||
}
|
||||
}
|
||||
|
||||
// 使用现有的 CheckAndReplyPrompt 函数进行提示词回复
|
||||
// 调用 HandleSymbolQuery 处理虚拟币名查询
|
||||
binance.HandleSymbolQuery(bot, message)
|
||||
|
||||
// 调用 CheckAndReplyPrompt 函数进行提示词回复
|
||||
prompt_reply.CheckAndReplyPrompt(bot, message)
|
||||
}
|
||||
|
||||
@ -150,40 +154,6 @@ func RunMessageHandler() error {
|
||||
//
|
||||
//
|
||||
|
||||
const (
|
||||
maxMessageLength = 4000
|
||||
)
|
||||
|
||||
func SendLongMessage(bot *tgbotapi.BotAPI, chatID int64, prefix string, items []string) error {
|
||||
message := prefix + "\n"
|
||||
for i, item := range items {
|
||||
newLine := fmt.Sprintf("%d. %s\n", i+1, item)
|
||||
if len(message)+len(newLine) > maxMessageLength {
|
||||
if err := sendMessage(bot, chatID, message); err != nil {
|
||||
return err
|
||||
}
|
||||
message = ""
|
||||
}
|
||||
message += newLine
|
||||
}
|
||||
|
||||
if message != "" {
|
||||
return sendMessage(bot, chatID, message)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func sendMessage(bot *tgbotapi.BotAPI, chatID int64, text string) error {
|
||||
msg := tgbotapi.NewMessage(chatID, text)
|
||||
_, err := bot.Send(msg)
|
||||
return err
|
||||
}
|
||||
|
||||
func sendErrorMessage(bot *tgbotapi.BotAPI, chatID int64, errMsg string) {
|
||||
sendMessage(bot, chatID, errMsg)
|
||||
}
|
||||
|
||||
func HandleKeywordCommand(bot *tgbotapi.BotAPI, message *tgbotapi.Message, command string, args string) {
|
||||
args = strings.TrimSpace(args)
|
||||
|
||||
@ -197,60 +167,60 @@ func HandleKeywordCommand(bot *tgbotapi.BotAPI, message *tgbotapi.Message, comma
|
||||
case "deletecontaining":
|
||||
handleDeleteContainingKeyword(bot, message, args)
|
||||
default:
|
||||
sendErrorMessage(bot, message.Chat.ID, "无效的命令或参数。")
|
||||
core.SendErrorMessage(bot, message.Chat.ID, "无效的命令或参数。")
|
||||
}
|
||||
}
|
||||
|
||||
func handleListKeywords(bot *tgbotapi.BotAPI, message *tgbotapi.Message) {
|
||||
keywords, err := core.DB.GetAllKeywords()
|
||||
if err != nil {
|
||||
sendErrorMessage(bot, message.Chat.ID, "获取关键词列表时发生错误。")
|
||||
core.SendErrorMessage(bot, message.Chat.ID, "获取关键词列表时发生错误。")
|
||||
return
|
||||
}
|
||||
if len(keywords) == 0 {
|
||||
sendMessage(bot, message.Chat.ID, "关键词列表为空。")
|
||||
core.SendMessage(bot, message.Chat.ID, "关键词列表为空。")
|
||||
} else {
|
||||
SendLongMessage(bot, message.Chat.ID, "当前关键词列表:", keywords)
|
||||
core.SendLongMessage(bot, message.Chat.ID, "当前关键词列表:", keywords)
|
||||
}
|
||||
}
|
||||
|
||||
func handleAddKeyword(bot *tgbotapi.BotAPI, message *tgbotapi.Message, keyword string) {
|
||||
if keyword == "" {
|
||||
sendErrorMessage(bot, message.Chat.ID, "请提供要添加的关键词。")
|
||||
core.SendErrorMessage(bot, message.Chat.ID, "请提供要添加的关键词。")
|
||||
return
|
||||
}
|
||||
|
||||
exists, err := core.DB.KeywordExists(keyword)
|
||||
if err != nil {
|
||||
sendErrorMessage(bot, message.Chat.ID, "检查关键词时发生错误。")
|
||||
core.SendErrorMessage(bot, message.Chat.ID, "检查关键词时发生错误。")
|
||||
return
|
||||
}
|
||||
if !exists {
|
||||
err = core.DB.AddKeyword(keyword)
|
||||
if err != nil {
|
||||
sendErrorMessage(bot, message.Chat.ID, "添加关键词时发生错误。")
|
||||
core.SendErrorMessage(bot, message.Chat.ID, "添加关键词时发生错误。")
|
||||
} else {
|
||||
sendMessage(bot, message.Chat.ID, fmt.Sprintf("关键词 '%s' 已添加。", keyword))
|
||||
core.SendMessage(bot, message.Chat.ID, fmt.Sprintf("关键词 '%s' 已添加。", keyword))
|
||||
}
|
||||
} else {
|
||||
sendMessage(bot, message.Chat.ID, fmt.Sprintf("关键词 '%s' 已存在。", keyword))
|
||||
core.SendMessage(bot, message.Chat.ID, fmt.Sprintf("关键词 '%s' 已存在。", keyword))
|
||||
}
|
||||
}
|
||||
|
||||
func handleDeleteKeyword(bot *tgbotapi.BotAPI, message *tgbotapi.Message, keyword string) {
|
||||
if keyword == "" {
|
||||
sendErrorMessage(bot, message.Chat.ID, "请提供要删除的关键词。")
|
||||
core.SendErrorMessage(bot, message.Chat.ID, "请提供要删除的关键词。")
|
||||
return
|
||||
}
|
||||
|
||||
removed, err := core.DB.RemoveKeyword(keyword)
|
||||
if err != nil {
|
||||
sendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("删除关键词 '%s' 时发生错误: %v", keyword, err))
|
||||
core.SendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("删除关键词 '%s' 时发生错误: %v", keyword, err))
|
||||
return
|
||||
}
|
||||
|
||||
if removed {
|
||||
sendMessage(bot, message.Chat.ID, fmt.Sprintf("关键词 '%s' 已成功删除。", keyword))
|
||||
core.SendMessage(bot, message.Chat.ID, fmt.Sprintf("关键词 '%s' 已成功删除。", keyword))
|
||||
} else {
|
||||
handleSimilarKeywords(bot, message, keyword)
|
||||
}
|
||||
@ -259,31 +229,31 @@ func handleDeleteKeyword(bot *tgbotapi.BotAPI, message *tgbotapi.Message, keywor
|
||||
func handleSimilarKeywords(bot *tgbotapi.BotAPI, message *tgbotapi.Message, keyword string) {
|
||||
similarKeywords, err := core.DB.SearchKeywords(keyword)
|
||||
if err != nil {
|
||||
sendErrorMessage(bot, message.Chat.ID, "搜索关键词时发生错误。")
|
||||
core.SendErrorMessage(bot, message.Chat.ID, "搜索关键词时发生错误。")
|
||||
return
|
||||
}
|
||||
if len(similarKeywords) > 0 {
|
||||
SendLongMessage(bot, message.Chat.ID, fmt.Sprintf("未能删除关键词 '%s'。\n\n以下是相似的关键词:", keyword), similarKeywords)
|
||||
core.SendLongMessage(bot, message.Chat.ID, fmt.Sprintf("未能删除关键词 '%s'。\n\n以下是相似的关键词:", keyword), similarKeywords)
|
||||
} else {
|
||||
sendMessage(bot, message.Chat.ID, fmt.Sprintf("未能删除关键词 '%s',且未找到相似的关键词。", keyword))
|
||||
core.SendMessage(bot, message.Chat.ID, fmt.Sprintf("未能删除关键词 '%s',且未找到相似的关键词。", keyword))
|
||||
}
|
||||
}
|
||||
|
||||
func handleDeleteContainingKeyword(bot *tgbotapi.BotAPI, message *tgbotapi.Message, substring string) {
|
||||
if substring == "" {
|
||||
sendErrorMessage(bot, message.Chat.ID, "请提供要删除的子字符串。")
|
||||
core.SendErrorMessage(bot, message.Chat.ID, "请提供要删除的子字符串。")
|
||||
return
|
||||
}
|
||||
|
||||
removedKeywords, err := core.DB.RemoveKeywordsContaining(substring)
|
||||
if err != nil {
|
||||
sendErrorMessage(bot, message.Chat.ID, "删除关键词时发生错误。")
|
||||
core.SendErrorMessage(bot, message.Chat.ID, "删除关键词时发生错误。")
|
||||
return
|
||||
}
|
||||
if len(removedKeywords) > 0 {
|
||||
SendLongMessage(bot, message.Chat.ID, fmt.Sprintf("已删除包含 '%s' 的以下关键词:", substring), removedKeywords)
|
||||
core.SendLongMessage(bot, message.Chat.ID, fmt.Sprintf("已删除包含 '%s' 的以下关键词:", substring), removedKeywords)
|
||||
} else {
|
||||
sendMessage(bot, message.Chat.ID, fmt.Sprintf("没有找到包含 '%s' 的关键词。", substring))
|
||||
core.SendMessage(bot, message.Chat.ID, fmt.Sprintf("没有找到包含 '%s' 的关键词。", substring))
|
||||
}
|
||||
}
|
||||
|
||||
@ -298,90 +268,90 @@ func HandleWhitelistCommand(bot *tgbotapi.BotAPI, message *tgbotapi.Message, com
|
||||
case "delwhite":
|
||||
handleDeleteWhitelist(bot, message, args)
|
||||
default:
|
||||
sendErrorMessage(bot, message.Chat.ID, "无效的命令或参数。")
|
||||
core.SendErrorMessage(bot, message.Chat.ID, "无效的命令或参数。")
|
||||
}
|
||||
}
|
||||
|
||||
func handleListWhitelist(bot *tgbotapi.BotAPI, message *tgbotapi.Message) {
|
||||
whitelist, err := core.DB.GetAllWhitelist()
|
||||
if err != nil {
|
||||
sendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("获取白名单时发生错误: %v", err))
|
||||
core.SendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("获取白名单时发生错误: %v", err))
|
||||
return
|
||||
}
|
||||
if len(whitelist) == 0 {
|
||||
sendMessage(bot, message.Chat.ID, "白名单为空。")
|
||||
core.SendMessage(bot, message.Chat.ID, "白名单为空。")
|
||||
} else {
|
||||
SendLongMessage(bot, message.Chat.ID, "白名单域名列表:", whitelist)
|
||||
core.SendLongMessage(bot, message.Chat.ID, "白名单域名列表:", whitelist)
|
||||
}
|
||||
}
|
||||
|
||||
func handleAddWhitelist(bot *tgbotapi.BotAPI, message *tgbotapi.Message, domain string) {
|
||||
if domain == "" {
|
||||
sendErrorMessage(bot, message.Chat.ID, "请提供要添加的域名。")
|
||||
core.SendErrorMessage(bot, message.Chat.ID, "请提供要添加的域名。")
|
||||
return
|
||||
}
|
||||
|
||||
domain = strings.ToLower(domain)
|
||||
exists, err := core.DB.WhitelistExists(domain)
|
||||
if err != nil {
|
||||
sendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("检查白名单时发生错误: %v", err))
|
||||
core.SendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("检查白名单时发生错误: %v", err))
|
||||
return
|
||||
}
|
||||
if exists {
|
||||
sendMessage(bot, message.Chat.ID, fmt.Sprintf("域名 '%s' 已在白名单中。", domain))
|
||||
core.SendMessage(bot, message.Chat.ID, fmt.Sprintf("域名 '%s' 已在白名单中。", domain))
|
||||
return
|
||||
}
|
||||
|
||||
err = core.DB.AddWhitelist(domain)
|
||||
if err != nil {
|
||||
sendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("添加到白名单时发生错误: %v", err))
|
||||
core.SendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("添加到白名单时发生错误: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
exists, err = core.DB.WhitelistExists(domain)
|
||||
if err != nil {
|
||||
sendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("验证添加操作时发生错误: %v", err))
|
||||
core.SendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("验证添加操作时发生错误: %v", err))
|
||||
return
|
||||
}
|
||||
if exists {
|
||||
sendMessage(bot, message.Chat.ID, fmt.Sprintf("域名 '%s' 已成功添加到白名单。", domain))
|
||||
core.SendMessage(bot, message.Chat.ID, fmt.Sprintf("域名 '%s' 已成功添加到白名单。", domain))
|
||||
} else {
|
||||
sendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("未能添加域名 '%s' 到白名单。", domain))
|
||||
core.SendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("未能添加域名 '%s' 到白名单。", domain))
|
||||
}
|
||||
}
|
||||
|
||||
func handleDeleteWhitelist(bot *tgbotapi.BotAPI, message *tgbotapi.Message, domain string) {
|
||||
if domain == "" {
|
||||
sendErrorMessage(bot, message.Chat.ID, "请提供要删除的域名。")
|
||||
core.SendErrorMessage(bot, message.Chat.ID, "请提供要删除的域名。")
|
||||
return
|
||||
}
|
||||
|
||||
domain = strings.ToLower(domain)
|
||||
exists, err := core.DB.WhitelistExists(domain)
|
||||
if err != nil {
|
||||
sendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("检查白名单时发生错误: %v", err))
|
||||
core.SendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("检查白名单时发生错误: %v", err))
|
||||
return
|
||||
}
|
||||
if !exists {
|
||||
sendMessage(bot, message.Chat.ID, fmt.Sprintf("域名 '%s' 不在白名单中。", domain))
|
||||
core.SendMessage(bot, message.Chat.ID, fmt.Sprintf("域名 '%s' 不在白名单中。", domain))
|
||||
return
|
||||
}
|
||||
|
||||
err = core.DB.RemoveWhitelist(domain)
|
||||
if err != nil {
|
||||
sendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("从白名单删除时发生错误: %v", err))
|
||||
core.SendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("从白名单删除时发生错误: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
exists, err = core.DB.WhitelistExists(domain)
|
||||
if err != nil {
|
||||
sendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("验证删除操作时发生错误: %v", err))
|
||||
core.SendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("验证删除操作时发生错误: %v", err))
|
||||
return
|
||||
}
|
||||
if !exists {
|
||||
sendMessage(bot, message.Chat.ID, fmt.Sprintf("域名 '%s' 已成功从白名单中删除。", domain))
|
||||
core.SendMessage(bot, message.Chat.ID, fmt.Sprintf("域名 '%s' 已成功从白名单中删除。", domain))
|
||||
} else {
|
||||
sendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("未能从白名单中删除域名 '%s'。", domain))
|
||||
core.SendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("未能从白名单中删除域名 '%s'。", domain))
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user