新增 虚拟币价格实时获取

This commit is contained in:
wood chen 2024-09-20 15:22:53 +08:00
parent 98ebbce6f1
commit 04ff32efdd
5 changed files with 173 additions and 72 deletions

View File

@ -20,6 +20,10 @@
- 发送详细的价格更新包括当前价格、24小时变化、高低点等 - 发送详细的价格更新包括当前价格、24小时变化、高低点等
- 可自定义货币对, 更新频率可自行在代码里修改 - 可自定义货币对, 更新频率可自行在代码里修改
### 虚拟币价格实时获取
- 从币安获取所有交易对, 缓存到内存里, 每小时刷新一次
- 当群消息触及关键词时, 会返回对应虚拟币/USDT的价格
### 链接拦截 ### 链接拦截
- 新增: 当非管理员时, 才会进行链接拦截 - 新增: 当非管理员时, 才会进行链接拦截
- 非白名单域名链接, 在发送第二次会被拦截撤回 - 非白名单域名链接, 在发送第二次会被拦截撤回

View File

@ -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
}

View File

@ -118,6 +118,14 @@ func RunBinance() {
symbols = core.Symbols symbols = core.Symbols
singaporeTZ = core.SingaporeTZ singaporeTZ = core.SingaporeTZ
// 初始化并加载交易对
if err := LoadSymbols(); err != nil {
log.Fatalf("Failed to load trading pairs: %v", err)
}
// 启动每小时刷新交易对缓存
go StartSymbolRefresh(1 * time.Hour)
// 立即发送一次价格更新 // 立即发送一次价格更新
sendPriceUpdate() sendPriceUpdate()

View 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 // 找到并回复后退出
}
}
}

View File

@ -9,6 +9,7 @@ import (
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5" tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
"github.com/woodchen-ink/Q58Bot/core" "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/group_member_management"
"github.com/woodchen-ink/Q58Bot/service/link_filter" "github.com/woodchen-ink/Q58Bot/service/link_filter"
"github.com/woodchen-ink/Q58Bot/service/prompt_reply" "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) 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) { func HandleKeywordCommand(bot *tgbotapi.BotAPI, message *tgbotapi.Message, command string, args string) {
args = strings.TrimSpace(args) args = strings.TrimSpace(args)
@ -197,60 +167,60 @@ func HandleKeywordCommand(bot *tgbotapi.BotAPI, message *tgbotapi.Message, comma
case "deletecontaining": case "deletecontaining":
handleDeleteContainingKeyword(bot, message, args) handleDeleteContainingKeyword(bot, message, args)
default: default:
sendErrorMessage(bot, message.Chat.ID, "无效的命令或参数。") core.SendErrorMessage(bot, message.Chat.ID, "无效的命令或参数。")
} }
} }
func handleListKeywords(bot *tgbotapi.BotAPI, message *tgbotapi.Message) { func handleListKeywords(bot *tgbotapi.BotAPI, message *tgbotapi.Message) {
keywords, err := core.DB.GetAllKeywords() keywords, err := core.DB.GetAllKeywords()
if err != nil { if err != nil {
sendErrorMessage(bot, message.Chat.ID, "获取关键词列表时发生错误。") core.SendErrorMessage(bot, message.Chat.ID, "获取关键词列表时发生错误。")
return return
} }
if len(keywords) == 0 { if len(keywords) == 0 {
sendMessage(bot, message.Chat.ID, "关键词列表为空。") core.SendMessage(bot, message.Chat.ID, "关键词列表为空。")
} else { } else {
SendLongMessage(bot, message.Chat.ID, "当前关键词列表:", keywords) core.SendLongMessage(bot, message.Chat.ID, "当前关键词列表:", keywords)
} }
} }
func handleAddKeyword(bot *tgbotapi.BotAPI, message *tgbotapi.Message, keyword string) { func handleAddKeyword(bot *tgbotapi.BotAPI, message *tgbotapi.Message, keyword string) {
if keyword == "" { if keyword == "" {
sendErrorMessage(bot, message.Chat.ID, "请提供要添加的关键词。") core.SendErrorMessage(bot, message.Chat.ID, "请提供要添加的关键词。")
return return
} }
exists, err := core.DB.KeywordExists(keyword) exists, err := core.DB.KeywordExists(keyword)
if err != nil { if err != nil {
sendErrorMessage(bot, message.Chat.ID, "检查关键词时发生错误。") core.SendErrorMessage(bot, message.Chat.ID, "检查关键词时发生错误。")
return return
} }
if !exists { if !exists {
err = core.DB.AddKeyword(keyword) err = core.DB.AddKeyword(keyword)
if err != nil { if err != nil {
sendErrorMessage(bot, message.Chat.ID, "添加关键词时发生错误。") core.SendErrorMessage(bot, message.Chat.ID, "添加关键词时发生错误。")
} else { } else {
sendMessage(bot, message.Chat.ID, fmt.Sprintf("关键词 '%s' 已添加。", keyword)) core.SendMessage(bot, message.Chat.ID, fmt.Sprintf("关键词 '%s' 已添加。", keyword))
} }
} else { } 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) { func handleDeleteKeyword(bot *tgbotapi.BotAPI, message *tgbotapi.Message, keyword string) {
if keyword == "" { if keyword == "" {
sendErrorMessage(bot, message.Chat.ID, "请提供要删除的关键词。") core.SendErrorMessage(bot, message.Chat.ID, "请提供要删除的关键词。")
return return
} }
removed, err := core.DB.RemoveKeyword(keyword) removed, err := core.DB.RemoveKeyword(keyword)
if err != nil { 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 return
} }
if removed { if removed {
sendMessage(bot, message.Chat.ID, fmt.Sprintf("关键词 '%s' 已成功删除。", keyword)) core.SendMessage(bot, message.Chat.ID, fmt.Sprintf("关键词 '%s' 已成功删除。", keyword))
} else { } else {
handleSimilarKeywords(bot, message, keyword) 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) { func handleSimilarKeywords(bot *tgbotapi.BotAPI, message *tgbotapi.Message, keyword string) {
similarKeywords, err := core.DB.SearchKeywords(keyword) similarKeywords, err := core.DB.SearchKeywords(keyword)
if err != nil { if err != nil {
sendErrorMessage(bot, message.Chat.ID, "搜索关键词时发生错误。") core.SendErrorMessage(bot, message.Chat.ID, "搜索关键词时发生错误。")
return return
} }
if len(similarKeywords) > 0 { 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 { } 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) { func handleDeleteContainingKeyword(bot *tgbotapi.BotAPI, message *tgbotapi.Message, substring string) {
if substring == "" { if substring == "" {
sendErrorMessage(bot, message.Chat.ID, "请提供要删除的子字符串。") core.SendErrorMessage(bot, message.Chat.ID, "请提供要删除的子字符串。")
return return
} }
removedKeywords, err := core.DB.RemoveKeywordsContaining(substring) removedKeywords, err := core.DB.RemoveKeywordsContaining(substring)
if err != nil { if err != nil {
sendErrorMessage(bot, message.Chat.ID, "删除关键词时发生错误。") core.SendErrorMessage(bot, message.Chat.ID, "删除关键词时发生错误。")
return return
} }
if len(removedKeywords) > 0 { 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 { } 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": case "delwhite":
handleDeleteWhitelist(bot, message, args) handleDeleteWhitelist(bot, message, args)
default: default:
sendErrorMessage(bot, message.Chat.ID, "无效的命令或参数。") core.SendErrorMessage(bot, message.Chat.ID, "无效的命令或参数。")
} }
} }
func handleListWhitelist(bot *tgbotapi.BotAPI, message *tgbotapi.Message) { func handleListWhitelist(bot *tgbotapi.BotAPI, message *tgbotapi.Message) {
whitelist, err := core.DB.GetAllWhitelist() whitelist, err := core.DB.GetAllWhitelist()
if err != nil { if err != nil {
sendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("获取白名单时发生错误: %v", err)) core.SendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("获取白名单时发生错误: %v", err))
return return
} }
if len(whitelist) == 0 { if len(whitelist) == 0 {
sendMessage(bot, message.Chat.ID, "白名单为空。") core.SendMessage(bot, message.Chat.ID, "白名单为空。")
} else { } else {
SendLongMessage(bot, message.Chat.ID, "白名单域名列表:", whitelist) core.SendLongMessage(bot, message.Chat.ID, "白名单域名列表:", whitelist)
} }
} }
func handleAddWhitelist(bot *tgbotapi.BotAPI, message *tgbotapi.Message, domain string) { func handleAddWhitelist(bot *tgbotapi.BotAPI, message *tgbotapi.Message, domain string) {
if domain == "" { if domain == "" {
sendErrorMessage(bot, message.Chat.ID, "请提供要添加的域名。") core.SendErrorMessage(bot, message.Chat.ID, "请提供要添加的域名。")
return return
} }
domain = strings.ToLower(domain) domain = strings.ToLower(domain)
exists, err := core.DB.WhitelistExists(domain) exists, err := core.DB.WhitelistExists(domain)
if err != nil { if err != nil {
sendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("检查白名单时发生错误: %v", err)) core.SendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("检查白名单时发生错误: %v", err))
return return
} }
if exists { if exists {
sendMessage(bot, message.Chat.ID, fmt.Sprintf("域名 '%s' 已在白名单中。", domain)) core.SendMessage(bot, message.Chat.ID, fmt.Sprintf("域名 '%s' 已在白名单中。", domain))
return return
} }
err = core.DB.AddWhitelist(domain) err = core.DB.AddWhitelist(domain)
if err != nil { if err != nil {
sendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("添加到白名单时发生错误: %v", err)) core.SendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("添加到白名单时发生错误: %v", err))
return return
} }
exists, err = core.DB.WhitelistExists(domain) exists, err = core.DB.WhitelistExists(domain)
if err != nil { if err != nil {
sendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("验证添加操作时发生错误: %v", err)) core.SendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("验证添加操作时发生错误: %v", err))
return return
} }
if exists { if exists {
sendMessage(bot, message.Chat.ID, fmt.Sprintf("域名 '%s' 已成功添加到白名单。", domain)) core.SendMessage(bot, message.Chat.ID, fmt.Sprintf("域名 '%s' 已成功添加到白名单。", domain))
} else { } 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) { func handleDeleteWhitelist(bot *tgbotapi.BotAPI, message *tgbotapi.Message, domain string) {
if domain == "" { if domain == "" {
sendErrorMessage(bot, message.Chat.ID, "请提供要删除的域名。") core.SendErrorMessage(bot, message.Chat.ID, "请提供要删除的域名。")
return return
} }
domain = strings.ToLower(domain) domain = strings.ToLower(domain)
exists, err := core.DB.WhitelistExists(domain) exists, err := core.DB.WhitelistExists(domain)
if err != nil { if err != nil {
sendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("检查白名单时发生错误: %v", err)) core.SendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("检查白名单时发生错误: %v", err))
return return
} }
if !exists { if !exists {
sendMessage(bot, message.Chat.ID, fmt.Sprintf("域名 '%s' 不在白名单中。", domain)) core.SendMessage(bot, message.Chat.ID, fmt.Sprintf("域名 '%s' 不在白名单中。", domain))
return return
} }
err = core.DB.RemoveWhitelist(domain) err = core.DB.RemoveWhitelist(domain)
if err != nil { if err != nil {
sendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("从白名单删除时发生错误: %v", err)) core.SendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("从白名单删除时发生错误: %v", err))
return return
} }
exists, err = core.DB.WhitelistExists(domain) exists, err = core.DB.WhitelistExists(domain)
if err != nil { if err != nil {
sendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("验证删除操作时发生错误: %v", err)) core.SendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("验证删除操作时发生错误: %v", err))
return return
} }
if !exists { if !exists {
sendMessage(bot, message.Chat.ID, fmt.Sprintf("域名 '%s' 已成功从白名单中删除。", domain)) core.SendMessage(bot, message.Chat.ID, fmt.Sprintf("域名 '%s' 已成功从白名单中删除。", domain))
} else { } else {
sendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("未能从白名单中删除域名 '%s'。", domain)) core.SendErrorMessage(bot, message.Chat.ID, fmt.Sprintf("未能从白名单中删除域名 '%s'。", domain))
} }
} }